Added DropDown. Also needed to refactor a bit to reduce dependencies.
parent
d5bf1a4ef0
commit
2bd80aa513
|
@ -45,7 +45,9 @@ func (a *Application) Run() error {
|
|||
// We catch panics to clean up because they mess up the terminal.
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
a.screen.Fini()
|
||||
if a.screen != nil {
|
||||
a.screen.Fini()
|
||||
}
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
|
@ -59,6 +61,9 @@ func (a *Application) Run() error {
|
|||
|
||||
// Start event loop.
|
||||
for {
|
||||
if a.screen == nil {
|
||||
break
|
||||
}
|
||||
event := a.screen.PollEvent()
|
||||
if event == nil {
|
||||
break // The screen was finalized.
|
||||
|
@ -66,14 +71,16 @@ func (a *Application) Run() error {
|
|||
switch event := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
if event.Key() == tcell.KeyCtrlC {
|
||||
a.Stop()
|
||||
a.Stop() // Ctrl-C closes the application.
|
||||
}
|
||||
a.Lock()
|
||||
p := a.focus
|
||||
p := a.focus // Pass other key events to the currently focused primitive.
|
||||
a.Unlock()
|
||||
if p != nil {
|
||||
if handler := p.InputHandler(); handler != nil {
|
||||
handler(event)
|
||||
handler(event, func(p Primitive) {
|
||||
a.SetFocus(p)
|
||||
})
|
||||
a.Draw()
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +100,11 @@ func (a *Application) Run() error {
|
|||
|
||||
// Stop stops the application, causing Run() to return.
|
||||
func (a *Application) Stop() {
|
||||
if a.screen == nil {
|
||||
return
|
||||
}
|
||||
a.screen.Fini()
|
||||
a.screen = nil
|
||||
}
|
||||
|
||||
// Draw refreshes the screen. It calls the Draw() function of the application's
|
||||
|
@ -102,7 +113,7 @@ func (a *Application) Draw() *Application {
|
|||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
// Maybe we're not ready yet.
|
||||
// Maybe we're not ready yet or not anymore.
|
||||
if a.screen == nil {
|
||||
return a
|
||||
}
|
||||
|
@ -151,7 +162,9 @@ func (a *Application) SetFocus(p Primitive) *Application {
|
|||
}
|
||||
a.focus = p
|
||||
a.Unlock()
|
||||
p.Focus(a)
|
||||
p.Focus(func(p Primitive) {
|
||||
a.SetFocus(p)
|
||||
})
|
||||
|
||||
return a
|
||||
}
|
||||
|
|
4
box.go
4
box.go
|
@ -145,7 +145,7 @@ func (b *Box) SetRect(x, y, width, height int) {
|
|||
}
|
||||
|
||||
// InputHandler returns nil.
|
||||
func (b *Box) InputHandler() func(event *tcell.EventKey) {
|
||||
func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ func (b *Box) SetTitleColor(color tcell.Color) *Box {
|
|||
}
|
||||
|
||||
// Focus is called when this primitive receives focus.
|
||||
func (b *Box) Focus(app *Application) {
|
||||
func (b *Box) Focus(delegate func(p Primitive)) {
|
||||
b.hasFocus = true
|
||||
}
|
||||
|
||||
|
|
|
@ -118,8 +118,8 @@ func (b *Button) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (b *Button) InputHandler() func(event *tcell.EventKey) {
|
||||
return func(event *tcell.EventKey) {
|
||||
func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
// Process key event.
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyEnter: // Selected.
|
||||
|
|
|
@ -10,24 +10,24 @@ func main() {
|
|||
var list *tview.List
|
||||
|
||||
frame := tview.NewFrame(tview.NewForm().
|
||||
AddItem("First name", "", 20, nil).
|
||||
AddItem("Last name", "", 20, nil).
|
||||
AddItem("Age", "", 4, nil).
|
||||
AddInputField("First name", "", 20, nil).
|
||||
AddInputField("Last name", "", 20, nil).
|
||||
AddInputField("Age", "", 4, nil).
|
||||
AddDropDown("Select", []string{"One", "Two", "Three"}, 1, func(text string, index int) {
|
||||
if text == "Three" {
|
||||
app.Stop()
|
||||
}
|
||||
}).
|
||||
AddButton("Save", func() { app.Stop() }).
|
||||
AddButton("Cancel", nil).
|
||||
AddButton("Go to list", func() { app.SetFocus(list) })).
|
||||
AddText("Customer details", true, tview.AlignLeft, tcell.ColorRed).
|
||||
AddText("Customer details", false, tview.AlignCenter, tcell.ColorRed)
|
||||
frame.SetBorder(true)
|
||||
frame.SetBorder(true).SetTitle("Customers")
|
||||
|
||||
list = tview.NewList().
|
||||
AddItem("Edit a form", "You can do whatever you want", 'e').
|
||||
AddItem("Quit the program", "Do it!", 0).
|
||||
SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) {
|
||||
if shortcut == 'e' {
|
||||
app.SetFocus(frame)
|
||||
}
|
||||
})
|
||||
AddItem("Edit a form", "You can do whatever you want", 'e', func() { app.SetFocus(frame) }).
|
||||
AddItem("Quit the program", "Do it!", 0, func() { app.Stop() })
|
||||
list.SetBorder(true)
|
||||
|
||||
flex := tview.NewFlex(tview.FlexColumn, []tview.Primitive{
|
||||
|
|
|
@ -0,0 +1,298 @@
|
|||
package tview
|
||||
|
||||
import (
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// dropDownOption is one option that can be selected in a drop-down primitive.
|
||||
type dropDownOption struct {
|
||||
Text string // The text to be displayed in the drop-down.
|
||||
Selected func() // The (optional) callback for when this option was selected.
|
||||
}
|
||||
|
||||
// DropDown is a one-line box (three lines if there is a title) where the
|
||||
// user can enter text.
|
||||
type DropDown struct {
|
||||
*Box
|
||||
|
||||
// The options from which the user can choose.
|
||||
options []*dropDownOption
|
||||
|
||||
// The index of the currently selected option. Negative if no option is
|
||||
// currently selected.
|
||||
currentOption int
|
||||
|
||||
// Set to true if the options are visible and selectable.
|
||||
open bool
|
||||
|
||||
// The list element for the options.
|
||||
list *List
|
||||
|
||||
// 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 is called when the user indicated that they
|
||||
// are done selecting options. The key which was pressed is provided (tab,
|
||||
// shift-tab, or escape).
|
||||
done func(tcell.Key)
|
||||
}
|
||||
|
||||
// NewDropDown returns a new drop-down.
|
||||
func NewDropDown() *DropDown {
|
||||
list := NewList().ShowSecondaryText(false)
|
||||
list.SetMainTextColor(tcell.ColorBlack).
|
||||
SetSelectedTextColor(tcell.ColorBlack).
|
||||
SetSelectedBackgroundColor(tcell.ColorWhite).
|
||||
SetBackgroundColor(tcell.ColorGreen)
|
||||
|
||||
d := &DropDown{
|
||||
Box: NewBox(),
|
||||
currentOption: -1,
|
||||
list: list,
|
||||
labelColor: tcell.ColorYellow,
|
||||
fieldBackgroundColor: tcell.ColorBlue,
|
||||
fieldTextColor: tcell.ColorWhite,
|
||||
}
|
||||
|
||||
d.focus = d
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// SetCurrentOption sets the index of the currently selected option.
|
||||
func (d *DropDown) SetCurrentOption(index int) *DropDown {
|
||||
d.currentOption = index
|
||||
d.list.SetCurrentItem(index)
|
||||
return d
|
||||
}
|
||||
|
||||
// SetLabel sets the text to be displayed before the input area.
|
||||
func (d *DropDown) SetLabel(label string) *DropDown {
|
||||
d.label = label
|
||||
return d
|
||||
}
|
||||
|
||||
// GetLabel returns the text to be displayed before the input area.
|
||||
func (d *DropDown) GetLabel() string {
|
||||
return d.label
|
||||
}
|
||||
|
||||
// SetLabelColor sets the color of the label.
|
||||
func (d *DropDown) SetLabelColor(color tcell.Color) *DropDown {
|
||||
d.labelColor = color
|
||||
return d
|
||||
}
|
||||
|
||||
// SetFieldBackgroundColor sets the background color of the options area.
|
||||
func (d *DropDown) SetFieldBackgroundColor(color tcell.Color) *DropDown {
|
||||
d.fieldBackgroundColor = color
|
||||
return d
|
||||
}
|
||||
|
||||
// SetFieldTextColor sets the text color of the options area.
|
||||
func (d *DropDown) SetFieldTextColor(color tcell.Color) *DropDown {
|
||||
d.fieldTextColor = color
|
||||
return d
|
||||
}
|
||||
|
||||
// SetFormAttributes sets attributes shared by all form items.
|
||||
func (d *DropDown) SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
|
||||
d.label = label
|
||||
d.labelColor = labelColor
|
||||
d.backgroundColor = bgColor
|
||||
d.fieldTextColor = fieldTextColor
|
||||
d.fieldBackgroundColor = fieldBgColor
|
||||
return d
|
||||
}
|
||||
|
||||
// SetFieldLength sets the length of the options area. A value of 0 means extend
|
||||
// to as long as the longest option text.
|
||||
func (d *DropDown) SetFieldLength(length int) *DropDown {
|
||||
d.fieldLength = length
|
||||
return d
|
||||
}
|
||||
|
||||
// AddOption adds a new selectable option to this drop-down. The "selected"
|
||||
// callback is called when this option was selected. It may be nil.
|
||||
func (d *DropDown) AddOption(text string, selected func()) *DropDown {
|
||||
d.options = append(d.options, &dropDownOption{Text: text, Selected: selected})
|
||||
d.list.AddItem(text, "", 0, selected)
|
||||
return d
|
||||
}
|
||||
|
||||
// SetOptions replaces all current options with the ones provided and installs
|
||||
// one callback function which is called when one of the options is selected.
|
||||
// It will be called with the option's text and its index into the options
|
||||
// slice. The "selected" parameter may be nil.
|
||||
func (d *DropDown) SetOptions(texts []string, selected func(text string, index int)) *DropDown {
|
||||
d.list.ClearItems()
|
||||
d.options = nil
|
||||
for index, text := range texts {
|
||||
func(t string, i int) {
|
||||
d.AddOption(text, func() {
|
||||
if selected != nil {
|
||||
selected(t, i)
|
||||
}
|
||||
})
|
||||
}(text, index)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// SetDoneFunc sets a handler which is called when the user is done selecting
|
||||
// options. The callback function is provided with the key that was pressed,
|
||||
// which is one of the following:
|
||||
//
|
||||
// - KeyEscape: Abort selection.
|
||||
// - KeyTab: Move to the next field.
|
||||
// - KeyBacktab: Move to the previous field.
|
||||
func (d *DropDown) SetDoneFunc(handler func(key tcell.Key)) *DropDown {
|
||||
d.done = handler
|
||||
return d
|
||||
}
|
||||
|
||||
// SetFinishedFunc calls SetDoneFunc().
|
||||
func (d *DropDown) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
|
||||
return d.SetDoneFunc(handler)
|
||||
}
|
||||
|
||||
// GetFocusable returns the item's Focusable.
|
||||
func (d *DropDown) GetFocusable() Focusable {
|
||||
return d.focus
|
||||
}
|
||||
|
||||
// Draw draws this primitive onto the screen.
|
||||
func (d *DropDown) Draw(screen tcell.Screen) {
|
||||
d.Box.Draw(screen)
|
||||
|
||||
// Prepare
|
||||
x := d.x
|
||||
y := d.y
|
||||
rightLimit := x + d.width
|
||||
height := d.height
|
||||
if d.border {
|
||||
x++
|
||||
y++
|
||||
rightLimit -= 2
|
||||
height -= 2
|
||||
}
|
||||
if height < 1 || rightLimit <= x {
|
||||
return
|
||||
}
|
||||
|
||||
// Draw label.
|
||||
x += Print(screen, d.label, x, y, rightLimit-x, AlignLeft, d.labelColor)
|
||||
|
||||
// What's the longest option text?
|
||||
maxLength := 0
|
||||
for _, option := range d.options {
|
||||
length := len([]rune(option.Text))
|
||||
if length > maxLength {
|
||||
maxLength = length
|
||||
}
|
||||
}
|
||||
|
||||
// Draw selection area.
|
||||
fieldLength := d.fieldLength
|
||||
if fieldLength == 0 {
|
||||
fieldLength = maxLength
|
||||
}
|
||||
if rightLimit-x < fieldLength {
|
||||
fieldLength = rightLimit - x
|
||||
}
|
||||
fieldStyle := tcell.StyleDefault.Background(d.fieldBackgroundColor)
|
||||
if d.GetFocusable().HasFocus() && !d.open {
|
||||
fieldStyle = fieldStyle.Background(d.fieldTextColor)
|
||||
}
|
||||
for index := 0; index < fieldLength; index++ {
|
||||
screen.SetContent(x+index, y, ' ', nil, fieldStyle)
|
||||
}
|
||||
|
||||
// Draw selected text.
|
||||
if d.currentOption >= 0 && d.currentOption < len(d.options) {
|
||||
color := d.fieldTextColor
|
||||
if d.GetFocusable().HasFocus() && !d.open {
|
||||
color = d.fieldBackgroundColor
|
||||
}
|
||||
Print(screen, d.options[d.currentOption].Text, x, y, fieldLength, AlignLeft, color)
|
||||
}
|
||||
|
||||
// Draw options list.
|
||||
if d.HasFocus() && d.open {
|
||||
// We prefer to drop down but if there is no space, maybe drop up?
|
||||
lx := x
|
||||
ly := y + 1
|
||||
lwidth := maxLength
|
||||
lheight := len(d.options)
|
||||
_, sheight := screen.Size()
|
||||
if ly+lheight >= sheight && ly-lheight-1 >= 0 {
|
||||
ly = y - lheight
|
||||
}
|
||||
d.list.SetRect(lx, ly, lwidth, lheight)
|
||||
d.list.Draw(screen)
|
||||
}
|
||||
|
||||
// No cursor for this primitive.
|
||||
if d.focus.HasFocus() {
|
||||
screen.HideCursor()
|
||||
}
|
||||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
// Process key event.
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyEnter, tcell.KeyRune, tcell.KeyDown:
|
||||
if key == tcell.KeyRune && event.Rune() != ' ' {
|
||||
break
|
||||
}
|
||||
d.open = true
|
||||
d.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) {
|
||||
// An option was selected. Close the list again.
|
||||
d.open = false
|
||||
setFocus(d)
|
||||
d.currentOption = index
|
||||
|
||||
// Trigger "selected" event.
|
||||
if d.options[d.currentOption].Selected != nil {
|
||||
d.options[d.currentOption].Selected()
|
||||
}
|
||||
})
|
||||
setFocus(d.list)
|
||||
case tcell.KeyEscape, tcell.KeyTab, tcell.KeyBacktab:
|
||||
if d.done != nil {
|
||||
d.done(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Focus is called by the application when the primitive receives focus.
|
||||
func (d *DropDown) Focus(delegate func(p Primitive)) {
|
||||
d.Box.Focus(delegate)
|
||||
if d.open {
|
||||
delegate(d.list)
|
||||
}
|
||||
}
|
||||
|
||||
// HasFocus returns whether or not this primitive has focus.
|
||||
func (d *DropDown) HasFocus() bool {
|
||||
if d.open {
|
||||
return d.list.HasFocus()
|
||||
}
|
||||
return d.hasFocus
|
||||
}
|
6
flex.go
6
flex.go
|
@ -99,14 +99,14 @@ func (f *Flex) SetRect(x, y, width, height int) {
|
|||
}
|
||||
|
||||
// InputHandler returns nil.
|
||||
func (f *Flex) InputHandler() func(event *tcell.EventKey) {
|
||||
func (f *Flex) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Focus is called when this primitive receives focus.
|
||||
func (f *Flex) Focus(app *Application) {
|
||||
func (f *Flex) Focus(delegate func(p Primitive)) {
|
||||
if len(f.items) > 0 {
|
||||
app.SetFocus(f.items[0].Item)
|
||||
delegate(f.items[0].Item)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
88
form.go
88
form.go
|
@ -6,12 +6,33 @@ import (
|
|||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// FormItem is the interface all form items must implement to be able to be
|
||||
// included in a form.
|
||||
type FormItem interface {
|
||||
Primitive
|
||||
|
||||
// GetLabel returns the item's label text.
|
||||
GetLabel() string
|
||||
|
||||
// SetFormAttributes sets a number of item attributes at once.
|
||||
SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem
|
||||
|
||||
// SetEnteredFunc sets the handler function for when the user finished
|
||||
// entering data into the item. The handler may receive events for the
|
||||
// Enter key (we're done), the Escape key (cancel input), the Tab key (move to
|
||||
// next field), and the Backtab key (move to previous field).
|
||||
SetFinishedFunc(handler func(key tcell.Key)) FormItem
|
||||
|
||||
// GetFocusable returns the item's Focusable.
|
||||
GetFocusable() Focusable
|
||||
}
|
||||
|
||||
// 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
|
||||
items []FormItem
|
||||
|
||||
// The buttons of the form.
|
||||
buttons []*Button
|
||||
|
@ -74,11 +95,11 @@ func (f *Form) SetFieldTextColor(color tcell.Color) *Form {
|
|||
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
|
||||
// AddInputField adds an input field 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 {
|
||||
func (f *Form) AddInputField(label, value string, fieldLength int, accept func(textToCheck string, lastChar rune) bool) *Form {
|
||||
f.items = append(f.items, NewInputField().
|
||||
SetLabel(label).
|
||||
SetText(value).
|
||||
|
@ -87,6 +108,17 @@ func (f *Form) AddItem(label, value string, fieldLength int, accept func(textToC
|
|||
return f
|
||||
}
|
||||
|
||||
// AddDropDown adds a drop-down element to the form. It has a label, options,
|
||||
// and an (optional) callback function which is invoked when an option was
|
||||
// selected.
|
||||
func (f *Form) AddDropDown(label string, options []string, initialOption int, selected func(option string, optionIndex int)) *Form {
|
||||
f.items = append(f.items, NewDropDown().
|
||||
SetLabel(label).
|
||||
SetCurrentOption(initialOption).
|
||||
SetOptions(options, selected))
|
||||
return f
|
||||
}
|
||||
|
||||
// AddButton adds a new button to the form. The "selected" function is called
|
||||
// when the user selects this button. It may be nil.
|
||||
func (f *Form) AddButton(label string, selected func()) *Form {
|
||||
|
@ -113,8 +145,8 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||
|
||||
// Find the longest label.
|
||||
var labelLength int
|
||||
for _, inputField := range f.items {
|
||||
label := strings.TrimSpace(inputField.GetLabel())
|
||||
for _, item := range f.items {
|
||||
label := strings.TrimSpace(item.GetLabel())
|
||||
if len([]rune(label)) > labelLength {
|
||||
labelLength = len([]rune(label))
|
||||
}
|
||||
|
@ -122,18 +154,23 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||
labelLength++ // Add one space.
|
||||
|
||||
// Set up and draw the input fields.
|
||||
for _, inputField := range f.items {
|
||||
for _, item := range f.items {
|
||||
if y >= bottomLimit {
|
||||
return // Stop here.
|
||||
}
|
||||
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)
|
||||
label := strings.TrimSpace(item.GetLabel())
|
||||
item.SetFormAttributes(
|
||||
label+strings.Repeat(" ", labelLength-len([]rune(label))),
|
||||
f.labelColor,
|
||||
f.backgroundColor,
|
||||
f.fieldTextColor,
|
||||
f.fieldBackgroundColor,
|
||||
).SetRect(x, y, width, 1)
|
||||
if item.GetFocusable().HasFocus() {
|
||||
defer item.Draw(screen)
|
||||
} else {
|
||||
item.Draw(screen)
|
||||
}
|
||||
y += 1 + f.itemPadding
|
||||
}
|
||||
|
||||
|
@ -161,7 +198,7 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// Focus is called by the application when the primitive receives focus.
|
||||
func (f *Form) Focus(app *Application) {
|
||||
func (f *Form) Focus(delegate func(p Primitive)) {
|
||||
if len(f.items)+len(f.buttons) == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -182,30 +219,31 @@ func (f *Form) Focus(app *Application) {
|
|||
case tcell.KeyEscape:
|
||||
f.focusedElement = 0
|
||||
}
|
||||
f.Focus(app)
|
||||
f.Focus(delegate)
|
||||
}
|
||||
|
||||
if f.focusedElement < len(f.items) {
|
||||
// We're selecting an item.
|
||||
inputField := f.items[f.focusedElement]
|
||||
inputField.SetDoneFunc(handler)
|
||||
app.SetFocus(inputField)
|
||||
item := f.items[f.focusedElement]
|
||||
item.SetFinishedFunc(handler)
|
||||
delegate(item)
|
||||
} else {
|
||||
// We're selecting a button.
|
||||
button := f.buttons[f.focusedElement-len(f.items)]
|
||||
button.SetBlurFunc(handler)
|
||||
app.SetFocus(button)
|
||||
delegate(button)
|
||||
}
|
||||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (f *Form) InputHandler() func(event *tcell.EventKey) {
|
||||
return func(event *tcell.EventKey) {}
|
||||
func (f *Form) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return func(event *tcell.EventKey, setFocus func(p Primitive)) {}
|
||||
}
|
||||
|
||||
// HasFocus returns whether or not this primitive has focus.
|
||||
func (f *Form) HasFocus() bool {
|
||||
for _, item := range f.items {
|
||||
if item.focus.HasFocus() {
|
||||
if item.GetFocusable().HasFocus() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
9
frame.go
9
frame.go
|
@ -150,14 +150,13 @@ func (f *Frame) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// Focus is called when this primitive receives focus.
|
||||
func (f *Frame) Focus(app *Application) {
|
||||
app.SetFocus(f.primitive)
|
||||
func (f *Frame) Focus(delegate func(p Primitive)) {
|
||||
delegate(f.primitive)
|
||||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (f *Frame) InputHandler() func(event *tcell.EventKey) {
|
||||
return func(event *tcell.EventKey) {
|
||||
}
|
||||
func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return func(event *tcell.EventKey, setFocus func(p Primitive)) {}
|
||||
}
|
||||
|
||||
// HasFocus returns whether or not this primitive has focus.
|
||||
|
|
|
@ -132,6 +132,16 @@ func (i *InputField) SetFieldTextColor(color tcell.Color) *InputField {
|
|||
return i
|
||||
}
|
||||
|
||||
// SetFormAttributes sets attributes shared by all form items.
|
||||
func (i *InputField) SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
|
||||
i.label = label
|
||||
i.labelColor = labelColor
|
||||
i.backgroundColor = bgColor
|
||||
i.fieldTextColor = fieldTextColor
|
||||
i.fieldBackgroundColor = fieldBgColor
|
||||
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 {
|
||||
|
@ -162,6 +172,16 @@ func (i *InputField) SetDoneFunc(handler func(key tcell.Key)) *InputField {
|
|||
return i
|
||||
}
|
||||
|
||||
// SetFinishedFunc calls SetDoneFunc().
|
||||
func (i *InputField) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
|
||||
return i.SetDoneFunc(handler)
|
||||
}
|
||||
|
||||
// GetFocusable returns the item's Focusable.
|
||||
func (i *InputField) GetFocusable() Focusable {
|
||||
return i.focus
|
||||
}
|
||||
|
||||
// Draw draws this primitive onto the screen.
|
||||
func (i *InputField) Draw(screen tcell.Screen) {
|
||||
i.Box.Draw(screen)
|
||||
|
@ -233,8 +253,8 @@ func (i *InputField) setCursor(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (i *InputField) InputHandler() func(event *tcell.EventKey) {
|
||||
return func(event *tcell.EventKey) {
|
||||
func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
// Process key event.
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyRune: // Regular character.
|
||||
|
|
83
list.go
83
list.go
|
@ -11,6 +11,7 @@ type listItem struct {
|
|||
MainText string // The main text of the list item.
|
||||
SecondaryText string // A secondary text to be shown underneath the main text.
|
||||
Shortcut rune // The key to select the list item directly, 0 if there is no shortcut.
|
||||
Selected func() // The optional function which is called when the item is selected.
|
||||
}
|
||||
|
||||
// List displays rows of items, each of which can be selected.
|
||||
|
@ -26,8 +27,7 @@ type List struct {
|
|||
// Whether or not to show the secondary item texts.
|
||||
showSecondaryText bool
|
||||
|
||||
// The item main text color. Selected items have their background and text
|
||||
// color switched.
|
||||
// The item main text color.
|
||||
mainTextColor tcell.Color
|
||||
|
||||
// The item secondary text color.
|
||||
|
@ -36,29 +36,44 @@ type List struct {
|
|||
// The item shortcut text color.
|
||||
shortcutColor tcell.Color
|
||||
|
||||
// An optional function which is called when a list item was selected.
|
||||
// The text color for selected items.
|
||||
selectedTextColor tcell.Color
|
||||
|
||||
// The background color for selected items.
|
||||
selectedBackgroundColor tcell.Color
|
||||
|
||||
// An optional function which is called when a list item was selected. This
|
||||
// function will be called even if the list item defines its own callback.
|
||||
selected func(index int, mainText, secondaryText string, shortcut rune)
|
||||
}
|
||||
|
||||
// NewList returns a new form.
|
||||
func NewList() *List {
|
||||
return &List{
|
||||
Box: NewBox(),
|
||||
showSecondaryText: true,
|
||||
mainTextColor: tcell.ColorWhite,
|
||||
secondaryTextColor: tcell.ColorGreen,
|
||||
shortcutColor: tcell.ColorYellow,
|
||||
Box: NewBox(),
|
||||
showSecondaryText: true,
|
||||
mainTextColor: tcell.ColorWhite,
|
||||
secondaryTextColor: tcell.ColorGreen,
|
||||
shortcutColor: tcell.ColorYellow,
|
||||
selectedTextColor: tcell.ColorBlack,
|
||||
selectedBackgroundColor: tcell.ColorWhite,
|
||||
}
|
||||
}
|
||||
|
||||
// SetMainTextColorColor sets the color of the items' main text.
|
||||
func (l *List) SetMainTextColorColor(color tcell.Color) *List {
|
||||
// SetCurrentItem sets the currently selected item by its index.
|
||||
func (l *List) SetCurrentItem(index int) *List {
|
||||
l.currentItem = index
|
||||
return l
|
||||
}
|
||||
|
||||
// SetMainTextColor sets the color of the items' main text.
|
||||
func (l *List) SetMainTextColor(color tcell.Color) *List {
|
||||
l.mainTextColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetSecondaryTextColorColor sets the color of the items' secondary text.
|
||||
func (l *List) SetSecondaryTextColorColor(color tcell.Color) *List {
|
||||
// SetSecondaryTextColor sets the color of the items' secondary text.
|
||||
func (l *List) SetSecondaryTextColor(color tcell.Color) *List {
|
||||
l.secondaryTextColor = color
|
||||
return l
|
||||
}
|
||||
|
@ -69,6 +84,18 @@ func (l *List) SetShortcutColor(color tcell.Color) *List {
|
|||
return l
|
||||
}
|
||||
|
||||
// SetSelectedTextColor sets the text color of selected items.
|
||||
func (l *List) SetSelectedTextColor(color tcell.Color) *List {
|
||||
l.selectedTextColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetSelectedBackgroundColor sets the background color of selected items.
|
||||
func (l *List) SetSelectedBackgroundColor(color tcell.Color) *List {
|
||||
l.selectedBackgroundColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// ShowSecondaryText determines whether or not to show secondary item texts.
|
||||
func (l *List) ShowSecondaryText(show bool) *List {
|
||||
l.showSecondaryText = show
|
||||
|
@ -90,15 +117,27 @@ func (l *List) SetSelectedFunc(handler func(int, string, string, rune)) *List {
|
|||
//
|
||||
// The shortcut is a key binding. If the specified rune is entered, the item
|
||||
// is selected immediately. Set to 0 for no binding.
|
||||
func (l *List) AddItem(mainText, secondaryText string, shortcut rune) *List {
|
||||
//
|
||||
// The "selected" callback will be invoked when the user selects the item. You
|
||||
// may provide nil if no such item is needed or if all events are handled
|
||||
// through the selected callback set with SetSelectedFunc().
|
||||
func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected func()) *List {
|
||||
l.items = append(l.items, &listItem{
|
||||
MainText: mainText,
|
||||
SecondaryText: secondaryText,
|
||||
Shortcut: shortcut,
|
||||
Selected: selected,
|
||||
})
|
||||
return l
|
||||
}
|
||||
|
||||
// ClearItems removes all items from the list.
|
||||
func (l *List) ClearItems() *List {
|
||||
l.items = nil
|
||||
l.currentItem = 0
|
||||
return l
|
||||
}
|
||||
|
||||
// Draw draws this primitive onto the screen.
|
||||
func (l *List) Draw(screen tcell.Screen) {
|
||||
l.Box.Draw(screen)
|
||||
|
@ -141,11 +180,11 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
color := l.mainTextColor
|
||||
if l.focus.HasFocus() && index == l.currentItem {
|
||||
textLength := len([]rune(item.MainText))
|
||||
style := tcell.StyleDefault.Background(l.mainTextColor)
|
||||
style := tcell.StyleDefault.Background(l.selectedBackgroundColor)
|
||||
for bx := 0; bx < textLength && bx < width; bx++ {
|
||||
screen.SetContent(x+bx, y, ' ', nil, style)
|
||||
}
|
||||
color = l.backgroundColor
|
||||
color = l.selectedTextColor
|
||||
}
|
||||
Print(screen, item.MainText, x, y, width, AlignLeft, color)
|
||||
y++
|
||||
|
@ -163,8 +202,8 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (l *List) InputHandler() func(event *tcell.EventKey) {
|
||||
return func(event *tcell.EventKey) {
|
||||
func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyTab, tcell.KeyDown, tcell.KeyRight:
|
||||
l.currentItem++
|
||||
|
@ -179,8 +218,11 @@ func (l *List) InputHandler() func(event *tcell.EventKey) {
|
|||
case tcell.KeyPgUp:
|
||||
l.currentItem -= 5
|
||||
case tcell.KeyEnter:
|
||||
item := l.items[l.currentItem]
|
||||
if item.Selected != nil {
|
||||
item.Selected()
|
||||
}
|
||||
if l.selected != nil {
|
||||
item := l.items[l.currentItem]
|
||||
l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
case tcell.KeyRune:
|
||||
|
@ -200,8 +242,11 @@ func (l *List) InputHandler() func(event *tcell.EventKey) {
|
|||
break
|
||||
}
|
||||
}
|
||||
item := l.items[l.currentItem]
|
||||
if item.Selected != nil {
|
||||
item.Selected()
|
||||
}
|
||||
if l.selected != nil {
|
||||
item := l.items[l.currentItem]
|
||||
l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,12 +22,17 @@ type Primitive interface {
|
|||
// A value of nil may also be returned, in which case this primitive cannot
|
||||
// receive focus and will not process any key events.
|
||||
//
|
||||
// The handler will receive the key event and a function that allows it to
|
||||
// set the focus to a different primitive, so that future key events are sent
|
||||
// to that primitive.
|
||||
//
|
||||
// The Application's Draw() function will be called automatically after the
|
||||
// handler returns.
|
||||
InputHandler() func(event *tcell.EventKey)
|
||||
InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive))
|
||||
|
||||
// Focus is called by the application when the primitive receives focus.
|
||||
Focus(app *Application)
|
||||
// Implementers may call delegate() to pass the focus on to another primitive.
|
||||
Focus(delegate func(p Primitive))
|
||||
|
||||
// Blur is called by the application when the primitive loses focus.
|
||||
Blur()
|
||||
|
|
Loading…
Reference in New Issue