diff --git a/examples/helloworld/src/helloworld.d b/examples/helloworld/src/helloworld.d index 365d2855..df5d1f53 100644 --- a/examples/helloworld/src/helloworld.d +++ b/examples/helloworld/src/helloworld.d @@ -46,7 +46,7 @@ extern (C) int UIAppMain(string[] args) { layoutWidth: fill CheckBox { id: cb1; text: "checkbox 1" } CheckBox { id: cb2; text: "checkbox 2" } - ComboEdit { id: ce1; text: "some text"; } + ComboEdit { id: ce1; text: "some text"; minWidth: 100; items: ["Item 1", "Item 2", "Additional item"] } } } HorizontalLayout { diff --git a/src/dlangui/core/i18n.d b/src/dlangui/core/i18n.d index 5c83bc99..e5e45759 100644 --- a/src/dlangui/core/i18n.d +++ b/src/dlangui/core/i18n.d @@ -192,20 +192,42 @@ struct UIStringCollection { clear(); addAll(items); } - /** Append array of string resource IDs */ - void addAll(string[] items) { - foreach (string item; items) { - add(item); - } - } /** Assign array of unicode strings */ void opAssign(dstring[] items) { clear(); addAll(items); } + /** Assign array of UIString */ + void opAssign(UIString[] items) { + clear(); + addAll(items); + } + /** Assign array of StringListValue */ + void opAssign(StringListValue[] items) { + clear(); + addAll(items); + } /** Append array of unicode strings */ void addAll(dstring[] items) { - foreach (dstring item; items) { + foreach (item; items) { + add(item); + } + } + /** Append array of unicode strings */ + void addAll(string[] items) { + foreach (item; items) { + add(item); + } + } + /** Append array of unicode strings */ + void addAll(UIString[] items) { + foreach (item; items) { + add(item); + } + } + /** Append array of unicode strings */ + void addAll(StringListValue[] items) { + foreach (item; items) { add(item); } } @@ -226,6 +248,10 @@ struct UIStringCollection { s = item; add(s, index); } + /** Insert StringListValue.label item into specified position */ + void add(StringListValue item, int index = -1) { + add(item.label, index); + } /** Insert UIString item into specified position */ void add(UIString item, int index = -1) { if (index < 0 || index > _length) @@ -274,6 +300,43 @@ struct UIStringCollection { } } +/// string values string list adapter - each item can have optional string or integer id, and optional icon resource id +struct StringListValue { + /// integer id for item + int intId; + /// string id for item + string stringId; + /// icon resource id + string iconId; + /// label to show for item + UIString label; + + this(string id, dstring name, string iconId = null) { + this.stringId = id; + this.label = name; + this.iconId = iconId; + } + this(string id, string nameResourceId, string iconId = null) { + this.stringId = id; + this.label = nameResourceId; + this.iconId = iconId; + } + this(int id, dstring name, string iconId = null) { + this.intId = id; + this.label = name; + this.iconId = iconId; + } + this(int id, string nameResourceId, string iconId = null) { + this.intId = id; + this.label = nameResourceId; + this.iconId = iconId; + } + this(dstring name, string iconId = null) { + this.label = name; + this.iconId = iconId; + } +} + /** UI Strings internationalization translator */ class UIStringTranslator { diff --git a/src/dlangui/dml/parser.d b/src/dlangui/dml/parser.d index 37d6097f..18ef0e7c 100644 --- a/src/dlangui/dml/parser.d +++ b/src/dlangui/dml/parser.d @@ -181,6 +181,18 @@ class MLParser { error("unknown double property " ~ propName); } + protected void setStringListValueProperty(string propName, StringListValue[] values) { + if (!_currentWidget.setStringListValueListProperty(propName, values)) { + UIString[] strings; + foreach(value; values) + strings ~= value.label; + if (!_currentWidget.setUIStringListProperty(propName, strings)) { + error("unknown string list property " ~ propName); + } + } + } + + protected void setRectProperty(string propName, Rect value) { if (!_currentWidget.setRectProperty(propName, value)) error("unknown Rect property " ~ propName); @@ -300,6 +312,66 @@ class MLParser { setRectProperty(propName, Rect(values[0], values[1], values[2], values[3])); } + // something in [] + protected void parseArrayProperty(string propName) { + // current token is Rect + nextToken(); + skipWhitespaceAndEolsNoEof(); + StringListValue[] values; + for (;;) { + if (_token.type == TokenType.squareClose) + break; + if (_token.type == TokenType.integer) { + if (_token.text.length) + error("Integer literal suffixes not allowed for [] items"); + StringListValue value; + value.intId = _token.intvalue; + value.label = UIString(to!dstring(_token.intvalue)); + values ~= value; + nextToken(); + skipWhitespaceAndEolsNoEof(); + if (_token.type == TokenType.comma || _token.type == TokenType.semicolon) { + nextToken(); + skipWhitespaceAndEolsNoEof(); + } + } else if (_token.type == TokenType.ident) { + string name = _token.text; + + StringListValue value; + value.stringId = name; + value.label = UIString(name); + values ~= value; + + nextToken(); + skipWhitespaceAndEolsNoEof(); + + if (_token.type == TokenType.comma || _token.type == TokenType.semicolon) { + nextToken(); + skipWhitespaceAndEolsNoEof(); + } + } else if (_token.type == TokenType.str) { + string name = _token.text; + + StringListValue value; + value.stringId = name; + value.label = UIString(name.toUTF32); + values ~= value; + + nextToken(); + skipWhitespaceAndEolsNoEof(); + + if (_token.type == TokenType.comma || _token.type == TokenType.semicolon) { + nextToken(); + skipWhitespaceAndEolsNoEof(); + } + } else { + error("invalid [] item"); + } + + } + setStringListValueProperty(propName, values); + } + protected void parseProperty() { if (_token.type != TokenType.ident) error("identifier expected"); @@ -323,6 +395,8 @@ class MLParser { error("number expected after + and -"); } else if (_token.type == TokenType.floating) setFloatProperty(propName, _token.floatvalue); + else if (_token.type == TokenType.squareOpen) + parseArrayProperty(propName); else if (_token.type == TokenType.str) setStringProperty(propName, _token.text); else if (_token.type == TokenType.ident) { diff --git a/src/dlangui/dml/tokenizer.d b/src/dlangui/dml/tokenizer.d index 260f43ec..40025096 100644 --- a/src/dlangui/dml/tokenizer.d +++ b/src/dlangui/dml/tokenizer.d @@ -41,9 +41,9 @@ enum TokenType : ushort { minus, /// + operator plus, - /// [ + /// { curlyOpen, - /// ] + /// } curlyClose, /// ( open, diff --git a/src/dlangui/widgets/combobox.d b/src/dlangui/widgets/combobox.d index 6bbee3c2..6768fc7b 100644 --- a/src/dlangui/widgets/combobox.d +++ b/src/dlangui/widgets/combobox.d @@ -119,6 +119,8 @@ class ComboBoxBase : HorizontalLayout, OnClickHandler { } protected void showPopup() { + if (!_adapter || !_adapter.itemCount) + return; // don't show empty popup _popupList = createPopup(); _popup = window.showPopup(_popupList, this, PopupAlign.Below | PopupAlign.FitAnchorSize); _popup.flags = PopupFlags.CloseOnClickOutside; @@ -233,13 +235,25 @@ class ComboBox : ComboBoxBase { } @property void items(StringListValue[] items) { - setAdapter(new StringListAdapter(items)); + if (auto a = cast(StringListAdapter)_adapter) + a.items = items; + else + setAdapter(new StringListAdapter(items)); if(items.length > 0) { selectedItemIndex = 0; } requestLayout(); } + /// StringListValue list values + override bool setStringListValueListProperty(string propName, StringListValue[] values) { + if (propName == "items") { + items = values; + return true; + } + return false; + } + /// get selected item as text @property dstring selectedItem() { if (_selectedItemIndex < 0 || _selectedItemIndex >= _adapter.itemCount) @@ -348,7 +362,10 @@ class IconTextComboBox : ComboBoxBase { } @property void items(StringListValue[] items) { - setAdapter(new IconStringListAdapter(items)); + if (auto a = cast(IconStringListAdapter)_adapter) + a.items = items; + else + setAdapter(new IconStringListAdapter(items)); if(items.length > 0) { selectedItemIndex = 0; } @@ -451,18 +468,42 @@ class ComboEdit : ComboBox { /// empty parameter list constructor - for usage by factory this() { this(null); + postInit(); } /// create with ID parameter this(string ID) { super(ID); + postInit(); } this(string ID, string[] items) { super(ID, items); + postInit(); } this(string ID, dstring[] items) { super(ID, items); + postInit(); + } + + protected void postInit() { + focusable = false; + clickable = false; + _edit.focusable = true; + keyEvent = delegate(Widget source, KeyEvent event) { + if (event.keyCode == KeyCode.DOWN) { + if (event.action == KeyAction.KeyDown) { + showPopup(); + } + } + return _edit.onKeyEvent(event); + }; + } + + // called to process click and notify listeners + override protected bool handleClick() { + _edit.setFocus(); + return true; } @property bool readOnly() { @@ -484,6 +525,8 @@ class ComboEdit : ComboBox { res.layoutHeight = WRAP_CONTENT; res.readOnly = false; _edit = res; + postInit(); + //_edit.focusable = true; return res; } diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d index a3dfcb14..579fcfa1 100644 --- a/src/dlangui/widgets/lists.d +++ b/src/dlangui/widgets/lists.d @@ -177,43 +177,6 @@ class WidgetListAdapter : ListAdapterBase { } } -/// string values string list adapter - each item can have optional string or integer id, and optional icon resource id -struct StringListValue { - /// integer id for item - int intId; - /// string id for item - string stringId; - /// icon resource id - string iconId; - /// label to show for item - UIString label; - - this(string id, dstring name, string iconId = null) { - this.stringId = id; - this.label = name; - this.iconId = iconId; - } - this(string id, string nameResourceId, string iconId = null) { - this.stringId = id; - this.label = nameResourceId; - this.iconId = iconId; - } - this(int id, dstring name, string iconId = null) { - this.intId = id; - this.label = name; - this.iconId = iconId; - } - this(int id, string nameResourceId, string iconId = null) { - this.intId = id; - this.label = nameResourceId; - this.iconId = iconId; - } - this(dstring name, string iconId = null) { - this.label = name; - this.iconId = iconId; - } -} - /** List adapter providing strings only. */ class StringListAdapterBase : ListAdapterBase { protected UIStringCollection _items; @@ -340,6 +303,40 @@ class StringListAdapterBase : ListAdapterBase { return this; } + /** Replace items collection. */ + @property StringListAdapterBase items(UIString[] values) { + _items = values; + _intIds.length = items.length; + _states.length = _items.length; + _stringIds.length = items.length; + _iconIds.length = items.length; + for (int i = 0; i < _items.length; i++) { + _intIds[i] = 0; + _stringIds[i] = null; + _iconIds[i] = null; + _states[i] = State.Enabled; + } + updateViews(); + return this; + } + + /** Replace items collection. */ + @property StringListAdapterBase items(StringListValue[] values) { + _items = values; + _intIds.length = items.length; + _states.length = _items.length; + _stringIds.length = items.length; + _iconIds.length = items.length; + for (int i = 0; i < _items.length; i++) { + _intIds[i] = values[i].intId; + _stringIds[i] = values[i].stringId; + _iconIds[i] = values[i].iconId; + _states[i] = State.Enabled; + } + updateViews(); + return this; + } + /// returns number of widgets in list @property override int itemCount() const { return _items.length; @@ -1355,6 +1352,15 @@ class StringListWidget : ListWidget { requestLayout(); } + /// StringListValue list values + override bool setStringListValueListProperty(string propName, StringListValue[] values) { + if (propName == "items") { + items = values; + return true; + } + return false; + } + /// get selected item as text @property dstring selectedItem() { if (_selectedItemIndex < 0 || _selectedItemIndex >= _adapter.itemCount) diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 68b9252e..60150adc 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -1570,6 +1570,16 @@ public: return false; } + /// StringListValue list values + bool setStringListValueListProperty(string propName, StringListValue[] values) { + return false; + } + + /// UIString list values + bool setUIStringListProperty(string propName, UIString[] values) { + return false; + } + /// set string property value, for ML loaders bool setBoolProperty(string name, bool value) { mixin(generatePropertySetters("enabled", "clickable", "checkable", "focusable", "checked", "fontItalic")); @@ -1809,3 +1819,4 @@ string generatePropertySettersMethodOverride(string methodName, string typeName, return res; } +