diff --git a/dropdown.go b/dropdown.go new file mode 100644 index 0000000..ba422fa --- /dev/null +++ b/dropdown.go @@ -0,0 +1,115 @@ +package objects + +// import "image" +import "git.tebibyte.media/tomo/tomo" +import "git.tebibyte.media/tomo/tomo/input" +import "git.tebibyte.media/tomo/tomo/event" +import "git.tebibyte.media/tomo/objects/layouts" + +// Dropdown is a non-editable text input that allows the user to pick a value +// from a list. +type Dropdown struct { + tomo.ContainerBox + label *Label + + value string + items []string + menu *Menu + + on struct { + valueChange event.FuncBroadcaster + } +} + +// NewDropdown creates a new dropdown input with the specified items +func NewDropdown (items ...string) *Dropdown { + dropdown := &Dropdown { + ContainerBox: tomo.NewContainerBox(), + label: NewLabel(""), + } + dropdown.SetRole(tomo.R("objects", "Dropdown")) + dropdown.SetAttr(tomo.ALayout(layouts.Row { true, false })) + dropdown.Add(dropdown.label) + // TODO: replace IconValueDecrement with a drop-down expand icon once + // we get that + dropdown.Add(NewIcon(tomo.IconValueDecrement, tomo.IconSizeSmall)) + + dropdown.SetItems(items...) + if len(items) > 0 { + dropdown.SetValue(items[0]) + } + + dropdown.SetInputMask(true) + dropdown.OnButtonDown(dropdown.handleButtonDown) + dropdown.OnButtonUp(dropdown.handleButtonUp) + dropdown.OnKeyDown(dropdown.handleKeyDown) + dropdown.OnKeyUp(dropdown.handleKeyUp) + dropdown.SetFocusable(true) + return dropdown +} + +// Value returns the value of the dropdown. This does not necissarily have to be +// in the list of items. +func (this *Dropdown) Value () string { + return this.value +} + +// SetValue sets the value of the dropdown. This does not necissarily have to be +// in the list of items. +func (this *Dropdown) SetValue (value string) { + this.value = value + this.label.SetText(value) +} + +// SetItems sets the items from which the user is able to pick. +func (this *Dropdown) SetItems (items ...string) { + this.items = items +} + +func (this *Dropdown) itemList () []tomo.Object { + items := make([]tomo.Object, len(this.items)) + for index, value := range this.items { + value := value + item := NewMenuItem(value) + item.OnClick(func () { + this.SetValue(value) + this.on.valueChange.Broadcast() + }) + items[index] = item + } + return items +} + +func (this *Dropdown) showMenu () { + if this.menu != nil { + this.menu.Close() + } + menu, err := NewAnchoredMenu(this, this.itemList()...) + if err != nil { return } + this.menu = menu + menu.SetVisible(true) +} + +func (this *Dropdown) handleKeyDown (key input.Key, numberPad bool) bool { + if key != input.KeyEnter && key != input.Key(' ') { return false } + return true +} + +func (this *Dropdown) handleKeyUp (key input.Key, numberPad bool) bool { + if key != input.KeyEnter && key != input.Key(' ') { return false } + this.showMenu() + return true +} + +func (this *Dropdown) handleButtonDown (button input.Button) bool { + if button != input.ButtonLeft { return false } + return true +} + +func (this *Dropdown) handleButtonUp (button input.Button) bool { + if button != input.ButtonLeft { return false } + if this.Window().MousePosition().In(this.Bounds()) { + this.showMenu() + } + return true +}