diff --git a/haxe/ui/_module/styles/default/pickers.css b/haxe/ui/_module/styles/default/pickers.css new file mode 100644 index 000000000..c5499ee58 --- /dev/null +++ b/haxe/ui/_module/styles/default/pickers.css @@ -0,0 +1,59 @@ +.item-picker { + background-color: #FFEEEE; + padding: 6px 5px; + height: auto; + width: auto; + + border: 1px solid $normal-border-color; + border-radius: 2px; + + background: $normal-background-color-start $normal-background-color-end vertical; +} + +.item-picker:hover { + background: $hover-background-color-start $hover-background-color-end vertical; + color: $hover-text-color; + border: $normal-border-size solid $hover-border-color; +} + +.item-picker.selected { + background-color: $down-background-color-start; + color: $hover-text-color; + border-color: $hover-border-color; + filter: drop-shadow(1, 45, #888888, 0.1, 1, 1, 1, 3, true); + + border-color: $accent-color; +} + +.item-picker:active { + border: 1px solid $accent-color; +} + +.item-picker-panel-filler { + background-color: $accent-color; +} + +.item-picker-trigger { + pointer-events: true; +} + +.item-picker-trigger-icon { + resource: $arrow-up-down; + vertical-align: center; +} + +.item-picker-container.position-down { + padding: 1px; + border: 1px solid $accent-color; + border-top-color: $down-border-color; + margin-top: -1px; + filter: box-shadow(2, 2, #000000, 0.15, 6); +} + +.item-picker-container.position-up { + padding: 1px; + border: 1px solid $accent-color; + border-bottom-color: $down-border-color; + margin-top: -1px; + filter: box-shadow(2, -2, #000000, 0.15, 6); +} diff --git a/haxe/ui/components/pickers/ItemPicker.hx b/haxe/ui/components/pickers/ItemPicker.hx new file mode 100644 index 000000000..74874e2d7 --- /dev/null +++ b/haxe/ui/components/pickers/ItemPicker.hx @@ -0,0 +1,393 @@ +package haxe.ui.components.pickers; + +import haxe.ui.components.Image; +import haxe.ui.containers.Box; +import haxe.ui.containers.HBox; +import haxe.ui.core.BasicItemRenderer; +import haxe.ui.core.Component; +import haxe.ui.core.CompositeBuilder; +import haxe.ui.core.IDataComponent; +import haxe.ui.core.InteractiveComponent; +import haxe.ui.core.ItemRenderer; +import haxe.ui.core.Screen; +import haxe.ui.data.ArrayDataSource; +import haxe.ui.data.DataSource; +import haxe.ui.events.MouseEvent; +import haxe.ui.events.UIEvent; +import haxe.ui.geom.Size; +import haxe.ui.layouts.DefaultLayout; + +@:composite(ItemPickerBuilder, Layout) +class ItemPicker extends InteractiveComponent implements IDataComponent { + public var selectionType = "dropPanel"; + public var panelPosition = "auto"; + public var panelOrigin = "auto"; + public var panelWidth:Null = null; + + private var _dataSource:DataSource = null; + public var dataSource(get, set):DataSource; + private function get_dataSource():DataSource { + if (_dataSource == null) { + _dataSource = new ArrayDataSource(); + var builder:ItemPickerBuilder = cast(_compositeBuilder, ItemPickerBuilder); + builder.handler.applyDataSource(_dataSource); + } + return _dataSource; + } + private function set_dataSource(value:DataSource):DataSource { + _dataSource = value; + var builder:ItemPickerBuilder = cast(_compositeBuilder, ItemPickerBuilder); + builder.handler.applyDataSource(_dataSource); + return value; + } + + private var _panelSelectionEvent:String = UIEvent.CHANGE; + public var panelSelectionEvent(get, set):String; + private function get_panelSelectionEvent():String { + return _panelSelectionEvent; + } + private function set_panelSelectionEvent(value:String):String { + _panelSelectionEvent = value; + var builder:ItemPickerBuilder = cast(_compositeBuilder, ItemPickerBuilder); + builder.panelSelectionEvent = _panelSelectionEvent; + return value; + } +} + +class ItemPickerHandler { + public var builder:ItemPickerBuilder; + public var picker:ItemPicker; + public var renderer:Component; + public var panel:Component; + + private function pausePanelEvents() { + builder.pausePanelEvents(); + } + + private function resumePanelEvents() { + builder.resumePanelEvents(); + } + + public function applyDataSource(ds:DataSource) { + } + + public function onPanelShown() { + } + + public function onPanelHidden() { + } + + public function onPanelSelection(event:UIEvent) { + } +} + +private class DefaultItemPickerRenderer extends HBox { + private var _renderer:ItemRenderer = new BasicItemRenderer(); + private var _triggerIcon:Image = new Image(); + + public function new() { + super(); + + addComponent(_renderer); + _triggerIcon.scriptAccess = false; + _triggerIcon.id = "itemPickerTriggerIcon"; + _triggerIcon.addClass("item-picker-trigger-icon"); + addComponent(_triggerIcon); + //_renderer.data = { text: "bob", icon: "haxeui-core/styles/default/haxeui_tiny.png"}; + } +} + +class ItemPickerBuilder extends CompositeBuilder { + private var picker:ItemPicker; + + public var renderer:Component = null; + public var panel:Component = null; + public var panelContainer:Box = new Box(); + public var handler:ItemPickerHandler = null; + + public function new(picker:ItemPicker) { + super(picker); + this.picker = picker; + handler = Type.createInstance(handlerClass, []); + handler.builder = this; + handler.picker = picker; + } + + private var _panelSelectionEvent:String = UIEvent.CHANGE; + public var panelSelectionEvent(get, set):String; + private function get_panelSelectionEvent():String { + return _panelSelectionEvent; + } + private function set_panelSelectionEvent(value:String):String { + if (panel != null) { + panel.unregisterEvent(_panelSelectionEvent, onPanelSelection); + } + _panelSelectionEvent = value; + registerPanelEvents(); + return value; + } + + public var triggerEvent(get, null):String; + private function get_triggerEvent():String { + return MouseEvent.MOUSE_DOWN; + } + + public var triggerTarget(get, null):Component; + private function get_triggerTarget():Component { + var target = renderer.findComponent("itemPickerTrigger", Component); + if (target != null) { + picker.unregisterEvent(triggerEvent, onTrigger); + picker.removeClass("item-picker-trigger"); + return target; + } + return picker; + } + + public var handlerClass(get, null):Class; + private function get_handlerClass():Class { + return ItemPickerHandler; + } + + public override function create() { + super.create(); + var defaultRenderer = new DefaultItemPickerRenderer(); + defaultRenderer.id = "itemPickerRenderer"; + picker.addComponent(defaultRenderer); + + panelContainer.addClass("item-picker-container"); + } + + public override function onReady() { + super.onReady(); + } + + public override function addComponent(child:Component):Component { + if (child.id == "itemPickerRenderer" || child.id == "item-picker-renderer" || child.hasClass("item-picker-renderer")) { + if (renderer != null) { + picker.removeComponent(renderer); + } + + child.id = "itemPickerRenderer"; + renderer = child; + handler.renderer = renderer; + registerTriggerEvents(); + } else { + child.addClass("item-picker-data"); + panel = child; + handler.panel = panel; + panelContainer.addComponent(panel); + registerPanelEvents(); + return child; + } + return null; + } + + private function registerTriggerEvents() { + triggerTarget.addClass("item-picker-trigger"); + if (!triggerTarget.hasEvent(triggerEvent, onTrigger)) { + triggerTarget.registerEvent(triggerEvent, onTrigger); + } + } + + private function registerPanelEvents() { + if (panel != null) { + panel.registerEvent(panelSelectionEvent, onPanelSelection); + } + } + + public function pausePanelEvents() { + if (panel != null) { + panel.pauseEvent(panelSelectionEvent); + } + } + + public function resumePanelEvents() { + if (panel != null) { + panel.resumeEvent(panelSelectionEvent); + } + } + + private function onPanelSelection(event:UIEvent) { + handler.onPanelSelection(event); + trace("panel selection!"); + hidePanel(); + } + + private function onTrigger(event:UIEvent) { + //event.cancel(); + trace("on trigger"); + if (!_panelVisible) { + picker.focus = true; + showPanel(); + } else { + hidePanel(); + } + } + + private var _panelVisible:Bool = false; + private var _panelFiller:Component = null; + public function showPanel() { + if (panel == null || panelContainer == null) { + return; + } + + pausePanelEvents(); + + picker.addClass("selected", true, true); + handler.onPanelShown(); + panelContainer.opacity = 0; + Screen.instance.addComponent(panelContainer); + panelContainer.invalidateComponent(); + panel.validateNow(); + panelContainer.validateNow(); + + + var panelPosition = "down"; + var panelOrigin = "left"; + var panelPosition = picker.panelPosition; + var panelOrigin = picker.panelOrigin; + var panelWidth:Null = picker.width; + var panelHeight:Null = panel.height; + + if (picker.panelWidth != null) { + panelWidth = picker.panelWidth; + } + + if (panelPosition == "auto") { + if (picker.screenTop + picker.height + panelHeight > Screen.instance.height) { + panelPosition = "up"; + } else { + panelPosition = "down"; + } + } + + if (panelOrigin == "auto") { + if (picker.screenLeft + panelWidth > Screen.instance.width) { + panelOrigin = "right"; + } else { + panelOrigin = "left"; + } + } + + trace(panelWidth, panelHeight); + + if (panelPosition == "down") { + panelContainer.addClass("position-down"); + } else if (panelPosition == "up") { + panelContainer.addClass("position-up"); + } + + panelContainer.invalidateComponent(); + panel.validateNow(); + panelContainer.validateNow(); + + var marginTop:Float = 0; + var marginLeft:Float = 0; + var marginBottom:Float = 0; + var marginRight:Float = 0; + var horizontalPadding:Float = 0; + var verticalPadding:Float = 0; + var borderSize:Float = 0; + if (panelContainer.style != null) { + marginTop = panelContainer.style.marginTop; + marginLeft = panelContainer.style.marginLeft; + marginBottom = panelContainer.style.marginBottom; + marginRight = panelContainer.style.marginRight; + horizontalPadding = panelContainer.style.paddingLeft + panelContainer.style.paddingRight; + borderSize = panelContainer.style.borderTopSize; + } + + if (_panelFiller == null) { + _panelFiller = new Component(); + _panelFiller.addClass("item-picker-panel-filler"); + _panelFiller.includeInLayout = false; + _panelFiller.height = borderSize; + panelContainer.addComponent(_panelFiller); + } + + _panelFiller.width = panelWidth - picker.width; + panel.width = panelWidth - horizontalPadding; + + if (panelOrigin == "left") { + panelContainer.left = picker.screenLeft; + _panelFiller.left = picker.width - borderSize; + } else if (panelOrigin == "right") { + panelContainer.left = picker.screenLeft + picker.width - panelWidth; + _panelFiller.left = borderSize; + } + + if (panelPosition == "down") { + panelContainer.top = picker.screenTop + picker.height + marginTop; + _panelFiller.top = 0; + } else if (panelPosition == "up") { + panelContainer.top = picker.screenTop - panelContainer.height - marginTop; + _panelFiller.top = panelHeight + borderSize; + } + + if (picker.animatable) { + panelContainer.fadeIn(); + } else { + panelContainer.opacity = 1; + } + Screen.instance.registerEvent(MouseEvent.MOUSE_DOWN, onScreenMouseDown); + _panelVisible = true; + + Toolkit.callLater(function() { + resumePanelEvents(); + }); + } + + public function hidePanel() { + handler.onPanelHidden(); + picker.removeClass("selected", true, true); + Screen.instance.removeComponent(panelContainer, false); + Screen.instance.unregisterEvent(MouseEvent.MOUSE_DOWN, onScreenMouseDown); + _panelVisible = false; + } + + private function onScreenMouseDown(event:MouseEvent) { + if (triggerTarget.hitTest(event.screenX, event.screenY)) { + return; + } + if (panelContainer.hitTest(event.screenX, event.screenY)) { + return; + } + hidePanel(); + } +} + +private class Layout extends DefaultLayout { + public override function resizeChildren() { + super.resizeChildren(); + + var usableSize = this.usableSize; + var renderer = findComponent("itemPickerRenderer", Component); + if (renderer == null) { + return; + } + if (!component.autoWidth) { + renderer.width = usableSize.width; + } else { + + } + + if (!component.autoHeight) { + renderer.height = usableSize.height; + } else { + } + } + + public override function repositionChildren() { + super.repositionChildren(); + } + + /* + public override function calcAutoSize(exclusions:Array = null):Size { + var size = new Size(); + size.height = 50; + size.width = 100; + return size; + } + */ +} + diff --git a/haxe/ui/components/pickers/ListItemPicker.hx b/haxe/ui/components/pickers/ListItemPicker.hx new file mode 100644 index 000000000..09b0217fe --- /dev/null +++ b/haxe/ui/components/pickers/ListItemPicker.hx @@ -0,0 +1,40 @@ +package haxe.ui.components.pickers; + +import haxe.ui.components.pickers.ItemPicker; +import haxe.ui.core.ItemRenderer; +import haxe.ui.data.DataSource; +import haxe.ui.events.UIEvent; + +@:composite(Builder) +@:xml(' + + + +') +class ListItemPicker extends ItemPicker { +} + +private class Builder extends ItemPickerBuilder { + private override function get_handlerClass():Class { + return Handler; + } +} + +private class Handler extends ItemPickerHandler { + public override function applyDataSource(ds:DataSource) { + var listItemPicker:ListItemPicker = cast picker; + listItemPicker.listView.dataSource = ds; + var indexToSelect = 0; + if (indexToSelect != -1) { + listItemPicker.listView.selectedIndex = indexToSelect; + var r = renderer.findComponent(ItemRenderer); + r.data = listItemPicker.listView.selectedItem; + } + } + + public override function onPanelSelection(event:UIEvent) { + var listItemPicker:ListItemPicker = cast picker; + var r = renderer.findComponent(ItemRenderer); + r.data = listItemPicker.listView.selectedItem; + } +} \ No newline at end of file diff --git a/haxe/ui/module.xml b/haxe/ui/module.xml index b7d0b25fe..d4e1782ad 100644 --- a/haxe/ui/module.xml +++ b/haxe/ui/module.xml @@ -52,6 +52,7 @@ + @@ -200,6 +201,7 @@