Added DropDown. Also needed to refactor a bit to reduce dependencies.

This commit is contained in:
Oliver 2017-12-18 20:04:52 +01:00
parent d5bf1a4ef0
commit 2bd80aa513
11 changed files with 495 additions and 77 deletions

View File

@ -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
View File

@ -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
}

View File

@ -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.

View File

@ -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{

298
dropdown.go Normal file
View File

@ -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
}

View File

@ -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
View File

@ -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
}
}

View File

@ -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.

View File

@ -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
View File

@ -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)
}
}

View File

@ -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()