Skip to content
Jakub Sobon edited this page Nov 14, 2020 · 15 revisions

Doc Status

The termdash package provides the developer with the main entry point to Termdash. This package is used to:

  • Start and stop the application.
  • Control screen redraw.
  • Process runtime errors.
  • Subscribe to keyboard and mouse events.

Controlling screen redraw

There are two main ways of how users of Termdash can controls screen redraw.

  • Periodic time based redraw. See the termdash.Run function documented below.
  • Manually triggered redraw. See the termdash.Controller type documented below.

Apart from this, Termdash will redraw the screen each time an input event happens, e.g. a keyboard, a mouse event or a terminal resize.

Processing runtime errors

Unless an error handler is provided via the termdash.ErrorHandler option documented below, the Termdash application will panic on all runtime errors. A runtime error can occurs when:

  • The terminal layer experiences an error while processing or retrieving an event.
  • A subscriber to an event (e.g. a widget) returns an error.
  • The container or a widget fails to draw itself onto the terminal.

Subscribing to keyboard and mouse events

The container and some of the widgets subscribe to keyboard and mouse events. E.g. the Text widget will subscribe to mouse events in order to support scrolling of its content.

The developer might want to also subscribe to events so that the application can define its own keyboard shortcuts. E.g. the developer might want the application to quit when the letter 'q' is pressed. In order to do this, the developer can provide an additional termdash.KeyboardSubscriber or termdash.MouseSubscriber.

The public API surface of this package consists of the following:

The Run function provides the simplest way to run a Termdash based application. Run blocks until the provided context expires, therefore we need a trigger to close the context, e.g. a keyboard shortcut or a timeout. When Termdash is started using this function, the screen is redrawn periodically.

The following code snippet starts Termdash with an empty container and no widgets. Termdash will quit after five seconds when the context expires.

// Create the terminal.
t, err := tcell.New()
if err != nil {
    return fmt.Errorf("tcell.New => %v", err)
}
defer t.Close()

// Create the container without widgets.
c, err := container.New(t)
if err != nil {
    return fmt.Errorf("container.New => %v", err)
}

// Termdash runs until the context expires.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := termdash.Run(ctx, t, c); err != nil {
    return fmt.Errorf("termdash.Run => %v", err)
}

The termdash.RedrawInterval is used to specify how often will the screen redraw when starting Termdash via the termdash.Run function. This option is ignored when using the termdash.NewController.

The following code snippet shows how to run a Termdash application that will redraw every three seconds:

// Create context ctx, terminal t and container c as in the examples above.
if err := termdash.Run(ctx, t, c, termdash.RedrawInterval(3 * time.Second)); err != nil {
    return fmt.Errorf("termdash.Run => %v", err)
}

The NewController function creates a new Controller instance which allows the user to manually trigger a screen redraw. Periodic redraws are disabled in this mode. Termdash will still redraw the screen each time an input event happens, e.g. a keyboard, a mouse event or a terminal resize.

The following code snippet starts Termdash with an empty container and no widgets and redraws the screen once. As opposed to Run, the following code isn't blocking so the user remains in control of the main goroutine.

// Create the terminal.
t, err := tcell.New()
if err != nil {
    return fmt.Errorf("tcell.New => %v", err)
}
defer t.Close()

// Create the container without widgets.
c, err := container.New(t)
if err != nil {
    return fmt.Errorf("container.New => %v", err)
}

// Create the controller.
ctrl, err := termdash.NewController(t, c)
if err != nil {
    return fmt.Errorf("termdash.NewController => %v", err)
}
// Close the controller and termdash once it isn't required anymore.
defer ctrl.Close()

// Redraw the screen manually.
if err := ctrl.Redraw(); err != nil {
    return fmt.Errorf("ctrl.Redraw => %v", err)
}

The termdash.ErrorHandler function is used to provide a custom error handler. If not provided, the application will panic on all runtime errors. An error handler is a function (a callback) that receives the error and decides what to do. This function must be thread-safe, since Termdash delivers events concurrently.

The following example shows initialisation of Termdash with an error handler that just logs the error message:

// Create context ctx, terminal t and container c as in the examples above.

// An error handler that just logs the error.
eh := func(err error) {
  log.Printf("Termdash runtime error: %v", err)
}

// When running Termdash via the Run function.
if err := termdash.Run(ctx, t, c, termdash.ErrorHandler(eh)); err != nil {
    return fmt.Errorf("termdash.Run => %v", err)
}

// When running Termdash via the Controller type.
ctrl, err := termdash.NewController(t, c, termdash.ErrorHandler(eh))
if err != nil {
    return fmt.Errorf("termdash.NewController => %v", err)
}
defer ctrl.Close()

The termdash.KeyboardSubscriber function can be used by the developer to provide an additional subscriber to keyboard events. A keyboard subscriber is a function (a callback) that processes keyboard events. This function must be thread-safe, since Termdash delivers events concurrently. Providing an additional keyboard subscriber is completely optional and doesn't affect any of Termdash functionalities.

The following example shows initialisation of Termdash with a keyboard subscriber that terminates the application when the letter 'q' or 'Q' is pressed on the keyboard:

// Create terminal t and container c as in the examples above.

// Context for Termdash, the application quits when this expires.
// Since the context has no deadline, it will only expire when cancel() is called.
ctx, cancel := context.WithCancel(context.Background())

// A keyboard subscriber that terminates the application by cancelling the context.
quitter := func(k *terminalapi.Keyboard) {
  if k.Key == 'q' || k.Key == 'Q' {
    cancel()
  }
}

// Termdash will run until one of the two letters is pressed.
if err := termdash.Run(ctx, t, c, termdash.KeyboardSubscriber(quitter)); err != nil {
    return fmt.Errorf("termdash.Run => %v", err)
}

The termdash.MouseSubscriber function can be used by the developer to provide an additional subscriber to mouse events. A mouse subscriber is a function (a callback) that processes mouse events. This function must be thread-safe, since Termdash delivers events concurrently. Providing an additional mouse subscriber is completely optional and doesn't affect any of Termdash functionalities.

The following example shows initialisation of Termdash with a mouse subscriber that terminates the application when the right mouse button is pressed:

// Create terminal t and container c as in the examples above.

// Context for Termdash, the application quits when this expires.
// Since the context has no deadline, it will only expire when cancel() is called.
ctx, cancel := context.WithCancel(context.Background())

// A mouse subscriber that terminates the application by cancelling the context.
quitter := func(m *terminalapi.Mouse) {
  if m.Button == mouse.ButtonRight {
    cancel()
  }
}

// Termdash will run until the right mouse button is pressed.
if err := termdash.Run(ctx, t, c, termdash.MouseSubscriber(quitter)); err != nil {
    return fmt.Errorf("termdash.Run => %v", err)
}