// // Copyright (c) 2014, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 19 Dec 2014 Andy Frank Creation // using dom using graphics ** ** Button is a widget that invokes an action when pressed. ** ** See also: [docDomkit]`docDomkit::Controls#button`, ** `ToggleButton`, `ListButton` ** @Js class Button : Elem { new make() : super() { this.style.addClass("domkit-control domkit-control-button domkit-Button") this->tabIndex = 0 this.onEvent("mousedown", false) |e| { e.stop if (!enabled) return this._event = e mouseDown = true doMouseDown } this.onEvent("mouseup", false) |e| { if (!enabled) return this._event = e doMouseUp if (mouseDown) { fireAction(e) if (cbPopup != null) openPopup } mouseDown = false } this.onEvent("mouseleave", false) |e| { if (!mouseDown) return this._event = e doMouseUp mouseDown = false } this.onEvent("keydown", false) |e| { if (!enabled) return this._event = e if (e.key == Key.space || (this is ListButton && e.key == Key.down)) { doMouseDown if (cbPopup == null) Win.cur.setTimeout(100ms) |->| { fireAction(e); doMouseUp } else { if (popup?.isOpen == true) popup.close else openPopup } } } } ** Callback when button action is invoked. Void onAction(|This| f) { this.cbAction = f } ** Callback to create Popup to display when button is pressed. Void onPopup(|Button->Popup| f) { this.cbPopup = f } ** Offset to apply to default origin for `onPopup`. @NoDoc Point popupOffset := Point.defVal // TODO: how should this work? // TODO: something like onLazyPopup work better? ** Remove existing popup callback. @NoDoc Void removeOnPopup() { this.cbPopup = null } ** Programmatically open popup, or do nothing if no popup defined. Void openPopup() { if (cbPopup == null) return if (popup?.isOpen == true) return x := pagePos.x + popupOffset.x y := pagePos.y + popupOffset.y + size.h.toInt w := size.w.toInt if (isCombo) { // stretch popup to fit combo combo := this.parent x = combo.pagePos.x w = combo.size.w.toInt } showDown popup = cbPopup(this) // adjust popup origin if haligned switch (popup.halign) { case Align.center: x += w / 2 case Align.right: x += w } // use internal _onClose to keep onClose available for use popup._onClose { showUp if (isCombo) ((Combo)this.parent).field.focus else this.focus } // limit width to button size if not explicity set if (popup.style.effective("min-width") == null) popup.style->minWidth = "${w}px" popup.open(x, y) } override Bool? enabled { get { !style.hasClass("disabled") } set { if (it) { style.removeClass("disabled") this->tabIndex = 0 } else { style.addClass("disabled") this->tabIndex = -1 } } } // internal use only internal Bool isCombo := false internal Void showDown() { style.addClass("down") } internal Void showUp() { style.removeClass("down") } internal virtual Void doMouseDown() { showDown } internal virtual Void doMouseUp() { showUp } internal Bool mouseDown := false private Void fireAction(Event e) { cbAction?.call(this) } // TODO: not sure how this works yet @NoDoc Event? _event private Popup? popup := null private Func? cbAction := null private Func? cbPopup := null }