123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- package h2d;
- private class Fake extends Object {
- var dd : Dropdown;
- public function new(dd : Dropdown) {
- super(dd);
- this.dd = dd;
- }
- override function getBoundsRec(relativeTo:Object, out:h2d.col.Bounds, forSize:Bool) {
- super.getBoundsRec(relativeTo, out, forSize);
- if (dd.selectedItem >= 0) {
- var item = @:privateAccess dd.getItem(dd.selectedItem);
- var size = item.getSize();
- addBounds(relativeTo, out, 0, 0, size.width, size.height);
- }
- }
- override function draw(ctx) {
- if (dd.selectedItem >= 0) {
- var item = @:privateAccess dd.getItem(dd.selectedItem);
- var oldX = item.absX;
- var oldY = item.absY;
- item.absX = absX;
- item.absY = absY;
- for( c in item ) c.posChanged = true;
- item.drawRec(ctx);
- for( c in item ) c.posChanged = true;
- item.absX = oldX;
- item.absY = oldY;
- }
- }
- }
- /**
- A simple UI component that creates an interactive drop-down list.
- Dropdown will add an `h2d.Flow` to the `Scene` when opening in order to be visible above other objects. See `Dropdown.dropdownLayer` for more details.
- There is no handling of user input on items, and implementation of selection and other actions is up to the user.
- Note that when `dropdownList` opens and closes, item objects will receive the `onHierarchyChanged` callback.
- **/
- @:uiNoComponent
- class Dropdown extends Flow {
- var fake : Fake;
- var cursor : h2d.Bitmap;
- var arrow : h2d.Bitmap;
- /**
- A background Tile that is shown when user hover over an item in the dropdown list.
- The tile will be stretched to cover full row width and item height during rendering.
- **/
- public var tileOverItem(default, set) : h2d.Tile;
- /**
- A Tile used to visualize an arrow of the dropdown when the list is closed.
- **/
- public var tileArrow(default, set) : h2d.Tile;
- /**
- A Tile used to visualize and arrow of the dropdown when the list is open.
- **/
- public var tileArrowOpen : h2d.Tile;
- /**
- When disabled, the user would not be able to change the selected item.
- **/
- public var canEdit(default,set) : Bool = true;
- /**
- A reference to the Flow that will contain the items.
- **/
- public var dropdownList : Flow;
- /**
- A Scene layer to which `dropdownList` will be added when opening dropdown.
- **/
- public var dropdownLayer : Int = 0;
- /**
- Currently selected item index. To deselect an item, set it to `-1`.
- **/
- public var selectedItem(default, set) : Int = -1;
- /**
- Currently highlighted item index.
- **/
- public var highlightedItem(default, null) : Int = -1;
- /**
- When enabled, the dropdown list will appear above the dropdown.
- **/
- public var rollUp : Bool = false;
- /**
- Create a new Dropdown with given parent.
- @param parent An optional parent `h2d.Object` instance to which Dropdown adds itself if set.
- **/
- public function new(?parent) {
- super(parent);
- canEdit = true;
- minHeight = maxHeight = 21;
- paddingLeft = 5;
- verticalAlign = Middle;
- reverse = true;
- tileOverItem = h2d.Tile.fromColor(0x303030, 1, 1);
- tileArrow = tileArrowOpen = h2d.Tile.fromColor(0x404040, maxHeight - 2, maxHeight - 2);
- backgroundTile = h2d.Tile.fromColor(0x101010);
- borderHeight = borderWidth = 1;
- dropdownList = new Flow(this);
- dropdownList.layout = Vertical;
- dropdownList.borderHeight = dropdownList.borderWidth = 1;
- dropdownList.paddingLeft = paddingLeft;
- dropdownList.visible = false;
- cursor = new h2d.Bitmap(tileOverItem, dropdownList);
- dropdownList.getProperties(cursor).isAbsolute = true;
- arrow = new h2d.Bitmap(tileArrow, this);
- var p = getProperties(arrow);
- p.horizontalAlign = Right;
- p.verticalAlign = Top;
- inline function setItem(i) {
- if( selectedItem != i ) {
- selectedItem = i;
- onChange(getItem(i));
- }
- }
- //
- fake = new Fake(this);
- enableInteractive = true;
- interactive.onPush = function(e:hxd.Event) {
- if( e.button == 0 && canEdit )
- interactive.focus();
- }
- interactive.onClick = function(e) {
- if (dropdownList.parent != this ) {
- close();
- } else if( canEdit ) {
- var bds = this.getBounds();
- dropdownList.x = bds.xMin;
- dropdownList.minWidth = this.minWidth;
- open();
- dropdownList.y = rollUp ? bds.yMin - dropdownList.getSize().height : bds.yMax;
- }
- }
- interactive.onFocusLost = function(e) {
- if (highlightedItem >= 0 && canEdit) {
- setItem(highlightedItem);
- }
- close();
- }
- dropdownList.enableInteractive = true;
- dropdownList.interactive.onClick = function(e) {
- if( canEdit ) setItem(highlightedItem);
- close();
- }
- dropdownList.interactive.onMove = function(e : hxd.Event) {
- var clickPos = dropdownList.localToGlobal(new h2d.col.Point(e.relX, e.relY));
- var items = getItems();
- for (i in 0...items.length) {
- var item = items[i];
- var bds = item.getBounds();
- if (clickPos.y >= bds.yMin && clickPos.y < bds.yMax) {
- if (highlightedItem != i) {
- if (highlightedItem >= 0) {
- onOutItem(items[highlightedItem]);
- }
- highlightedItem = i;
- if (cursor.tile.width != 0 && cursor.tile.height != 0) {
- cursor.visible = true;
- cursor.x = 1;
- cursor.y = item.y;
- cursor.tile.width = minWidth - 2;
- cursor.tile.height = Std.int(item.getSize().height);
- }
- onOverItem(item);
- }
- break;
- }
- }
- }
- dropdownList.interactive.onOut = function(e : hxd.Event) {
- onOutItem(getItem(highlightedItem));
- highlightedItem = -1;
- cursor.visible = false;
- }
- needReflow = true;
- }
- function getItems() : Array<h2d.Object> {
- return [for( i => obj in dropdownList.children ) if( !dropdownList.properties[i].isAbsolute ) obj];
- }
- function getItem(index) {
- return getItems()[index];
- }
- override function set_backgroundTile(t) {
- super.set_backgroundTile(t);
- if(dropdownList != null) dropdownList.backgroundTile = t;
- return t;
- }
- function set_tileArrow(t) {
- if(arrow != null) arrow.tile = t;
- return tileArrow = t;
- }
- function set_tileOverItem(t) {
- if(cursor != null) cursor.tile = t;
- return tileOverItem = t;
- }
- /**
- Adds the Object `s` to the dropdown list. `s` is not restricted to be the same type across all items.
- **/
- public function addItem(s : Object) {
- dropdownList.addChild(s);
- var width = Std.int(dropdownList.getSize().width);
- if( maxWidth != null && width > maxWidth ) width = maxWidth;
- minWidth = hxd.Math.imax(minWidth, Std.int(width-arrow.getSize().width));
- }
- function set_canEdit(b) {
- if( !b ) close();
- alpha = b ? 1 : 0.7;
- return canEdit = b;
- }
- function set_selectedItem(s) {
- var items = getItems();
- if( s < 0 )
- s = -1;
- else if( s >= items.length )
- s = items.length - 1;
- var item = items[s];
- if( item != null )
- minHeight = Std.int(item.getSize().height);
- needReflow = true;
- return selectedItem = s;
- }
- /**
- Programmatically opens the dropdown, showing the dropdown list.
- **/
- public function open() {
- if( dropdownList.parent == this ) {
- getScene().add(dropdownList, dropdownLayer);
- dropdownList.visible = true;
- arrow.tile = tileArrowOpen;
- onOpen();
- }
- }
- /**
- Programmatically closes the dropdown, hiding the dropdown list.
- **/
- public function close() {
- if( dropdownList.parent != this ) {
- addChild(dropdownList);
- dropdownList.visible = false;
- arrow.tile = tileArrow;
- onClose();
- }
- }
- override function onRemove() {
- super.onRemove();
- if( dropdownList.parent != this )
- dropdownList.remove();
- }
- /**
- Sent when dropdown is being opened. Triggered both by user input and programmatic action via `Dropdown.open`.
- **/
- public dynamic function onOpen() {
- }
- /**
- Sent when dropdown is being closed. Triggered both by user input and programmatic action via `Dropdown.close`.
- **/
- public dynamic function onClose() {
- }
- /**
- Sent when user change the item in the list.
- @param item An object that was hovered.
- **/
- public dynamic function onChange(item : Object) {
- }
- /**
- Sent when user hovers over an item in the dropdown list.
- @param item An object that was hovered.
- **/
- public dynamic function onOverItem(item : Object) {
- }
- /**
- Sent when user moves mouse away from an item in the dropdown list.
- @param item An item that was hovered previously.
- **/
- public dynamic function onOutItem(item : Object) {
- }
- }
|