// // Copyright (c) 2016, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 17 Feb 2016 Andy Frank Creation // using dom using graphics ** ** ListButton allows user selection of an item in a list by ** showing a listbox popup on button press. ** ** See also: [docDomkit]`docDomkit::Controls#listButton`, ** `Button`, `ToggleButton` ** @Js class ListButton : Button { new make() : super() { this.style.addClass("domkit-ListButton disclosure-list") this.sel = ListButtonSelection(this) this.onPopup { makeListbox } this.update // shift to align text this.popupOffset = Point(-12, 0) } ** The current list items. Obj[] items := List.defVal { set { &items = it sel.index = it.size==0 ? null : 0 update } } ** Selection for list. Selection sel { private set } ** Callback when selected item has changed. Void onSelect(|This| f) { this.cbSelect = f } ** Callback to create an 'Elem' representation for a given list ** item. If function does not return an 'Elem' instance, one will ** be created using 'toStr' of value. Void onElem(|Obj->Obj| f) { this.cbElem = f update } ** Update button content. internal Void update() { if (isCombo) return this.removeAll if (items.size == 0 || sel.item == null) this.add(Elem { it.text = "\u200b" }) else this.add(makeElem(sel.item)) } ** Fire select event. internal Void fireSelect() { cbSelect?.call(this) } ** Build listbox. private Popup makeListbox() { this.find = "" this.menu = Menu {} items.each |item,i| { elem := makeElem(item) menu.add(MenuItem { if (!isCombo) { it.style.addClass("domkit-ListButton-MenuItem") if (sel.index == i) it.style.addClass("sel") } it.add(elem) it.onAction { sel.index=i; fireSelect } }) // TODO: temp hook to mark list items as disabled if (elem.style.hasClass("disabled")) menu.lastChild.enabled = false } menu.select(sel.index) menu.onCustomKeyDown = |Event e| { onMenuKeyDown(e) } return menu } private Elem makeElem(Obj item) { v := cbElem == null ? item.toStr : cbElem(item) return v is Elem ? v : Elem { it.text=v.toStr } } private Void onMenuKeyDown(Event e) { if (e.key.code.isAlphaNum) { find += e.key.code.toChar.lower ix := items.findIndex |i| { i.toStr.lower.startsWith(find) } if (ix != -1) menu.select(ix) } } private Func? cbSelect := null private Func? cbElem := null private Str find := "" // onPopup private Menu? menu // onPopup } ************************************************************************** ** ListButtonSelection ************************************************************************** @Js internal class ListButtonSelection : IndexSelection { new make(ListButton button) { this.button = button } override Int max() { button.items.size } override Obj toItem(Int index) { button.items[index] } override Int? toIndex(Obj item) { button.items.findIndex |i| { i == item }} override Void onUpdate(Int[] oldIndexes, Int[] newIndexes) { button.update } private ListButton button }