Browse Source

First commit. Some basic functionality. Publishing to GitHub now.

tablepad
Oliver 5 years ago
commit
f9f139caaf
  1. 157
      Application.go
  2. 21
      LICENSE.txt
  3. 5
      README.md
  4. 174
      box.go
  5. 34
      demos/basic.go
  6. 5
      doc.go
  7. 112
      flex.go
  8. 151
      form.go
  9. 269
      inputfield.go
  10. 34
      primitive.go

157
Application.go

@ -0,0 +1,157 @@
package tview
import (
"sync"
"github.com/gdamore/tcell"
)
// Application represents the top node of an application.
type Application struct {
sync.Mutex
// The application's screen.
screen tcell.Screen
// The primitive which currently has the keyboard focus.
focus Primitive
// The root primitive to be seen on the screen.
root Primitive
// Whether or not the application resizes the root primitive.
rootAutoSize bool
}
// NewApplication creates and returns a new application.
func NewApplication() *Application {
return &Application{}
}
// Run starts the application and thus the event loop. This function returns
// when Stop() was called.
func (a *Application) Run() error {
var err error
// Make a screen.
a.screen, err = tcell.NewScreen()
if err != nil {
return err
}
if err = a.screen.Init(); err != nil {
return err
}
// We catch panics to clean up because they mess up the terminal.
defer func() {
if p := recover(); p != nil {
a.screen.Fini()
panic(p)
}
}()
// Draw the screen for the first time.
if a.rootAutoSize && a.root != nil {
width, height := a.screen.Size()
a.root.SetRect(0, 0, width, height)
}
a.Draw()
// Start event loop.
for {
event := a.screen.PollEvent()
if event == nil {
break // The screen was finalized.
}
switch event := event.(type) {
case *tcell.EventKey:
if event.Key() == tcell.KeyCtrlC {
a.Stop()
}
a.Lock()
p := a.focus
a.Unlock()
if p != nil {
if handler := p.InputHandler(); handler != nil {
handler(event)
a.Draw()
}
}
case *tcell.EventResize:
if a.rootAutoSize && a.root != nil {
width, height := a.screen.Size()
a.Lock()
a.root.SetRect(0, 0, width, height)
a.Unlock()
a.Draw()
}
}
}
return nil
}
// Stop stops the application, causing Run() to return.
func (a *Application) Stop() {
a.screen.Fini()
}
// Draw refreshes the screen. It calls the Draw() function of the application's
// root primitive and then syncs the screen buffer.
func (a *Application) Draw() *Application {
a.Lock()
defer a.Unlock()
// Maybe we're not ready yet.
if a.screen == nil {
return a
}
// Draw all primitives.
if a.root != nil {
a.root.Draw(a.screen)
}
// Sync screen.
a.screen.Show()
return a
}
// SetRoot sets the root primitive for this application. This function must be
// called or nothing will be displayed when the application starts.
//
// If autoSize is set to true, the application will set the root primitive's
// position to (0,0) and its size to the screen's size. It will also resize and
// redraw it when the screen resizes.
func (a *Application) SetRoot(root Primitive, autoSize bool) *Application {
a.Lock()
defer a.Unlock()
a.root = root
a.rootAutoSize = autoSize
return a
}
// SetFocus sets the focus on a new primitive. All key events will be redirected
// to that primitive. Callers must ensure that the primitive will handle key
// events.
//
// Blur() will be called on the previously focused primitive. Focus() will be
// called on the new primitive.
func (a *Application) SetFocus(p Primitive) *Application {
if p.InputHandler() == nil {
return a
}
a.Lock()
if a.focus != nil {
a.focus.Blur()
}
a.focus = p
a.Unlock()
p.Focus(a)
return a
}

21
LICENSE.txt

@ -0,0 +1,21 @@
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

5
README.md

@ -0,0 +1,5 @@
# Widgets for Terminal GUIs
Based on [github.com/gdamore/tcell](https://github.com/gdamore/tcell).
Work in progress.

174
box.go

@ -0,0 +1,174 @@
package tview
import "github.com/gdamore/tcell"
// Characters to draw the box border.
const (
BoxVertBar = '\u2500'
BoxHorBar = '\u2502'
BoxTopLeftCorner = '\u250c'
BoxTopRightCorner = '\u2510'
BoxBottomRightCorner = '\u2518'
BoxBottomLeftCorner = '\u2514'
BoxEllipsis = '\u2026'
)
// Box implements Rect with a background and optional elements such as a border
// and a title.
type Box struct {
// The position of the rect.
x, y, width, height int
// Whether or not the box has focus.
hasFocus bool
// The box's background color.
backgroundColor tcell.Color
// Whether or not a border is drawn, reducing the box's space for content by
// two in width and height.
border bool
// The color of the border.
borderColor tcell.Color
// The color of the border when the box has focus.
focusedBorderColor tcell.Color
// The title. Only visible if there is a border, too.
title string
// The color of the title.
titleColor tcell.Color
}
// NewBox returns a Box without a border.
func NewBox() *Box {
return &Box{
width: 15,
height: 10,
borderColor: tcell.ColorWhite,
focusedBorderColor: tcell.ColorYellow,
titleColor: tcell.ColorWhite,
}
}
// Draw draws this primitive onto the screen.
func (b *Box) Draw(screen tcell.Screen) {
// Don't draw anything if there is no space.
if b.width <= 0 || b.height <= 0 {
return
}
def := tcell.StyleDefault
// Fill background.
background := def.Background(b.backgroundColor)
for y := b.y; y < b.y+b.height; y++ {
for x := b.x; x < b.x+b.width; x++ {
screen.SetContent(x, y, ' ', nil, background)
}
}
// Draw border.
if b.border && b.width >= 2 && b.height >= 2 {
border := background.Foreground(b.borderColor)
if b.hasFocus {
border = background.Foreground(b.focusedBorderColor)
}
for x := b.x + 1; x < b.x+b.width-1; x++ {
screen.SetContent(x, b.y, BoxVertBar, nil, border)
screen.SetContent(x, b.y+b.height-1, BoxVertBar, nil, border)
}
for y := b.y + 1; y < b.y+b.height-1; y++ {
screen.SetContent(b.x, y, BoxHorBar, nil, border)
screen.SetContent(b.x+b.width-1, y, BoxHorBar, nil, border)
}
screen.SetContent(b.x, b.y, BoxTopLeftCorner, nil, border)
screen.SetContent(b.x+b.width-1, b.y, BoxTopRightCorner, nil, border)
screen.SetContent(b.x, b.y+b.height-1, BoxBottomLeftCorner, nil, border)
screen.SetContent(b.x+b.width-1, b.y+b.height-1, BoxBottomRightCorner, nil, border)
// Draw title.
if b.title != "" && b.width >= 4 {
title := background.Foreground(b.titleColor)
x := b.x
for index, ch := range b.title {
x++
if x >= b.x+b.width-1 {
break
}
if x == b.x+b.width-2 && index < len(b.title)-1 {
ch = BoxEllipsis
}
screen.SetContent(x, b.y, ch, nil, title)
}
}
}
}
// GetRect returns the current position of the rectangle, x, y, width, and
// height.
func (b *Box) GetRect() (int, int, int, int) {
return b.x, b.y, b.width, b.height
}
// SetRect sets a new position of the rectangle.
func (b *Box) SetRect(x, y, width, height int) {
b.x = x
b.y = y
b.width = width
b.height = height
}
// InputHandler returns nil.
func (b *Box) InputHandler() func(event *tcell.EventKey) {
return nil
}
// SetBackgroundColor sets the box's background color.
func (b *Box) SetBackgroundColor(color tcell.Color) *Box {
b.backgroundColor = color
return b
}
// SetBorder sets the flag indicating whether or not the box should have a
// border.
func (b *Box) SetBorder(show bool) *Box {
b.border = show
return b
}
// SetBorderColor sets the box's border color.
func (b *Box) SetBorderColor(color tcell.Color) *Box {
b.borderColor = color
return b
}
// SetFocusedBorderColor sets the box's border color for when the box has focus.
func (b *Box) SetFocusedBorderColor(color tcell.Color) *Box {
b.focusedBorderColor = color
return b
}
// SetTitle sets the box's title.
func (b *Box) SetTitle(title string) *Box {
b.title = title
return b
}
// SetTitleColor sets the box's title color.
func (b *Box) SetTitleColor(color tcell.Color) *Box {
b.titleColor = color
return b
}
// Focus is called when this primitive receives focus.
func (b *Box) Focus(app *Application) {
b.hasFocus = true
}
// Blur is called when this primitive loses focus.
func (b *Box) Blur() {
b.hasFocus = false
}

34
demos/basic.go

@ -0,0 +1,34 @@
package main
import "github.com/rivo/tview"
func main() {
form := tview.NewForm().AddItem("First name", "", 20, nil).AddItem("Last name", "", 20, nil).AddItem("Age", "", 4, nil)
form.SetBorder(true)
box := tview.NewFlex(tview.FlexColumn, []tview.Primitive{
form,
tview.NewFlex(tview.FlexRow, []tview.Primitive{
tview.NewBox().SetBorder(true).SetTitle("Second"),
tview.NewBox().SetBorder(true).SetTitle("Third"),
}),
tview.NewBox().SetBorder(true).SetTitle("Fourth"),
})
box.AddItem(tview.NewBox().SetBorder(true).SetTitle("Fifth"), 20)
inputField := tview.NewInputField().
SetLabel("Type something: ").
SetFieldLength(10).
SetAcceptanceFunc(tview.InputFieldFloat)
inputField.SetBorder(true).SetTitle("Type!")
final := tview.NewFlex(tview.FlexRow, []tview.Primitive{box})
final.AddItem(inputField, 3)
app := tview.NewApplication()
app.SetRoot(final, true).SetFocus(form)
if err := app.Run(); err != nil {
panic(err)
}
}

5
doc.go

@ -0,0 +1,5 @@
/*
Package tview implements primitives for terminal based applications. It uses
github.com/gdamore/tcell.
*/
package tview

112
flex.go

@ -0,0 +1,112 @@
package tview
import "github.com/gdamore/tcell"
// Configuration values.
const (
FlexRow = iota
FlexColumn
)
// FlexItem holds layout options for one item.
type FlexItem struct {
Item Primitive // The item to be positioned.
FixedSize int // The item's fixed size which may not be changed, 0 if it has no fixed size.
}
// Flex is a basic implementation of a flexbox layout.
type Flex struct {
x, y, width, height int // The size and position of this primitive.
Items []FlexItem // The items to be positioned.
Direction int // FlexRow or FlexColumn.
}
// NewFlex returns a new flexbox layout container with the given primitives.
// The items all have no fixed size. If more control is needed, call AddItem().
// The direction argument must be FlexRow or FlexColumn.
func NewFlex(direction int, items []Primitive) *Flex {
box := &Flex{
width: 15,
height: 10,
Direction: direction,
}
for _, item := range items {
box.Items = append(box.Items, FlexItem{Item: item})
}
return box
}
// AddItem adds a new item to the container. fixedSize is a size that may not be
// changed. A value of 0 means that its size may be changed.
func (f *Flex) AddItem(item Primitive, fixedSize int) *Flex {
f.Items = append(f.Items, FlexItem{Item: item, FixedSize: fixedSize})
return f
}
// Draw draws this primitive onto the screen.
func (f *Flex) Draw(screen tcell.Screen) {
// Calculate size and position of the items.
// How much space can we distribute?
var variables int
distSize := f.width
if f.Direction == FlexRow {
distSize = f.height
}
for _, item := range f.Items {
if item.FixedSize > 0 {
distSize -= item.FixedSize
} else {
variables++
}
}
// Calculate positions and draw items.
pos := f.x
if f.Direction == FlexRow {
pos = f.y
}
for _, item := range f.Items {
size := item.FixedSize
if size <= 0 {
size = distSize / variables
distSize -= size
variables--
}
if f.Direction == FlexColumn {
item.Item.SetRect(pos, f.y, size, f.height)
} else {
item.Item.SetRect(f.x, pos, f.width, size)
}
pos += size
item.Item.Draw(screen)
}
}
// GetRect returns the current position of the primitive, x, y, width, and
// height.
func (f *Flex) GetRect() (int, int, int, int) {
return f.x, f.y, f.width, f.height
}
// SetRect sets a new position of the primitive.
func (f *Flex) SetRect(x, y, width, height int) {
f.x = x
f.y = y
f.width = width
f.height = height
}
// InputHandler returns nil.
func (f *Flex) InputHandler() func(event *tcell.EventKey) {
return nil
}
// Focus is called when this primitive receives focus.
func (f *Flex) Focus(app *Application) {
}
// Blur is called when this primitive loses focus.
func (f *Flex) Blur() {
}

151
form.go

@ -0,0 +1,151 @@
package tview
import (
"strings"
"github.com/gdamore/tcell"
)
// Form is a Box which contains multiple input fields, one per row.
type Form struct {
Box
// The items of the form (one row per item).
items []*InputField
// The number of empty rows between items.
itemPadding int
// The index of the item which has focus.
focusedItem int
// The label color.
labelColor tcell.Color
// The background color of the input area.
fieldBackgroundColor tcell.Color
// The text color of the input area.
fieldTextColor tcell.Color
}
// NewForm returns a new form.
func NewForm() *Form {
return &Form{
Box: *NewBox(),
itemPadding: 1,
labelColor: tcell.ColorYellow,
fieldBackgroundColor: tcell.ColorBlue,
fieldTextColor: tcell.ColorWhite,
}
}
// SetItemPadding sets the number of empty rows between form items.
func (f *Form) SetItemPadding(padding int) *Form {
f.itemPadding = padding
return f
}
// SetLabelColor sets the color of the labels.
func (f *Form) SetLabelColor(color tcell.Color) *Form {
f.labelColor = color
return f
}
// SetFieldBackgroundColor sets the background color of the input areas.
func (f *Form) SetFieldBackgroundColor(color tcell.Color) *Form {
f.fieldBackgroundColor = color
return f
}
// SetFieldTextColor sets the text color of the input areas.
func (f *Form) SetFieldTextColor(color tcell.Color) *Form {
f.fieldTextColor = color
return f
}
// AddItem adds a new item to the form. It has a label, an optional initial
// value, a field length (a value of 0 extends it as far as possible), and
// an optional accept function to validate the item's value (set to nil to
// accept any text).
func (f *Form) AddItem(label, value string, fieldLength int, accept func(textToCheck string, lastChar rune) bool) *Form {
f.items = append(f.items, NewInputField().
SetLabel(label).
SetText(value).
SetFieldLength(fieldLength).
SetAcceptanceFunc(accept))
return f
}
// Draw draws this primitive onto the screen.
func (f *Form) Draw(screen tcell.Screen) {
f.Box.Draw(screen)
// Determine the dimensions.
x := f.x
y := f.y
width := f.width
bottomLimit := f.y + f.height
if f.border {
x++
y++
width -= 2
bottomLimit -= 2
}
// Find the longest label.
var labelLength int
for _, inputField := range f.items {
label := strings.TrimSpace(inputField.GetLabel())
if len([]rune(label)) > labelLength {
labelLength = len([]rune(label))
}
}
labelLength++ // Add one space.
// Set up and draw the input fields.
for _, inputField := range f.items {
if y >= bottomLimit {
break
}
label := strings.TrimSpace(inputField.GetLabel())
inputField.SetLabelColor(f.labelColor).
SetFieldBackgroundColor(f.fieldBackgroundColor).
SetFieldTextColor(f.fieldTextColor).
SetLabel(label+strings.Repeat(" ", labelLength-len([]rune(label)))).
SetBackgroundColor(f.backgroundColor).
SetRect(x, y, width, 1)
inputField.Draw(screen)
y += 1 + f.itemPadding
}
}
// Focus is called by the application when the primitive receives focus.
func (f *Form) Focus(app *Application) {
f.Box.Focus(app)
if len(f.items) == 0 {
return
}
// Hand on the focus to one of our items.
if f.focusedItem < 0 || f.focusedItem >= len(f.items) {
f.focusedItem = 0
}
f.hasFocus = false
inputField := f.items[f.focusedItem]
inputField.SetDoneFunc(func(key tcell.Key) {
switch key {
case tcell.KeyTab:
f.focusedItem++
f.Focus(app)
}
})
app.SetFocus(inputField)
}
// InputHandler returns the handler for this primitive.
func (f *Form) InputHandler() func(event *tcell.EventKey) {
return func(event *tcell.EventKey) {
}
}

269
inputfield.go

@ -0,0 +1,269 @@
package tview
import (
"math"
"strconv"
"github.com/gdamore/tcell"
)
var (
// InputFieldInteger accepts integers.
InputFieldInteger func(text string, ch rune) bool
// InputFieldFloat accepts floating-point numbers.
InputFieldFloat func(text string, ch rune) bool
// InputFieldMaxLength returns an input field accept handler which accepts
// input strings up to a given length. Use it like this:
//
// inputField.SetAcceptanceFunc(InputFieldMaxLength(10)) // Accept up to 10 characters.
InputFieldMaxLength func(maxLength int) func(text string, ch rune) bool
)
// Package initialization.
func init() {
// Initialize the predefined handlers.
InputFieldInteger = func(text string, ch rune) bool {
if text == "-" {
return true
}
_, err := strconv.Atoi(text)
return err == nil
}
InputFieldFloat = func(text string, ch rune) bool {
if text == "-" || text == "." {
return true
}
_, err := strconv.ParseFloat(text, 64)
return err == nil
}
InputFieldMaxLength = func(maxLength int) func(text string, ch rune) bool {
return func(text string, ch rune) bool {
return len([]rune(text)) <= maxLength
}
}
}
// InputField is a one-line box (three lines if there is a title) where the
// user can enter text.
type InputField struct {
Box
// The text that was entered.
text string
// The text to be displayed before the input area.
label string
// The label color.
labelColor tcell.Color
// The background color of the input area.
fieldBackgroundColor tcell.Color
// The text color of the input area.
fieldTextColor tcell.Color
// The length of the input area. A value of 0 means extend as much as
// possible.
fieldLength int
// An optional function which may reject the last character that was entered.
accept func(text string, ch rune) bool
// An optional function which is called when the user indicated that they
// are done entering text. The key which was pressed is provided.
done func(tcell.Key)
}
// NewInputField returns a new input field.
func NewInputField() *InputField {
return &InputField{
Box: *NewBox(),
labelColor: tcell.ColorYellow,
fieldBackgroundColor: tcell.ColorBlue,
fieldTextColor: tcell.ColorWhite,
}
}
// SetText sets the current text of the input field.
func (i *InputField) SetText(text string) *InputField {
i.text = text
return i
}
// GetText returns the current text of the input field.
func (i *InputField) GetText() string {
return i.text
}
// SetLabel sets the text to be displayed before the input area.
func (i *InputField) SetLabel(label string) *InputField {
i.label = label
return i
}
// GetLabel returns the text to be displayed before the input area.
func (i *InputField) GetLabel() string {
return i.label
}
// SetLabelColor sets the color of the label.
func (i *InputField) SetLabelColor(color tcell.Color) *InputField {
i.labelColor = color
return i
}
// SetFieldBackgroundColor sets the background color of the input area.
func (i *InputField) SetFieldBackgroundColor(color tcell.Color) *InputField {
i.fieldBackgroundColor = color
return i
}
// SetFieldTextColor sets the text color of the input area.
func (i *InputField) SetFieldTextColor(color tcell.Color) *InputField {
i.fieldTextColor = color
return i
}
// SetFieldLength sets the length of the input area. A value of 0 means extend
// as much as possible.
func (i *InputField) SetFieldLength(length int) *InputField {
i.fieldLength = length
return i
}
// SetAcceptanceFunc sets a handler which may reject the last character that was
// entered (by returning false).
//
// This package defines a number of variables Prefixed with InputField which may
// be used for common input (e.g. numbers, maximum text length).
func (i *InputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar rune) bool) *InputField {
i.accept = handler
return i
}
// SetDoneFunc sets a handler which is called when the user is done entering
// text. The callback function is provided with the key that was pressed, which
// is one of the following:
//
// - KeyEnter: Done entering text.
// - KeyEscape: Abort text input.
// - KeyTab: Move to the next field.
func (i *InputField) SetDoneFunc(handler func(key tcell.Key)) *InputField {
i.done = handler
return i
}
// Draw draws this primitive onto the screen.
func (i *InputField) Draw(screen tcell.Screen) {
i.Box.Draw(screen)
// Prepare
x := i.x
y := i.y
rightLimit := x + i.width
height := i.height
if i.border {
x++
y++
rightLimit -= 2
height -= 2
}
if height < 1 || rightLimit <= x {
return
}
// Draw label.
labelStyle := tcell.StyleDefault.Background(i.backgroundColor).Foreground(i.labelColor)
for _, ch := range i.label {
if x >= rightLimit {
return
}
screen.SetContent(x, y, ch, nil, labelStyle)
x++
}
// Draw input area.
inputStyle := tcell.StyleDefault.Background(i.fieldBackgroundColor).Foreground(i.fieldTextColor)
fieldLength := i.fieldLength
if fieldLength == 0 {
fieldLength = math.MaxInt64
}
if rightLimit-x < fieldLength {
fieldLength = rightLimit - x
}
text := []rune(i.text)
index := 0
if fieldLength-1 < len(text) {
index = len(text) - fieldLength + 1
}
for fieldLength > 0 {
ch := ' '
if index < len(text) {
ch = text[index]
}
screen.SetContent(x, y, ch, nil, inputStyle)
x++
index++
fieldLength--
}
// Set cursor.
if i.hasFocus {
i.setCursor(screen)
}
}
// setCursor sets the cursor position.
func (i *InputField) setCursor(screen tcell.Screen) {
x := i.x
y := i.y
rightLimit := x + i.width
if i.border {
x++
y++
rightLimit -= 2
}
fieldLength := len([]rune(i.text))
if fieldLength > i.fieldLength-1 {
fieldLength = i.fieldLength - 1
}
x += len([]rune(i.label)) + fieldLength
if x >= rightLimit {
x = rightLimit - 1
}
screen.ShowCursor(x, y)
}
// InputHandler returns the handler for this primitive.
func (i *InputField) InputHandler() func(event *tcell.EventKey) {
return func(event *tcell.EventKey) {
// Process key event.
switch key := event.Key(); key {
case tcell.KeyRune: // Regular character.
newText := i.text + string(event.Rune())
if i.accept != nil {
if !i.accept(newText, event.Rune()) {
break
}
}
i.text = newText
case tcell.KeyCtrlU: // Delete all.
i.text = ""
case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete last character.
if len([]rune(i.text)) == 0 {
break
}
i.text = i.text[:len([]rune(i.text))-1]
case tcell.KeyEnter, tcell.KeyTab, tcell.KeyEscape: // We're done.
if i.done != nil {
i.done(key)
}
}
}
}

34
primitive.go

@ -0,0 +1,34 @@
package tview
import "github.com/gdamore/tcell"
// Primitive is the top-most interface for all graphical primitives.
type Primitive interface {
// Draw draws this primitive onto the screen. Implementers can call the
// screen's ShowCursor() function but should only do so when they have focus.
// (They will need to keep track of this themselves.)
Draw(screen tcell.Screen)
// GetRect returns the current position of the primitive, x, y, width, and
// height.
GetRect() (int, int, int, int)
// SetRect sets a new position of the primitive.
SetRect(x, y, width, height int)
// InputHandler returns a handler which receives key events when it has focus.
// It is called by the Application class.
//
// A value of nil may also be returned, in which case this primitive cannot
// receive focus and will not process any key events.
//
// The Application's Draw() function will be called automatically after the
// handler returns.
InputHandler() func(event *tcell.EventKey)
// Focus is called by the application when the primitive receives focus.
Focus(app *Application)
// Blur is called by the application when the primitive loses focus.
Blur()
}
Loading…
Cancel
Save