feat(dropdown): Expose `DropDownOption`

BREAKING CHANGE:
DropDown:
- The signature of `GetCurrentOption` has been changed to return the index and the newly exposed `DropDownOption`
- `AddOption` has been renamed to `AddOptions` and accepts now multiple `DropDownOption`s. A convenience func called `AddOptionsSimple` has been added to simply add options by only providing its text.
- The signature of `SetOptions` has been changed to accept the global `selected` handler with the newly exposed `DropDownOption` and multiple `DropDownOption`s to set. A convenience func, namely `SetOptionsSimple`, has been added to simply add options by only providing its text.
- The signature of `SetSelectedFunc` has been changed to accept a handler expecting an index and the newly exposed `DropDownOption`

Form:
- The signature of `AddDropDown` has been changed to accept an array of the newly exposed `DropDownOption`. A convenience func, namely `AddDropDownSimple`, has been added to simply add a DropDown by only providing the option's text.
This commit is contained in:
Andreas Bieber 2020-09-17 11:42:30 +02:00
parent 75b6d31409
commit f71273b9c8
6 changed files with 115 additions and 55 deletions

View File

@ -7,7 +7,12 @@ func main() {
app := cview.NewApplication()
dropdown := cview.NewDropDown().
SetLabel("Select an option (hit Enter): ").
SetOptions([]string{"First", "Second", "Third", "Fourth", "Fifth"}, nil)
SetOptions(nil,
cview.NewDropDownOption("First"),
cview.NewDropDownOption("Second"),
cview.NewDropDownOption("Third"),
cview.NewDropDownOption("Fourth"),
cview.NewDropDownOption("Fifth"))
if err := app.SetRoot(dropdown, true).EnableMouse(true).Run(); err != nil {
panic(err)
}

View File

@ -8,7 +8,7 @@ import (
func main() {
app := cview.NewApplication()
form := cview.NewForm().
AddDropDown("Title", []string{"Mr.", "Ms.", "Mrs.", "Dr.", "Prof."}, 0, nil).
AddDropDownSimple("Title", 0, nil, "Mr.", "Ms.", "Mrs.", "Dr.", "Prof.").
AddInputField("First name", "", 20, nil, nil).
AddInputField("Last name", "", 20, nil, nil).
AddPasswordField("Password", "", 10, '*', nil).

View File

@ -33,7 +33,7 @@ func Form(nextSlide func()) (title string, content cview.Primitive) {
f := cview.NewForm().
AddInputField("First name:", "", 20, nil, nil).
AddInputField("Last name:", "", 20, nil, nil).
AddDropDown("Role:", []string{"Engineer", "Manager", "Administration"}, 0, nil).
AddDropDownSimple("Role:", 0, nil, "Engineer", "Manager", "Administration").
AddPasswordField("Password:", "", 10, '*', nil).
AddCheckBox("", "On vacation", false, nil).
AddButton("Save", nextSlide).

View File

@ -12,15 +12,15 @@ func main() {
pages := cview.NewPages()
form := cview.NewForm()
form.AddDropDown("称谓", []string{"先生", "女士", "博士", "老师", "师傅"}, 0, nil).
form.AddDropDownSimple("称谓", 0, nil, "先生", "女士", "博士", "老师", "师傅").
AddInputField("姓名", "", 20, nil, nil).
AddPasswordField("密码", "", 10, '*', nil).
AddCheckBox("", "年龄 18+", false, nil).
AddButton("保存", func() {
_, title := form.GetFormItem(0).(*cview.DropDown).GetCurrentOption()
_, option := form.GetFormItem(0).(*cview.DropDown).GetCurrentOption()
userName := form.GetFormItem(1).(*cview.InputField).GetText()
alert(pages, "alert-dialog", fmt.Sprintf("保存成功,%s %s", userName, title))
alert(pages, "alert-dialog", fmt.Sprintf("保存成功,%s %s", userName, option.GetText()))
}).
AddButton("退出", func() {
app.Stop()

View File

@ -7,10 +7,31 @@ import (
"github.com/gdamore/tcell/v2"
)
// 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.
// 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(index int, option *DropDownOption) // The (optional) callback for when this option was selected.
}
func NewDropDownOption(text string) *DropDownOption {
return &DropDownOption{text: text}
}
// GetText returns the text of this dropdown option.
func (d *DropDownOption) GetText() string {
return d.text
}
// SetText returns the text of this dropdown option.
func (d *DropDownOption) SetText(text string) *DropDownOption {
d.text = text
return d
}
// SetSelectedFunc sets the handler to be called when this option is selected.
func (d *DropDownOption) SetSelectedFunc(handler func(index int, option *DropDownOption)) *DropDownOption {
d.selected = handler
return d
}
// DropDown implements a selection widget whose options become visible in a
@ -19,7 +40,7 @@ type DropDown struct {
*Box
// The options from which the user can choose.
options []*dropDownOption
options []*DropDownOption
// Strings to be placed before and after each drop-down option.
optionPrefix, optionSuffix string
@ -86,7 +107,7 @@ type DropDown struct {
// A callback function which is called when the user changes the drop-down's
// selection.
selected func(text string, index int)
selected func(index int, option *DropDownOption)
// Set to true when mouse dragging is in progress.
dragging bool
@ -134,12 +155,12 @@ func (d *DropDown) SetCurrentOption(index int) *DropDown {
d.list.SetCurrentItem(index)
if d.selected != nil {
d.Unlock()
d.selected(d.options[index].Text, index)
d.selected(index, d.options[index])
d.Lock()
}
if d.options[index].Selected != nil {
if d.options[index].selected != nil {
d.Unlock()
d.options[index].Selected()
d.options[index].selected(index, d.options[index])
d.Lock()
}
} else {
@ -147,7 +168,7 @@ func (d *DropDown) SetCurrentOption(index int) *DropDown {
d.list.SetCurrentItem(0) // Set to 0 because -1 means "last item".
if d.selected != nil {
d.Unlock()
d.selected("", -1)
d.selected(-1, nil)
d.Lock()
}
}
@ -155,16 +176,16 @@ func (d *DropDown) SetCurrentOption(index int) *DropDown {
}
// 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) {
// as the option itself. If no option was selected, -1 and nil is returned.
func (d *DropDown) GetCurrentOption() (int, *DropDownOption) {
d.RLock()
defer d.RUnlock()
var text string
var option *DropDownOption
if d.currentOption >= 0 && d.currentOption < len(d.options) {
text = d.options[d.currentOption].Text
option = d.options[d.currentOption]
}
return d.currentOption, text
return d.currentOption, option
}
// SetTextOptions sets the text to be placed before and after each drop-down
@ -182,7 +203,7 @@ func (d *DropDown) SetTextOptions(prefix, suffix, currentPrefix, currentSuffix,
d.optionPrefix = prefix
d.optionSuffix = suffix
for index := 0; index < d.list.GetItemCount(); index++ {
d.list.SetItemText(index, prefix+d.options[index].Text+suffix, "")
d.list.SetItemText(index, prefix+d.options[index].text+suffix, "")
}
return d
}
@ -351,7 +372,7 @@ func (d *DropDown) GetFieldWidth() int {
}
fieldWidth := 0
for _, option := range d.options {
width := TaggedStringWidth(option.Text)
width := TaggedStringWidth(option.text)
if width > fieldWidth {
fieldWidth = width
}
@ -359,36 +380,55 @@ func (d *DropDown) GetFieldWidth() int {
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.Lock()
defer d.Unlock()
return d.addOption(text, selected)
// AddOptions adds new selectable options to this drop-down.
func (d *DropDown) AddOptionsSimple(options ...string) *DropDown {
optionsToAdd := make([]*DropDownOption, len(options))
for i, option := range options {
optionsToAdd[i] = NewDropDownOption(option)
}
d.AddOptions(optionsToAdd...)
return d
}
func (d *DropDown) addOption(text string, selected func()) *DropDown {
d.options = append(d.options, &dropDownOption{Text: text, Selected: selected})
d.list.AddItem(NewListItem(d.optionPrefix + text + d.optionSuffix))
// AddOptions adds new selectable options to this drop-down.
func (d *DropDown) AddOptions(options ...*DropDownOption) *DropDown {
d.Lock()
defer d.Unlock()
return d.addOptions(options...)
}
func (d *DropDown) addOptions(options ...*DropDownOption) *DropDown {
d.options = append(d.options, options...)
for _, option := range options {
d.list.AddItem(NewListItem(d.optionPrefix + option.text + d.optionSuffix))
}
return d
}
// SetOptionsSimple 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 index and the option itself
// The "selected" parameter may be nil.
func (d *DropDown) SetOptionsSimple(selected func(index int, option *DropDownOption), options ...string) *DropDown {
optionsToSet := make([]*DropDownOption, len(options))
for i, option := range options {
optionsToSet[i] = NewDropDownOption(option)
}
d.SetOptions(selected, optionsToSet...)
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 {
// It will be called with the option's index and the option itself.
// The "selected" parameter may be nil.
func (d *DropDown) SetOptions(selected func(index int, option *DropDownOption), options ...*DropDownOption) *DropDown {
d.Lock()
defer d.Unlock()
d.list.Clear()
d.options = nil
for index, text := range texts {
func(t string, i int) {
d.addOption(text, nil)
}(text, index)
}
d.addOptions(options...)
d.selected = selected
return d
}
@ -396,9 +436,9 @@ func (d *DropDown) SetOptions(texts []string, selected func(text string, index i
// SetSelectedFunc sets a handler which is called when the user changes the
// drop-down's option. This handler will be called in addition and prior to
// an option's optional individual handler. The handler is provided with the
// selected option's text and index. If "no option" was selected, these values
// are an empty string and -1.
func (d *DropDown) SetSelectedFunc(handler func(text string, index int)) *DropDown {
// selected option's index and the option itself. If "no option" was selected, these values
// are -1 and nil.
func (d *DropDown) SetSelectedFunc(handler func(index int, option *DropDownOption)) *DropDown {
d.Lock()
defer d.Unlock()
@ -472,7 +512,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
maxWidth := 0
optionWrapWidth := TaggedStringWidth(d.optionPrefix + d.optionSuffix)
for _, option := range d.options {
strWidth := TaggedStringWidth(option.Text) + optionWrapWidth
strWidth := TaggedStringWidth(option.text) + optionWrapWidth
if strWidth > maxWidth {
maxWidth = strWidth
}
@ -488,7 +528,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
fieldWidth = noSelectionWidth
}
} else if d.currentOption < len(d.options) {
currentOptionWidth := TaggedStringWidth(d.currentOptionPrefix + d.options[d.currentOption].Text + d.currentOptionSuffix)
currentOptionWidth := TaggedStringWidth(d.currentOptionPrefix + d.options[d.currentOption].text + d.currentOptionSuffix)
if currentOptionWidth > fieldWidth {
fieldWidth = currentOptionWidth
}
@ -507,7 +547,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
// Show the prefix.
currentOptionPrefixWidth := TaggedStringWidth(d.currentOptionPrefix)
prefixWidth := stringWidth(d.prefix)
listItemText := d.options[d.list.GetCurrentItemIndex()].Text
listItemText := d.options[d.list.GetCurrentItemIndex()].text
Print(screen, d.currentOptionPrefix, x, y, fieldWidth, AlignLeft, fieldTextColor)
Print(screen, d.prefix, x+currentOptionPrefixWidth, y, fieldWidth-currentOptionPrefixWidth, AlignLeft, d.prefixTextColor)
if len(d.prefix) < len(listItemText) {
@ -517,7 +557,7 @@ func (d *DropDown) Draw(screen tcell.Screen) {
color := fieldTextColor
text := d.noSelection
if d.currentOption >= 0 && d.currentOption < len(d.options) {
text = d.currentOptionPrefix + d.options[d.currentOption].Text + d.currentOptionSuffix
text = d.currentOptionPrefix + d.options[d.currentOption].text + d.currentOptionSuffix
}
// Just show the current selection.
Print(screen, text, x, y, fieldWidth, AlignLeft, color)
@ -581,7 +621,7 @@ func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
func (d *DropDown) evalPrefix() {
if len(d.prefix) > 0 {
for index, option := range d.options {
if strings.HasPrefix(strings.ToLower(option.Text), d.prefix) {
if strings.HasPrefix(strings.ToLower(option.text), d.prefix) {
d.list.SetCurrentItem(index)
return
}
@ -609,10 +649,10 @@ func (d *DropDown) openList(setFocus func(Primitive)) {
// Trigger "selected" event.
if d.selected != nil {
d.selected(d.options[d.currentOption].Text, d.currentOption)
d.selected(d.currentOption, d.options[d.currentOption])
}
if d.options[d.currentOption].Selected != nil {
d.options[d.currentOption].Selected()
if d.options[d.currentOption].selected != nil {
d.options[d.currentOption].selected(d.currentOption, d.options[d.currentOption])
}
}).SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyRune {

21
form.go
View File

@ -305,17 +305,32 @@ func (f *Form) AddPasswordField(label, value string, fieldWidth int, mask rune,
return f
}
// AddDropDown adds a drop-down element to the form. It has a label, options,
// AddDropDownSimple 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. The initial option may be a negative value to indicate that no
// option is currently selected.
func (f *Form) AddDropDown(label string, options []string, initialOption int, selected func(option string, optionIndex int)) *Form {
func (f *Form) AddDropDownSimple(label string, initialOption int, selected func(index int, option *DropDownOption), options ...string) *Form {
f.Lock()
defer f.Unlock()
f.items = append(f.items, NewDropDown().
SetLabel(label).
SetOptions(options, selected).
SetOptionsSimple(selected, options...).
SetCurrentOption(initialOption))
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. The initial option may be a negative value to indicate that no
// option is currently selected.
func (f *Form) AddDropDown(label string, initialOption int, selected func(index int, option *DropDownOption), options []*DropDownOption) *Form {
f.Lock()
defer f.Unlock()
f.items = append(f.items, NewDropDown().
SetLabel(label).
SetOptions(selected, options...).
SetCurrentOption(initialOption))
return f
}