aboutsummaryrefslogtreecommitdiff
path: root/vendor/maunium.net/go/tview/dropdown.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/maunium.net/go/tview/dropdown.go')
-rw-r--r--vendor/maunium.net/go/tview/dropdown.go380
1 files changed, 380 insertions, 0 deletions
diff --git a/vendor/maunium.net/go/tview/dropdown.go b/vendor/maunium.net/go/tview/dropdown.go
new file mode 100644
index 0000000..981d1bd
--- /dev/null
+++ b/vendor/maunium.net/go/tview/dropdown.go
@@ -0,0 +1,380 @@
+package tview
+
+import (
+ "strings"
+
+ "maunium.net/go/tcell"
+ runewidth "github.com/mattn/go-runewidth"
+)
+
+// 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 implements a selection widget whose options become visible in a
+// drop-down list when activated.
+//
+// See https://github.com/rivo/tview/wiki/DropDown for an example.
+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 runes typed so far to directly access one of the list items.
+ prefix string
+
+ // 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 color for prefixes.
+ prefixTextColor tcell.Color
+
+ // The screen width of the input area. A value of 0 means extend as much as
+ // possible.
+ fieldWidth 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(Styles.PrimitiveBackgroundColor).
+ SetSelectedTextColor(Styles.PrimitiveBackgroundColor).
+ SetSelectedBackgroundColor(Styles.PrimaryTextColor).
+ SetBackgroundColor(Styles.MoreContrastBackgroundColor)
+
+ d := &DropDown{
+ Box: NewBox(),
+ currentOption: -1,
+ list: list,
+ labelColor: Styles.SecondaryTextColor,
+ fieldBackgroundColor: Styles.ContrastBackgroundColor,
+ fieldTextColor: Styles.PrimaryTextColor,
+ prefixTextColor: Styles.ContrastSecondaryTextColor,
+ }
+
+ d.focus = d
+
+ return d
+}
+
+// SetCurrentOption sets the index of the currently selected option. This may
+// be a negative value to indicate that no option is currently selected.
+func (d *DropDown) SetCurrentOption(index int) *DropDown {
+ d.currentOption = index
+ d.list.SetCurrentItem(index)
+ return d
+}
+
+// GetCurrentOption returns the index of the currently selected option as well
+// as its text. If no option was selected, -1 and an empty string is returned.
+func (d *DropDown) GetCurrentOption() (int, string) {
+ var text string
+ if d.currentOption >= 0 && d.currentOption < len(d.options) {
+ text = d.options[d.currentOption].Text
+ }
+ return d.currentOption, text
+}
+
+// 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
+}
+
+// SetPrefixTextColor sets the color of the prefix string. The prefix string is
+// shown when the user starts typing text, which directly selects the first
+// option that starts with the typed string.
+func (d *DropDown) SetPrefixTextColor(color tcell.Color) *DropDown {
+ d.prefixTextColor = 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
+}
+
+// SetFieldWidth sets the screen width of the options area. A value of 0 means
+// extend to as long as the longest option text.
+func (d *DropDown) SetFieldWidth(width int) *DropDown {
+ d.fieldWidth = width
+ return d
+}
+
+// GetFieldWidth returns this primitive's field screen width.
+func (d *DropDown) GetFieldWidth() int {
+ if d.fieldWidth > 0 {
+ return d.fieldWidth
+ }
+ fieldWidth := 0
+ for _, option := range d.options {
+ width := StringWidth(option.Text)
+ if width > fieldWidth {
+ fieldWidth = width
+ }
+ }
+ return fieldWidth
+}
+
+// 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.Clear()
+ 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)
+}
+
+// Draw draws this primitive onto the screen.
+func (d *DropDown) Draw(screen tcell.Screen) {
+ d.Box.Draw(screen)
+
+ // Prepare.
+ x, y, width, height := d.GetInnerRect()
+ rightLimit := x + width
+ if height < 1 || rightLimit <= x {
+ return
+ }
+
+ // Draw label.
+ _, drawnWidth := Print(screen, d.label, x, y, rightLimit-x, AlignLeft, d.labelColor)
+ x += drawnWidth
+
+ // What's the longest option text?
+ maxWidth := 0
+ for _, option := range d.options {
+ strWidth := StringWidth(option.Text)
+ if strWidth > maxWidth {
+ maxWidth = strWidth
+ }
+ }
+
+ // Draw selection area.
+ fieldWidth := d.fieldWidth
+ if fieldWidth == 0 {
+ fieldWidth = maxWidth
+ }
+ if rightLimit-x < fieldWidth {
+ fieldWidth = rightLimit - x
+ }
+ fieldStyle := tcell.StyleDefault.Background(d.fieldBackgroundColor)
+ if d.GetFocusable().HasFocus() && !d.open {
+ fieldStyle = fieldStyle.Background(d.fieldTextColor)
+ }
+ for index := 0; index < fieldWidth; index++ {
+ screen.SetContent(x+index, y, ' ', nil, fieldStyle)
+ }
+
+ // Draw selected text.
+ if d.open && len(d.prefix) > 0 {
+ // Show the prefix.
+ Print(screen, d.prefix, x, y, fieldWidth, AlignLeft, d.prefixTextColor)
+ prefixWidth := runewidth.StringWidth(d.prefix)
+ listItemText := d.options[d.list.GetCurrentItem()].Text
+ if prefixWidth < fieldWidth && len(d.prefix) < len(listItemText) {
+ Print(screen, listItemText[len(d.prefix):], x+prefixWidth, y, fieldWidth-prefixWidth, AlignLeft, d.fieldTextColor)
+ }
+ } else {
+ if d.currentOption >= 0 && d.currentOption < len(d.options) {
+ color := d.fieldTextColor
+ // Just show the current selection.
+ if d.GetFocusable().HasFocus() && !d.open {
+ color = d.fieldBackgroundColor
+ }
+ Print(screen, d.options[d.currentOption].Text, x, y, fieldWidth, 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 := maxWidth
+ lheight := len(d.options)
+ _, sheight := screen.Size()
+ if ly+lheight >= sheight && ly-2 > lheight-ly {
+ ly = y - lheight
+ if ly < 0 {
+ ly = 0
+ }
+ }
+ if ly+lheight >= sheight {
+ lheight = sheight - ly
+ }
+ d.list.SetRect(lx, ly, lwidth, lheight)
+ d.list.Draw(screen)
+ }
+}
+
+// InputHandler returns the handler for this primitive.
+func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
+ return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
+ // A helper function which selects an item in the drop-down list based on
+ // the current prefix.
+ evalPrefix := func() {
+ if len(d.prefix) > 0 {
+ for index, option := range d.options {
+ if strings.HasPrefix(strings.ToLower(option.Text), d.prefix) {
+ d.list.SetCurrentItem(index)
+ return
+ }
+ }
+ // Prefix does not match any item. Remove last rune.
+ r := []rune(d.prefix)
+ d.prefix = string(r[:len(r)-1])
+ }
+ }
+
+ // Process key event.
+ switch key := event.Key(); key {
+ case tcell.KeyEnter, tcell.KeyRune, tcell.KeyDown:
+ d.prefix = ""
+
+ // If the first key was a letter already, it becomes part of the prefix.
+ if r := event.Rune(); key == tcell.KeyRune && r != ' ' {
+ d.prefix += string(r)
+ evalPrefix()
+ }
+
+ // Hand control over to the list.
+ 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()
+ }
+ }).SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
+ if event.Key() == tcell.KeyRune {
+ d.prefix += string(event.Rune())
+ evalPrefix()
+ } else if event.Key() == tcell.KeyBackspace || event.Key() == tcell.KeyBackspace2 {
+ if len(d.prefix) > 0 {
+ r := []rune(d.prefix)
+ d.prefix = string(r[:len(r)-1])
+ }
+ evalPrefix()
+ } else {
+ d.prefix = ""
+ }
+ return event
+ })
+ 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
+}