haxe/ui/components/Switch.hx
package haxe.ui.components;
import haxe.ui.behaviours.DataBehaviour;
import haxe.ui.behaviours.DefaultBehaviour;
import haxe.ui.core.Component;
import haxe.ui.core.CompositeBuilder;
import haxe.ui.core.ICompositeInteractiveComponent;
import haxe.ui.core.IValueComponent;
import haxe.ui.core.InteractiveComponent;
import haxe.ui.events.UIEvent;
import haxe.ui.layouts.DefaultLayout;
import haxe.ui.layouts.HorizontalLayout;
import haxe.ui.util.Variant;
/**
* A switch component that can be used to toggle between two states.
*/
@:composite(Builder, HorizontalLayout)
class Switch extends Component implements IValueComponent implements ICompositeInteractiveComponent {
//***********************************************************************************************************
// Public API
//***********************************************************************************************************
/**
* Whether the switch is in the `on` state.
*
* `true` if it is, `false` otherwise.
*/
@:clonable @:behaviour(SelectedBehaviour) public var selected:Bool;
/**
The value of this switch, which is equivalent to its `selected` property
**/
@:clonable @:value(selected) public var value:Dynamic;
/**
* The text displayed on the switch
*/
@:clonable @:behaviour(TextBehaviour) public var text:String;
/**
* The text that displays on the switch when it is in the on state
*/
@:clonable @:behaviour(DefaultBehaviour) public var textOn:String;
/**
* The text that displays on the switch when it is in the off state
*/
@:clonable @:behaviour(DefaultBehaviour) public var textOff:String;
}
//***********************************************************************************************************
// Behaviours
//***********************************************************************************************************
@:dox(hide) @:noCompletion
private class SelectedBehaviour extends DataBehaviour {
private override function validateData() {
_component.findComponent(SwitchButtonSub).selected = _value;
}
}
@:dox(hide) @:noCompletion
private class TextBehaviour extends DataBehaviour {
private override function validateData() {
var label:Label = _component.findComponent(Label, false);
if (label == null) {
label = new Label();
label.styleNames = "switch-label";
label.id = "switch-label";
label.scriptAccess = false;
_component.addComponentAt(label, 0);
var spacer = new Spacer(); // TODO: ugly
spacer.percentWidth = 100;
_component.addComponentAt(spacer, 1);
_component.invalidateComponentStyle(true);
}
label.text = _value;
}
}
//***********************************************************************************************************
// Composite Builder
//***********************************************************************************************************
@:dox(hide) @:noCompletion
private class Builder extends CompositeBuilder {
private var _switch:Switch;
private var _button:SwitchButtonSub;
private var _label:Label;
public function new(s:Switch) {
super(s);
_switch = s;
}
public override function create() {
if (_button == null) {
_button = new SwitchButtonSub();
_button.onChange = function(e) {
_switch.selected = _button.selected;
_switch.dispatch(new UIEvent(UIEvent.CHANGE));
}
_component.addComponent(_button);
}
}
}
//***********************************************************************************************************
// Custom children
//***********************************************************************************************************
@:dox(hide) @:noCompletion
@:composite(SwitchButtonLayout)
private class SwitchButtonSub extends InteractiveComponent {
private var _button:Button;
private var _label:Label;
private override function createChildren() {
super.createChildren();
if (_button == null) {
var component:Component = new Component();
component.addClass("switch-button-sub-extra");
addComponent(component);
_label = new Label();
_label.id = "switch-label";
_label.addClass("switch-label");
_label.text = _unselectedText;
addComponent(_label);
_button = new Button();
_button.id = "switch-button";
_button.addClass("switch-button");
addComponent(_button);
onClick = function(e) {
selected = !selected;
}
}
}
//***********************************************************************************************************
// Public API
//***********************************************************************************************************
private var _selected:Bool = false;
@:clonable public var selected(get, set):Bool;
private function get_selected():Bool {
return _selected;
}
private function set_selected(value:Bool):Bool {
if (value == _selected) {
return value;
}
invalidateComponentData();
invalidateComponentLayout();
_selected = value;
if (_selected == false) {
_label.text = _unselectedText;
_label.removeClass(":selected");
removeClass(":selected", true, true);
addClass(":unselected", true, true);
} else {
_label.text = _selectedText;
_label.addClass(":selected");
removeClass(":unselected", true, true);
addClass(":selected", true, true);
}
var event:UIEvent = new UIEvent(UIEvent.CHANGE);
dispatch(event);
return value;
}
private var _selectedText:String = "";
public var selectedText(get, set):String;
private function get_selectedText():String {
return _selectedText;
}
private function set_selectedText(value:String):String {
_selectedText = value;
if (_componentReady && _selected == true) {
_label.text = _selectedText;
}
return value;
}
private var _unselectedText:String = "";
public var unselectedText(get, set):String;
private function get_unselectedText():String {
return _unselectedText;
}
private function set_unselectedText(value:String):String {
_unselectedText = value;
if (_componentReady && _selected == false) {
_label.text = _unselectedText;
}
return value;
}
private var _pos:Float = 0;
public var pos(get, set):Float;
private function get_pos():Float {
return _pos;
}
@:keep private function set_pos(value:Float):Float {
if (_pos == value) {
return value;
}
_pos = value;
invalidateComponentLayout();
return value;
}
}
//***********************************************************************************************************
// SwitchButton Layout
//***********************************************************************************************************
@:dox(hide) @:noCompletion
private class SwitchButtonLayout extends DefaultLayout {
private override function repositionChildren() {
var switchComp = cast(_component, SwitchButtonSub);
var button:Button = switchComp.findComponent("switch-button");
var label:Label = switchComp.findComponent("switch-label");
if (button == null || label == null) {
return;
}
button.top = paddingTop;
label.top = (component.componentHeight / 2) - (label.componentHeight / 2);
if (switchComp.selected == true) {
label.left = (button.componentWidth / 2) - (label.componentWidth / 2);
} else {
label.left = button.left + button.componentWidth + (button.componentWidth / 2) - (label.componentWidth / 2);
}
var ucx:Float = usableWidth - button.width;
var min = 0;
var max = 100;
var x = (switchComp.pos - min) / (max - min) * ucx;
button.left = paddingLeft + x;
var extra:Component = switchComp.findComponent("switch-button-sub-extra", "css");
if (extra != null) {
extra.top = (_component.height / 2) - (extra.height / 2);
}
}
}