diff --git a/haxe/ui/containers/Box.hx b/haxe/ui/containers/Box.hx index cab6a4839..dae727f9b 100644 --- a/haxe/ui/containers/Box.hx +++ b/haxe/ui/containers/Box.hx @@ -6,13 +6,20 @@ import haxe.ui.layouts.DefaultLayout; import haxe.ui.layouts.LayoutFactory; import haxe.ui.styles.Style; import haxe.ui.util.Variant; +import haxe.ui.core.CompositeBuilder; +import haxe.ui.core.ItemRenderer; +import haxe.ui.core.IDataComponent; +import haxe.ui.data.ArrayDataSource; +import haxe.ui.data.DataSource; +import haxe.ui.behaviours.DataBehaviour; +import haxe.ui.events.UIEvent; /** Base `Layout` that allows a container to specify an `icon`. How that icon resource is used depends on subclasses, like `TabView` **/ @:dox(icon = "/icons/ui-panel.png") -@:composite(DefaultLayout) -class Box extends Component { +@:composite(Builder, DefaultLayout) +class Box extends Component implements IDataComponent { //*********************************************************************************************************** // Public API //*********************************************************************************************************** @@ -23,6 +30,7 @@ class Box extends Component { of it should they want to **/ @:clonable @:behaviour(DefaultBehaviour) public var icon:Variant; + @:clonable @:behaviour(DataSourceBehaviour) public var dataSource:DataSource; @:noCompletion private var _layoutName:String; @:clonable public var layoutName(get, set):String; @@ -42,6 +50,20 @@ class Box extends Component { return value; } + private var _itemRenderer:ItemRenderer; + @:clonable public var itemRenderer(get, set):ItemRenderer; + private function get_itemRenderer():ItemRenderer { + return _itemRenderer; + } + private function set_itemRenderer(value:ItemRenderer):ItemRenderer { + if (_itemRenderer != value) { + _itemRenderer = value; + invalidateComponentLayout(); + } + + return value; + } + //*********************************************************************************************************** // Internals //*********************************************************************************************************** @@ -64,4 +86,92 @@ class Box extends Component { layoutName = style.layout; } } -} \ No newline at end of file +} + +private class Builder extends CompositeBuilder { + private var _box:Box; + + public function new(box:Box) { + super(box); + _box = box; + } + + @:access(haxe.ui.backend.ComponentImpl) + public override function addComponent(child:Component):Component { + var r = null; + if (child is ItemRenderer && _box.itemRenderer == null) { + _box.itemRenderer = cast(child, ItemRenderer); + _box.itemRenderer.ready(); + _box.itemRenderer.handleVisibility(false); + r = child; + } else { + r = super.addComponent(child); + } + return r; + } +} + +//*********************************************************************************************************** +// Behaviours +//*********************************************************************************************************** +@:dox(hide) @:noCompletion +private class DataSourceBehaviour extends DataBehaviour { + private var _box:Box; + + public function new(box:Box) { + super(box); + _box = box; + } + + public override function set(value:Variant) { + super.set(value); + var dataSource:DataSource = _value; + if (dataSource != null) { + dataSource.onDataSourceChange = function() { + syncChildren(); + } + } + syncChildren(); + } + + public override function get():Variant { + if (_value == null || _value.isNull) { + _value = new ArrayDataSource(); + var dataSource:DataSource = _value; + dataSource.onDataSourceChange = function() { + syncChildren(); + } + } + return _value; + } + + private function syncChildren() { // can probably be more efficient here, but doesnt seem to cause any obvious perf problem + // wouldnt hurt to just reuse item renderers though rather than destroying and creating them + var dataSource:DataSource = _value; + for (i in 0...dataSource.size) { + var item = dataSource.get(i); + var renderer = findRenderer(item); + if (renderer == null) { + renderer = _box.itemRenderer.cloneComponent(); + _box.addComponent(renderer); + } + _box.setComponentIndex(renderer, i); + renderer.data = item; + } + var childrenToRemove = []; + for (child in _component.findComponents(ItemRenderer)) { + if (dataSource.indexOf(child.data) == -1) { + _box.removeComponent(child); + } + } + } + + private function findRenderer(data:Dynamic):ItemRenderer { + for (child in _component.findComponents(ItemRenderer)) { + if (child.data == data) { + return child; + } + } + return null; + } +}