2018-03-24 17:05:01 +00:00
|
|
|
# Terminal Dashboard High-Level Design
|
|
|
|
|
|
|
|
## Objective
|
|
|
|
|
2018-03-26 15:40:23 +01:00
|
|
|
Develop infrastructure of dashboard widgets. The widgets should support both
|
|
|
|
input (mouse and keyboard) and output (display of information to the user).
|
|
|
|
|
|
|
|
Fulfill the requirements outlined in the main
|
|
|
|
[README](http://github.com/mum4k/termdash).
|
|
|
|
|
2018-03-24 17:05:01 +00:00
|
|
|
## Background
|
|
|
|
|
2018-03-26 15:40:23 +01:00
|
|
|
The terminal dashboard allows placement of configurable widgets onto the terminal.
|
|
|
|
|
|
|
|
A widget displays some information to the user, e.g. A graph, a chart, a
|
|
|
|
progress bar. A widget can receive information from the user in the form of
|
|
|
|
events, e.g. Mouse or keyboard input.
|
|
|
|
|
|
|
|
The widgets aren't placed onto the terminal directly, instead the terminal is
|
|
|
|
organized into containers. Each container can contain either a widget or
|
|
|
|
other containers.
|
|
|
|
|
2018-03-24 17:05:01 +00:00
|
|
|
## Overview
|
|
|
|
|
2018-03-26 15:40:23 +01:00
|
|
|
The terminal dashboard consists of the following layers:
|
|
|
|
|
2018-03-26 16:31:20 +01:00
|
|
|
- Terminal.
|
2018-03-26 15:40:23 +01:00
|
|
|
- Infrastructure.
|
|
|
|
- Widgets.
|
|
|
|
|
2018-03-26 16:31:20 +01:00
|
|
|
The **terminal layer** abstracts the terminal implementation. A real terminal
|
|
|
|
implementation is used in production when displaying data to the user. A fake
|
2018-03-26 15:40:23 +01:00
|
|
|
terminal implementation is used in widget unit tests and system tests. Other
|
2018-03-26 16:31:20 +01:00
|
|
|
implementations are possible, e.g. Image export. The terminal layer is private,
|
|
|
|
neither the users of this library nor the widgets interact with the terminal
|
2018-03-26 15:40:23 +01:00
|
|
|
directly.
|
|
|
|
|
|
|
|
The **infrastructure layer** is responsible for container management, tracking
|
|
|
|
of keyboard and mouse focus and handling external events like resizing of the
|
2018-03-26 16:31:20 +01:00
|
|
|
terminal. The infrastructure layer also decides when to flush the buffer and
|
2018-03-26 15:40:23 +01:00
|
|
|
refresh the screen. I.e. The widgets update content of a back buffer and the
|
2018-03-26 16:31:20 +01:00
|
|
|
infrastructure decides when it is synchronized to the terminal.
|
2018-03-26 15:40:23 +01:00
|
|
|
|
|
|
|
The **widgets layer** contains the implementations of individual widgets. Each
|
|
|
|
widget receives a canvas from the container on which it presents its content to
|
|
|
|
the user. Widgets indicate to the infrastructure layer if they support input
|
|
|
|
events, which are then forwarded from the infrastructure layer.
|
|
|
|
|
|
|
|
The user interacts with the widget API when constructing individual widgets and
|
|
|
|
with the container API when placing the widgets onto the dashboard.
|
|
|
|
|
2018-03-26 15:45:29 +01:00
|
|
|
<p align="center">
|
|
|
|
<img src="hld.png" width="50%">
|
|
|
|
</p>
|
2018-03-26 15:40:23 +01:00
|
|
|
|
2018-03-24 17:05:01 +00:00
|
|
|
## Detailed design
|
|
|
|
|
2018-03-26 16:31:20 +01:00
|
|
|
### Terminal
|
|
|
|
|
2018-03-26 17:28:14 +01:00
|
|
|
The terminal provides access to the input and output.
|
|
|
|
|
|
|
|
It allows to:
|
|
|
|
|
|
|
|
- Set values and attributes of cells on a back buffer representing a 2-D
|
|
|
|
canvas.
|
|
|
|
- Flush the content of the back buffer to the output.
|
|
|
|
- Manipulate the cursor position and visibility.
|
|
|
|
- Read input events (keyboard, mouse, terminal resize, etc...).
|
|
|
|
|
|
|
|
The terminal buffers input events until they are read by the client. The buffer
|
|
|
|
is bound, if the client isn't picking up events fast enough, new events are
|
|
|
|
dropped and a message is logged.
|
2018-03-26 15:40:23 +01:00
|
|
|
|
|
|
|
### Infrastructure
|
|
|
|
|
2018-03-26 17:28:14 +01:00
|
|
|
The infrastructure handles terminal setup, input events and manages containers.
|
|
|
|
|
|
|
|
#### Keyboard and mouse input
|
|
|
|
|
|
|
|
The raw keyboard and mouse events received from the terminal are pre-processed
|
|
|
|
by the infrastructure. The pre-processing involves recognizing keyboard
|
|
|
|
shortcuts (i.e. Key combination). The infrastructure recognizes globally
|
|
|
|
configurable keyboard shortcuts that are processed by the infrastructure. All
|
|
|
|
other keyboard and mouse events are forwarded to the currently focused widget.
|
|
|
|
|
|
|
|
#### Input focus
|
|
|
|
|
|
|
|
The infrastructure tracks focus. Only the focused widget receives keyboard and
|
|
|
|
mouse events. Focus can be changed using mouse or global keyboard shortcuts.
|
|
|
|
The focused widget is highlighted on the dashboard.
|
|
|
|
|
|
|
|
#### Containers
|
|
|
|
|
|
|
|
The container provides a way of splitting the dashboard down to smaller
|
|
|
|
elements where individual widgets can be placed. Each container can be split to
|
|
|
|
multiple sub containers. A container contains either a sub container or a
|
|
|
|
widget.
|
|
|
|
|
|
|
|
Container is responsible for coordinate translation. Each widget receives a
|
|
|
|
virtual canvas it can draw on. Each of these canvases starts at coordinates
|
|
|
|
image.Point{0, 0}. The parent container translates coordinates from the virtual
|
|
|
|
canvas to the real terminal. The container therefore enforces limits for widgets.
|
|
|
|
|
|
|
|
Containers can be styled with borders and other options.
|
|
|
|
|
|
|
|
#### Flushing the terminal
|
|
|
|
|
|
|
|
All widgets indirectly write to the back buffer of the terminal implementation. The changes
|
|
|
|
to the back buffer only become visible when the infrastructure flushes its content.
|
|
|
|
|
|
|
|
Widgets cannot force a flush, but they can indicate that a flush is desired.
|
|
|
|
The infrastructure throttles the amount of times this happens.
|
|
|
|
|
|
|
|
#### Terminal resizing
|
|
|
|
|
|
|
|
The terminal resize events are processed by the infrastructure. Each widget
|
|
|
|
indicates its desired and minimum size for its canvas when registering with its
|
|
|
|
parent container.
|
|
|
|
|
|
|
|
The parent container in turn informs the widget what is the actual size of its
|
|
|
|
canvas. The infrastructure guarantees that the actual size won't ever be
|
|
|
|
smaller than the advertised minimum and guarantees that the size will keep the
|
|
|
|
aspect ration requested by the widget.
|
|
|
|
|
|
|
|
When the size of terminal changes, the infrastructure resizes all containers
|
|
|
|
according to the rules outlined above, asks all widgets to redraw their
|
|
|
|
canvases and flushes to the back buffer to the terminal.
|
|
|
|
|
2018-03-26 15:40:23 +01:00
|
|
|
### Widgets
|
|
|
|
|
|
|
|
## APIs
|
|
|
|
|
2018-03-26 16:31:20 +01:00
|
|
|
### Terminal API
|
2018-03-26 15:40:23 +01:00
|
|
|
|
2018-03-26 16:31:20 +01:00
|
|
|
The Terminal API is an interface private to the terminal dashboard library. Its
|
|
|
|
primary purpose is to act as a shim layer over different terminal
|
2018-03-26 15:40:23 +01:00
|
|
|
implementations.
|
|
|
|
|
2018-03-26 16:31:20 +01:00
|
|
|
The following outlines the terminal API:
|
2018-03-26 15:40:23 +01:00
|
|
|
|
|
|
|
```go
|
2018-03-26 16:31:20 +01:00
|
|
|
// Terminal abstracts an implementation of a 2-D terminal.
|
|
|
|
// A terminal consists of a number of cells.
|
|
|
|
type Terminal interface {
|
|
|
|
// Size returns the terminal width and height in cells.
|
2018-03-26 15:40:23 +01:00
|
|
|
Size() image.Point
|
|
|
|
|
|
|
|
// Clear clears the content of the internal back buffer, resetting all cells
|
|
|
|
// to their default content and attributes.
|
|
|
|
Clear() error
|
2018-03-26 16:31:20 +01:00
|
|
|
// Flush flushes the internal back buffer to the terminal.
|
2018-03-26 15:40:23 +01:00
|
|
|
Flush() error
|
|
|
|
|
|
|
|
// SetCursor sets the position of the cursor.
|
|
|
|
SetCursor(p image.Point)
|
|
|
|
// HideCursos hides the cursor.
|
|
|
|
HideCursor()
|
|
|
|
|
|
|
|
// SetCell sets the value of the specified cell to the provided rune.
|
|
|
|
// Use the options to specify which attributes to modify, if an attribute
|
|
|
|
// option isn't specified, the attribute retains its previous value.
|
|
|
|
SetCell(p image.Point, r rune, opts ...CellOption)
|
|
|
|
|
|
|
|
// Event waits for the next event and returns it.
|
|
|
|
// This call blocks until the next event or cancellation of the context.
|
|
|
|
Event(ctx context.Context) Event
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The **Event()** method returns the next input event. Different input event
|
|
|
|
types are defined as follows.
|
|
|
|
|
2018-03-26 15:45:29 +01:00
|
|
|
```go
|
2018-03-26 15:40:23 +01:00
|
|
|
// Event represents an input event.
|
|
|
|
type Event interface {
|
|
|
|
isEvent()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keyboard is the event used when a key is pressed.
|
|
|
|
// Implements Event.
|
|
|
|
type Keyboard struct {
|
|
|
|
// Key identifies the pressed key.
|
|
|
|
Key rune
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*Keyboard) isEvent() {}
|
|
|
|
|
2018-03-26 16:31:20 +01:00
|
|
|
// Resize is the event used when the terminal was resized.
|
2018-03-26 15:40:23 +01:00
|
|
|
// Implements Event.
|
2018-03-26 16:31:20 +01:00
|
|
|
type Resize struct {
|
|
|
|
// Size is the new size of the terminal.
|
2018-03-26 15:40:23 +01:00
|
|
|
Size image.Point
|
|
|
|
}
|
|
|
|
|
2018-03-26 16:31:20 +01:00
|
|
|
func (*Resize) isEvent() {}
|
2018-03-26 15:40:23 +01:00
|
|
|
|
|
|
|
// Mouse is the event used when the mouse is moved or a mouse button is
|
|
|
|
// pressed.
|
|
|
|
// Implements Event.
|
|
|
|
type Mouse struct {
|
2018-03-26 16:31:20 +01:00
|
|
|
// Position of the mouse on the terminal.
|
2018-03-26 15:40:23 +01:00
|
|
|
Position() image.Point
|
|
|
|
// Button identifies the pressed button if any.
|
|
|
|
Button MouseButton
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*Mouse) isEvent() {}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Container API
|
|
|
|
|
|
|
|
### Widget API
|
|
|
|
|
2018-03-24 17:05:01 +00:00
|
|
|
## Project information
|
|
|
|
|
|
|
|
## Caveats
|
|
|
|
|
|
|
|
## Testing plan
|
|
|
|
|
|
|
|
## Work estimates
|
|
|
|
|
|
|
|
## Future improvements
|
|
|
|
|
|
|
|
## Document history
|
|
|
|
|
2018-03-26 15:40:23 +01:00
|
|
|
Date | Author | Description
|
|
|
|
------------|--------|---------------
|
|
|
|
24-Mar-2018 | mum4k | Initial draft.
|
|
|
|
|
|
|
|
## Notes (work in progress)
|
|
|
|
|
|
|
|
- widget API (creation, options, updating displayed status, reading inputs).
|
|
|
|
- infra API for widgets.
|
|
|
|
- widget registration options (subscribe to input / events).
|
2018-03-26 16:31:20 +01:00
|
|
|
- testing framework (fake terminal and test helper functions).
|