mirror of
https://github.com/mum4k/termdash.git
synced 2025-04-25 13:48:50 +08:00
Defining the APIs.
This commit is contained in:
parent
0617fd5ecf
commit
bc42865277
@ -46,7 +46,7 @@ See the [design document](doc/design.md).
|
||||
# Project status
|
||||
|
||||
- [x] High-Level Design.
|
||||
- [ ] Submit the APIs.
|
||||
- [x] Submit the APIs.
|
||||
- [ ] Implement the terminal layer.
|
||||
- [ ] Implement the container.
|
||||
- [ ] Implement the input event pre-processing.
|
||||
|
28
canvas/canvas.go
Normal file
28
canvas/canvas.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Package canvas defines the canvas that the widgets draw on.
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/mum4k/termdash/cell"
|
||||
)
|
||||
|
||||
// Canvas is where a widget draws its output for display on the terminal.
|
||||
type Canvas struct{}
|
||||
|
||||
// Size returns the size of the 2-D canvas given to the widget.
|
||||
func (c *Canvas) Size() image.Point {
|
||||
return image.Point{0, 0}
|
||||
}
|
||||
|
||||
// Clear clears all the content on the canvas.
|
||||
func (c *Canvas) Clear() {}
|
||||
|
||||
// FlushDesired provides a hint to the infrastructure that the canvas was
|
||||
// changed and should be flushed to the terminal.
|
||||
func (c *Canvas) FlushDesired() {}
|
||||
|
||||
// SetCell sets the value of the specified cell on the canvas.
|
||||
// Use the options to specify which attributes to modify, if an attribute
|
||||
// option isn't specified, the attribute retains its previous value.
|
||||
func (c *Canvas) SetCell(p image.Point, r rune, opts ...cell.Option) {}
|
40
cell/cell.go
Normal file
40
cell/cell.go
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Package cell implements cell options and attributes.
|
||||
|
||||
A cell is the smallest point on the terminal.
|
||||
*/
|
||||
package cell
|
||||
|
||||
// Option is used to provide options for cells on a 2-D terminal.
|
||||
type Option interface {
|
||||
// set sets the provided option.
|
||||
set(*Options)
|
||||
}
|
||||
|
||||
// Options stores the provided options.
|
||||
type Options struct {
|
||||
FgColor Color
|
||||
BgColor Color
|
||||
}
|
||||
|
||||
// option implements Option.
|
||||
type option func(*Options)
|
||||
|
||||
// set implements Option.set.
|
||||
func (co option) set(opts *Options) {
|
||||
co(opts)
|
||||
}
|
||||
|
||||
// FgColor sets the foreground color of the cell.
|
||||
func FgColor(color Color) Option {
|
||||
return option(func(co *Options) {
|
||||
co.FgColor = color
|
||||
})
|
||||
}
|
||||
|
||||
// BgColor sets the background color of the cell.
|
||||
func BgColor(color Color) Option {
|
||||
return option(func(co *Options) {
|
||||
co.BgColor = color
|
||||
})
|
||||
}
|
39
cell/color.go
Normal file
39
cell/color.go
Normal file
@ -0,0 +1,39 @@
|
||||
package cell
|
||||
|
||||
// color.go defines constants for cell colors.
|
||||
|
||||
// Color is the color of a cell.
|
||||
type Color int
|
||||
|
||||
// String implements fmt.Stringer()
|
||||
func (cc Color) String() string {
|
||||
if n, ok := colorNames[cc]; ok {
|
||||
return n
|
||||
}
|
||||
return "ColorUnknown"
|
||||
}
|
||||
|
||||
// colorNames maps Color values to human readable names.
|
||||
var colorNames = map[Color]string{
|
||||
ColorDefault: "ColorDefault",
|
||||
ColorBlack: "ColorBlack",
|
||||
ColorRed: "ColorRed",
|
||||
ColorGreen: "ColorGreen",
|
||||
ColorYellow: "ColorYellow",
|
||||
ColorBlue: "ColorBlue",
|
||||
ColorMagenta: "ColorMagenta",
|
||||
ColorCyan: "ColorCyan",
|
||||
ColorWhite: "ColorWhite",
|
||||
}
|
||||
|
||||
const (
|
||||
ColorDefault Color = iota
|
||||
ColorBlack
|
||||
ColorRed
|
||||
ColorGreen
|
||||
ColorYellow
|
||||
ColorBlue
|
||||
ColorMagenta
|
||||
ColorCyan
|
||||
ColorWhite
|
||||
)
|
113
container/container.go
Normal file
113
container/container.go
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
Package container defines a type that wraps other containers or widgets.
|
||||
|
||||
The container supports splitting container into sub containers, defining
|
||||
container styles and placing widgets. The container also creates and manages
|
||||
canvases assigned to the placed widgets.
|
||||
*/
|
||||
package container
|
||||
|
||||
import (
|
||||
"github.com/mum4k/termdash/terminalapi"
|
||||
"github.com/mum4k/termdash/widget"
|
||||
)
|
||||
|
||||
// Container wraps either sub containers or widgets and positions them on the
|
||||
// terminal.
|
||||
type Container struct {
|
||||
// parent is the parent container, nil if this is the root container.
|
||||
parent *Container
|
||||
// The sub containers, if these aren't nil, the widget must be.
|
||||
first *Container
|
||||
second *Container
|
||||
|
||||
// term is the terminal this container is placed on.
|
||||
term terminalapi.Terminal
|
||||
|
||||
// split identifies how is this container split.
|
||||
split splitType
|
||||
|
||||
// widget is the widget in the container.
|
||||
// A container can have either two sub containers (left and right) or a
|
||||
// widget. But not both.
|
||||
widget widget.Widget
|
||||
|
||||
// Alignment of the widget if present.
|
||||
hAlign hAlignType
|
||||
vAlign vAlignType
|
||||
}
|
||||
|
||||
// New returns a new root container that will use the provided terminal and
|
||||
// applies the provided options.
|
||||
func New(t terminalapi.Terminal, opts ...Option) *Container {
|
||||
c := &Container{
|
||||
term: t,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt.set(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Returns the parent container of this container.
|
||||
// Returns nil if this container is the root of the container tree.
|
||||
func (c *Container) Parent(opts ...Option) *Container {
|
||||
if c == nil || c.parent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := c.parent
|
||||
for _, opt := range opts {
|
||||
opt.set(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// First returns the first sub container of this container.
|
||||
// This is the left sub container when using SplitVertical() or the top sub
|
||||
// container when using SplitHorizontal().
|
||||
// If this container doesn't have the first sub container yet, it will be
|
||||
// created. Applies the provided options to the first sub container.
|
||||
// Returns nil if this container contains a widget, containers with widgets
|
||||
// cannot have sub containers.
|
||||
func (c *Container) First(opts ...Option) *Container {
|
||||
if c == nil || c.widget != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if child := c.first; child != nil {
|
||||
for _, opt := range opts {
|
||||
opt.set(child)
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
c.first = New(c.term, opts...)
|
||||
c.first.parent = c
|
||||
return c.first
|
||||
}
|
||||
|
||||
// Second returns the second sub container of this container.
|
||||
// This is the left sub container when using SplitVertical() or the top sub
|
||||
// container when using SplitHorizontal().
|
||||
// If this container doesn't have the second sub container yet, it will be
|
||||
// created. Applies the provided options to the second sub container.
|
||||
// Returns nil if this container contains a widget, containers with widgets
|
||||
// cannot have sub containers.
|
||||
func (c *Container) Second(opts ...Option) *Container {
|
||||
if c == nil || c.widget != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if child := c.second; child != nil {
|
||||
for _, opt := range opts {
|
||||
opt.set(child)
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
c.second = New(c.term, opts...)
|
||||
c.second.parent = c
|
||||
return c.second
|
||||
}
|
19
container/container_test.go
Normal file
19
container/container_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
package container
|
||||
|
||||
// Example demonstrates how to use the Container API.
|
||||
func Example() {
|
||||
New( // Create the root container.
|
||||
/* terminal = */ nil,
|
||||
SplitHorizontal(),
|
||||
).First( // This is the top half part of the terminal.
|
||||
SplitVertical(),
|
||||
).First( // Left side on the top.
|
||||
VerticalAlignTop(),
|
||||
PlaceWidget( /* widget = */ nil),
|
||||
).Parent().Second( // Right side on the top.
|
||||
HorizontalAlignRight(),
|
||||
PlaceWidget( /* widget = */ nil),
|
||||
).Parent().Parent().Second( // Bottom half of the terminal.
|
||||
PlaceWidget( /* widget = */ nil),
|
||||
)
|
||||
}
|
169
container/options.go
Normal file
169
container/options.go
Normal file
@ -0,0 +1,169 @@
|
||||
package container
|
||||
|
||||
// options.go defines container options.
|
||||
|
||||
import "github.com/mum4k/termdash/widget"
|
||||
|
||||
// Option is used to provide options.
|
||||
type Option interface {
|
||||
// set sets the provided option.
|
||||
set(*Container)
|
||||
}
|
||||
|
||||
// options stores the provided options.
|
||||
type options struct{}
|
||||
|
||||
// option implements Option.
|
||||
type option func(*Container)
|
||||
|
||||
// set implements Option.set.
|
||||
func (o option) set(c *Container) {
|
||||
o(c)
|
||||
}
|
||||
|
||||
// PlaceWidget places the provided widget into the container.
|
||||
func PlaceWidget(w widget.Widget) Option {
|
||||
return option(func(c *Container) {
|
||||
c.widget = w
|
||||
})
|
||||
}
|
||||
|
||||
// SplitHorizontal configures the container for a horizontal split.
|
||||
func SplitHorizontal() Option {
|
||||
return option(func(c *Container) {
|
||||
c.split = splitTypeHorizontal
|
||||
})
|
||||
}
|
||||
|
||||
// SplitVertical configures the container for a vertical split.
|
||||
// This is the default split type if neither if SplitHorizontal() or
|
||||
// SplitVertical() is specified.
|
||||
func SplitVertical() Option {
|
||||
return option(func(c *Container) {
|
||||
c.split = splitTypeVertical
|
||||
})
|
||||
}
|
||||
|
||||
// HorizontalAlignLeft aligns the placed widget on the left of the
|
||||
// container along the horizontal axis. Has no effect if the container contains
|
||||
// no widget. This is the default horizontal alignment if no other is specified.
|
||||
func HorizontalAlignLeft() Option {
|
||||
return option(func(c *Container) {
|
||||
c.hAlign = hAlignTypeLeft
|
||||
})
|
||||
}
|
||||
|
||||
// HorizontalAlignCenter aligns the placed widget in the center of the
|
||||
// container along the horizontal axis. Has no effect if the container contains
|
||||
// no widget.
|
||||
func HorizontalAlignCenter() Option {
|
||||
return option(func(c *Container) {
|
||||
c.hAlign = hAlignTypeCenter
|
||||
})
|
||||
}
|
||||
|
||||
// HorizontalAlignRight aligns the placed widget on the right of the
|
||||
// container along the horizontal axis. Has no effect if the container contains
|
||||
// no widget.
|
||||
func HorizontalAlignRight() Option {
|
||||
return option(func(c *Container) {
|
||||
c.hAlign = hAlignTypeRight
|
||||
})
|
||||
}
|
||||
|
||||
// VerticalAlignTop aligns the placed widget on the top of the
|
||||
// container along the vertical axis. Has no effect if the container contains
|
||||
// no widget. This is the default vertical alignment if no other is specified.
|
||||
func VerticalAlignTop() Option {
|
||||
return option(func(c *Container) {
|
||||
c.vAlign = vAlignTypeTop
|
||||
})
|
||||
}
|
||||
|
||||
// VerticalAlignMiddle aligns the placed widget in the middle of the
|
||||
// container along the vertical axis. Has no effect if the container contains
|
||||
// no widget.
|
||||
func VerticalAlignMiddle() Option {
|
||||
return option(func(c *Container) {
|
||||
c.vAlign = vAlignTypeMiddle
|
||||
})
|
||||
}
|
||||
|
||||
// VerticalAlignBottom aligns the placed widget at the bottom of the
|
||||
// container along the vertical axis. Has no effect if the container contains
|
||||
// no widget.
|
||||
func VerticalAlignBottom() Option {
|
||||
return option(func(c *Container) {
|
||||
c.vAlign = vAlignTypeBottom
|
||||
})
|
||||
}
|
||||
|
||||
// splitType identifies how a container is split.
|
||||
type splitType int
|
||||
|
||||
// String implements fmt.Stringer()
|
||||
func (st splitType) String() string {
|
||||
if n, ok := splitTypeNames[st]; ok {
|
||||
return n
|
||||
}
|
||||
return "splitTypeUnknown"
|
||||
}
|
||||
|
||||
// splitTypeNames maps splitType values to human readable names.
|
||||
var splitTypeNames = map[splitType]string{
|
||||
splitTypeVertical: "splitTypeVertical",
|
||||
splitTypeHorizontal: "splitTypeHorizontal",
|
||||
}
|
||||
|
||||
const (
|
||||
splitTypeVertical splitType = iota
|
||||
splitTypeHorizontal
|
||||
)
|
||||
|
||||
// hAlignType indicates the horizontal alignment of the widget in the container.
|
||||
type hAlignType int
|
||||
|
||||
// String implements fmt.Stringer()
|
||||
func (hat hAlignType) String() string {
|
||||
if n, ok := hAlignTypeNames[hat]; ok {
|
||||
return n
|
||||
}
|
||||
return "hAlignTypeUnknown"
|
||||
}
|
||||
|
||||
// hAlignTypeNames maps hAlignType values to human readable names.
|
||||
var hAlignTypeNames = map[hAlignType]string{
|
||||
hAlignTypeLeft: "hAlignTypeLeft",
|
||||
hAlignTypeCenter: "hAlignTypeCenter",
|
||||
hAlignTypeRight: "hAlignTypeRight",
|
||||
}
|
||||
|
||||
const (
|
||||
hAlignTypeLeft hAlignType = iota
|
||||
hAlignTypeCenter
|
||||
hAlignTypeRight
|
||||
)
|
||||
|
||||
// vAlignType represents
|
||||
type vAlignType int
|
||||
|
||||
// String implements fmt.Stringer()
|
||||
func (vat vAlignType) String() string {
|
||||
if n, ok := vAlignTypeNames[vat]; ok {
|
||||
return n
|
||||
}
|
||||
return "vAlignTypeUnknown"
|
||||
}
|
||||
|
||||
// vAlignTypeNames maps vAlignType values to human readable names.
|
||||
var vAlignTypeNames = map[vAlignType]string{
|
||||
vAlignTypeTop: "vAlignTypeTop",
|
||||
vAlignTypeMiddle: "vAlignTypeMiddle",
|
||||
vAlignTypeBottom: "vAlignTypeBottom",
|
||||
}
|
||||
|
||||
const (
|
||||
vAlignTypeTop vAlignType = iota
|
||||
vAlignTypeMiddle
|
||||
vAlignTypeBottom
|
||||
)
|
149
doc/design.md
149
doc/design.md
@ -146,152 +146,45 @@ 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
|
||||
implementations.
|
||||
|
||||
The following outlines the terminal API:
|
||||
|
||||
```go
|
||||
// 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.
|
||||
Size() image.Point
|
||||
|
||||
// Clear clears the content of the internal back buffer, resetting all cells
|
||||
// to their default content and attributes.
|
||||
Clear() error
|
||||
// Flush flushes the internal back buffer to the terminal.
|
||||
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 Terminal API is defined in the
|
||||
[terminalapi](http://github.com/mum4k/termdash/terminalapi/terminalapi.go)
|
||||
package.
|
||||
|
||||
The **Event()** method returns the next input event. Different input event
|
||||
types are defined as follows.
|
||||
|
||||
```go
|
||||
// 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() {}
|
||||
|
||||
// Resize is the event used when the terminal was resized.
|
||||
// Implements Event.
|
||||
type Resize struct {
|
||||
// Size is the new size of the terminal.
|
||||
Size image.Point
|
||||
}
|
||||
|
||||
func (*Resize) isEvent() {}
|
||||
|
||||
// Mouse is the event used when the mouse is moved or a mouse button is
|
||||
// pressed.
|
||||
// Implements Event.
|
||||
type Mouse struct {
|
||||
// Position of the mouse on the terminal.
|
||||
Position() image.Point
|
||||
// Button identifies the pressed button if any.
|
||||
Button MouseButton
|
||||
}
|
||||
|
||||
func (*Mouse) isEvent() {}
|
||||
```
|
||||
types are defined in the
|
||||
[event.go](http://github.com/mum4k/termdash/terminalapi/event.go)
|
||||
file.
|
||||
|
||||
### Container API
|
||||
|
||||
The container API is used to split the terminal and place the widgets. Each
|
||||
The Container API is used to split the terminal and place the widgets. Each
|
||||
container can be split to two sub containers or have a widget placed into it.
|
||||
A container can be split either horizontally or vertically.
|
||||
|
||||
The containers further accept styling options and alignment options. The
|
||||
following indicates how the container API will be used.
|
||||
following indicates how the Container API will be used.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
w := mywidget.New(widgetOptions)
|
||||
t := terminal.New(terminalOptions)
|
||||
The Container API is defined in the
|
||||
[container](http://github.com/mum4k/termdash/container/container.go)
|
||||
package.
|
||||
|
||||
t.VerticalSplit()
|
||||
.Left(AlignCenter(), WithWidget(w))
|
||||
.Right(SolidBorder())
|
||||
}
|
||||
```
|
||||
A demonstration how this is used from the client perspective is in the
|
||||
[container_test.go](http://github.com/mum4k/termdash/container/container_test.go)
|
||||
file.
|
||||
|
||||
### Widget API
|
||||
|
||||
Each widget must implement the following API. All widget implementations must
|
||||
Each widget must implement the Widget API. All widget implementations must
|
||||
be thread-safe since the calls that update the displayed values come in
|
||||
concurrently with requests and events from the infrastructure.
|
||||
|
||||
```go
|
||||
// Canvas is where a widget draws its output for display on the terminal.
|
||||
type Canvas struct {}
|
||||
The Widget API is defined in the
|
||||
[widget](http://github.com/mum4k/termdash/widget/widget.go)
|
||||
package.
|
||||
|
||||
// Size returns the size of the 2-D canvas given to the widget.
|
||||
func (c *Canvas) Size() image.Point {}
|
||||
|
||||
// Clear clears all the content on the canvas.
|
||||
func (c *Canvas) Clear() {}
|
||||
|
||||
// FlushDesired provides a hint to the infrastructure that the canvas was
|
||||
// changed and should be flushed to the terminal.
|
||||
func (c *Canvas) FlushDesired() {}
|
||||
|
||||
// SetCell sets the value of the specified cell on the canvas.
|
||||
// Use the options to specify which attributes to modify, if an attribute
|
||||
// option isn't specified, the attribute retains its previous value.
|
||||
func (c *Canvas) SetCell(p image.Point, r rune, opts ...CellOption) {}
|
||||
|
||||
// Widget is a single widget on the dashboard.
|
||||
type Widget interface {
|
||||
// Draw executes the widget, when called the widget should draw on the
|
||||
// canvas. The widget can assume that the canvas content wasn't modified
|
||||
// since the last call, i.e. if the widget doesn't need to change anything in
|
||||
// the output, this can be a no-op.
|
||||
Draw(canvas *Canvas) error
|
||||
|
||||
// Redraw is called when the widget must redraw all of its content because
|
||||
// the previous canvas was invalidated. The widget must not assume that
|
||||
// anything on the canvas remained the same, including its size.
|
||||
Redraw(canvas *Canvas) error
|
||||
|
||||
// Keyboard is called when the widget is focused on the dashboard and a key
|
||||
// shortcut the widget registered for was pressed. Only called if the widget
|
||||
// registered for keyboard events.
|
||||
Keyboard(s Shortcut) error
|
||||
|
||||
// Mouse is called when the widget is focused on the dashboard and a mouse
|
||||
// event happens on its canvas. Only called if the widget registered for mouse
|
||||
// events.
|
||||
Mouse(m *Mouse) error
|
||||
|
||||
// Options returns registration options for the widget.
|
||||
// This is how the widget indicates to the infrastructure whether it is
|
||||
// interested in keyboard or mouse shortcuts, what is its minimum canvas
|
||||
// size, etc.
|
||||
Options() *WidgetOptions
|
||||
}
|
||||
```
|
||||
Each widget gets a Canvas to draw on. The Canvas API is defined in the
|
||||
[canvas](http://github.com/mum4k/termdash/canvas/canvas.go)
|
||||
package.
|
||||
|
||||
## Testing plan
|
||||
|
||||
|
118
keyboard/keyboard.go
Normal file
118
keyboard/keyboard.go
Normal file
@ -0,0 +1,118 @@
|
||||
// Package keyboard defines well known keyboard keys and shortcuts.
|
||||
package keyboard
|
||||
|
||||
// Button represents a single button on the keyboard.
|
||||
type Button rune
|
||||
|
||||
// String implements fmt.Stringer()
|
||||
func (b Button) String() string {
|
||||
if n, ok := buttonNames[b]; ok {
|
||||
return n
|
||||
}
|
||||
return "ButtonUnknown"
|
||||
}
|
||||
|
||||
// buttonNames maps Button values to human readable names.
|
||||
var buttonNames = map[Button]string{
|
||||
ButtonArrowDown: "ButtonArrowDown",
|
||||
ButtonArrowLeft: "ButtonArrowLeft",
|
||||
ButtonArrowRight: "ButtonArrowRight",
|
||||
ButtonArrowUp: "ButtonArrowUp",
|
||||
ButtonBackspace: "ButtonBackspace",
|
||||
ButtonDelete: "ButtonDelete",
|
||||
ButtonEnd: "ButtonEnd",
|
||||
ButtonEnter: "ButtonEnter",
|
||||
ButtonEsc: "ButtonEsc",
|
||||
ButtonF1: "ButtonF1",
|
||||
ButtonF10: "ButtonF10",
|
||||
ButtonF11: "ButtonF11",
|
||||
ButtonF12: "ButtonF12",
|
||||
ButtonF2: "ButtonF2",
|
||||
ButtonF3: "ButtonF3",
|
||||
ButtonF4: "ButtonF4",
|
||||
ButtonF5: "ButtonF5",
|
||||
ButtonF6: "ButtonF6",
|
||||
ButtonF7: "ButtonF7",
|
||||
ButtonF8: "ButtonF8",
|
||||
ButtonF9: "ButtonF9",
|
||||
ButtonHome: "ButtonHome",
|
||||
ButtonInsert: "ButtonInsert",
|
||||
ButtonPgdn: "ButtonPgdn",
|
||||
ButtonPgup: "ButtonPgup",
|
||||
ButtonSpace: "ButtonSpace",
|
||||
ButtonTab: "ButtonTab",
|
||||
ButtonTilde: "ButtonTilde",
|
||||
}
|
||||
|
||||
const (
|
||||
ButtonArrowDown Button = -(iota + 1)
|
||||
ButtonArrowLeft
|
||||
ButtonArrowRight
|
||||
ButtonArrowUp
|
||||
ButtonBackspace
|
||||
ButtonDelete
|
||||
ButtonEnd
|
||||
ButtonEnter
|
||||
ButtonEsc
|
||||
ButtonF1
|
||||
ButtonF10
|
||||
ButtonF11
|
||||
ButtonF12
|
||||
ButtonF2
|
||||
ButtonF3
|
||||
ButtonF4
|
||||
ButtonF5
|
||||
ButtonF6
|
||||
ButtonF7
|
||||
ButtonF8
|
||||
ButtonF9
|
||||
ButtonHome
|
||||
ButtonInsert
|
||||
ButtonPgdn
|
||||
ButtonPgup
|
||||
ButtonSpace
|
||||
ButtonTab
|
||||
ButtonTilde
|
||||
)
|
||||
|
||||
// Modifier represents a modified key on the keyboard, i.e. a keys that
|
||||
// together with buttons can form shortcuts.
|
||||
type Modifier int
|
||||
|
||||
// String implements fmt.Stringer()
|
||||
func (m Modifier) String() string {
|
||||
if n, ok := modifierNames[m]; ok {
|
||||
return n
|
||||
}
|
||||
return "ModifierUnknown"
|
||||
}
|
||||
|
||||
// modifierNames maps Modifier values to human readable names.
|
||||
var modifierNames = map[Modifier]string{
|
||||
ModifierShift: "ModifierShift",
|
||||
ModifierCtrl: "ModifierCtrl",
|
||||
ModifierAlt: "ModifierAlt",
|
||||
ModifierMeta: "ModifierMeta",
|
||||
}
|
||||
|
||||
const (
|
||||
modifierUnknown Modifier = iota
|
||||
ModifierShift
|
||||
ModifierCtrl
|
||||
ModifierAlt
|
||||
|
||||
// ModifierMeta is the platform specific key, i.e. Windows key on windows
|
||||
// or Apple (command) key on MacOS keyboard.
|
||||
ModifierMeta
|
||||
)
|
||||
|
||||
// Shortcut is a key combination pressed on the keyboard.
|
||||
type Shortcut struct {
|
||||
// Modifiers contains zero or more unique modifier keys.
|
||||
Modifiers []Modifier
|
||||
|
||||
// Key is the key pressed on the keyboard.
|
||||
// Either equals to one of the defined Button values or contains the raw
|
||||
// Unicode byte sequence.
|
||||
Key Button
|
||||
}
|
33
mouse/mouse.go
Normal file
33
mouse/mouse.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Package mouse defines known mouse buttons.
|
||||
package mouse
|
||||
|
||||
// Button represents
|
||||
type Button int
|
||||
|
||||
// String implements fmt.Stringer()
|
||||
func (b Button) String() string {
|
||||
if n, ok := buttonNames[b]; ok {
|
||||
return n
|
||||
}
|
||||
return "ButtonUnknown"
|
||||
}
|
||||
|
||||
// buttonNames maps Button values to human readable names.
|
||||
var buttonNames = map[Button]string{
|
||||
ButtonLeft: "ButtonLeft",
|
||||
ButtonRight: "ButtonRight",
|
||||
ButtonMiddle: "ButtonMiddle",
|
||||
ButtonRelease: "ButtonRelease",
|
||||
ButtonWheelUp: "ButtonWheelUp",
|
||||
ButtonWheelDown: "ButtonWheelDown",
|
||||
}
|
||||
|
||||
const (
|
||||
buttonUnknown Button = iota
|
||||
ButtonLeft
|
||||
ButtonRight
|
||||
ButtonMiddle
|
||||
ButtonRelease
|
||||
ButtonWheelUp
|
||||
ButtonWheelDown
|
||||
)
|
48
terminalapi/event.go
Normal file
48
terminalapi/event.go
Normal file
@ -0,0 +1,48 @@
|
||||
package terminalapi
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/mum4k/termdash/keyboard"
|
||||
"github.com/mum4k/termdash/mouse"
|
||||
)
|
||||
|
||||
// event.go defines events that can be received through the terminal API.
|
||||
|
||||
// Event represents an input event.
|
||||
type Event interface {
|
||||
isEvent()
|
||||
}
|
||||
|
||||
// Keyboard is the event used when a key is pressed.
|
||||
// Implements terminalapi.Event.
|
||||
type Keyboard struct {
|
||||
// Key identifies the pressed key.
|
||||
// The rune either has a negative int32 value equal to one of the
|
||||
// keyboard.Button values or a positive int32 value for all other Unicode
|
||||
// byte sequences.
|
||||
Key keyboard.Button
|
||||
}
|
||||
|
||||
func (*Keyboard) isEvent() {}
|
||||
|
||||
// Resize is the event used when the terminal was resized.
|
||||
// Implements terminalapi.Event.
|
||||
type Resize struct {
|
||||
// Size is the new size of the terminal.
|
||||
Size image.Point
|
||||
}
|
||||
|
||||
func (*Resize) isEvent() {}
|
||||
|
||||
// Mouse is the event used when the mouse is moved or a mouse button is
|
||||
// pressed.
|
||||
// Implements terminalapi.Event.
|
||||
type Mouse struct {
|
||||
// Position of the mouse on the terminal.
|
||||
Position image.Point
|
||||
// Button identifies the pressed button if any.
|
||||
Button mouse.Button
|
||||
}
|
||||
|
||||
func (*Mouse) isEvent() {}
|
36
terminalapi/terminalapi.go
Normal file
36
terminalapi/terminalapi.go
Normal file
@ -0,0 +1,36 @@
|
||||
// Package terminalapi defines the API of all terminal implementations.
|
||||
package terminalapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"image"
|
||||
|
||||
"github.com/mum4k/termdash/cell"
|
||||
)
|
||||
|
||||
// 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.
|
||||
Size() image.Point
|
||||
|
||||
// Clear clears the content of the internal back buffer, resetting all cells
|
||||
// to their default content and attributes.
|
||||
Clear() error
|
||||
// Flush flushes the internal back buffer to the terminal.
|
||||
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 ...cell.Option) error
|
||||
|
||||
// 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
|
||||
}
|
43
widget/widget.go
Normal file
43
widget/widget.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Package widget defines the API of a widget on the dashboard.
|
||||
package widget
|
||||
|
||||
import (
|
||||
"github.com/mum4k/termdash/canvas"
|
||||
"github.com/mum4k/termdash/keyboard"
|
||||
"github.com/mum4k/termdash/mouse"
|
||||
)
|
||||
|
||||
// Options contains registration options for a widget.
|
||||
// This is how the widget indicates its needs to the infrastructure.
|
||||
type Options struct {
|
||||
}
|
||||
|
||||
// Widget is a single widget on the dashboard.
|
||||
type Widget interface {
|
||||
// Draw executes the widget, when called the widget should draw on the
|
||||
// canvas. The widget can assume that the canvas content wasn't modified
|
||||
// since the last call, i.e. if the widget doesn't need to change anything in
|
||||
// the output, this can be a no-op.
|
||||
Draw(canvas *canvas.Canvas) error
|
||||
|
||||
// Redraw is called when the widget must redraw all of its content because
|
||||
// the previous canvas was invalidated. The widget must not assume that
|
||||
// anything on the canvas remained the same, including its size.
|
||||
Redraw(canvas *canvas.Canvas) error
|
||||
|
||||
// Keyboard is called when the widget is focused on the dashboard and a key
|
||||
// shortcut the widget registered for was pressed. Only called if the widget
|
||||
// registered for keyboard events.
|
||||
Keyboard(s *keyboard.Shortcut) error
|
||||
|
||||
// Mouse is called when the widget is focused on the dashboard and a mouse
|
||||
// event happens on its canvas. Only called if the widget registered for mouse
|
||||
// events.
|
||||
Mouse(m *mouse.Button) error
|
||||
|
||||
// Options returns registration options for the widget.
|
||||
// This is how the widget indicates to the infrastructure whether it is
|
||||
// interested in keyboard or mouse shortcuts, what is its minimum canvas
|
||||
// size, etc.
|
||||
Options() *Options
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user