diff --git a/examples/example1/src/example1.d b/examples/example1/src/example1.d index a2f83573..92de05bb 100644 --- a/examples/example1/src/example1.d +++ b/examples/example1/src/example1.d @@ -467,12 +467,12 @@ extern (C) int UIAppMain(string[] args) { StringListAdapter stringList = new StringListAdapter(); WidgetListAdapter listAdapter = new WidgetListAdapter(); - listAdapter.widgets.add((new TextWidget()).text("This is a list of widgets"d).styleId("LIST_ITEM")); - stringList.items.add("This is a list of strings from StringListAdapter"d); + listAdapter.add((new TextWidget()).text("This is a list of widgets"d).styleId("LIST_ITEM")); + stringList.add("This is a list of strings from StringListAdapter"d); for (int i = 1; i < 1000; i++) { dstring label = "List item "d ~ to!dstring(i); - listAdapter.widgets.add((new TextWidget()).text("Widget list - "d ~ label).styleId("LIST_ITEM")); - stringList.items.add("Simple string - "d ~ label); + listAdapter.add((new TextWidget()).text("Widget list - "d ~ label).styleId("LIST_ITEM")); + stringList.add("Simple string - "d ~ label); } list.ownAdapter = listAdapter; listAdapter.resetItemState(0, State.Enabled); @@ -492,6 +492,19 @@ extern (C) int UIAppMain(string[] args) { list2.selectItem(0); longLists.addChild(list2); + VerticalLayout itemedit = new VerticalLayout(); + itemedit.addChild(new TextWidget(null, "New item text:"d)); + EditLine itemtext = new EditLine(null, "Text for new item"d); + itemedit.addChild(itemtext); + Button btn = new Button(null, "Add item"d); + itemedit.addChild(btn); + longLists.addChild(itemedit); + btn.onClickListener = delegate(Widget src) + { + stringList.add(itemtext.text); + listAdapter.add((new TextWidget()).text(itemtext.text).styleId("LIST_ITEM")); + return true; + }; tabs.addTab(longLists, "TAB_LONG_LIST"c); } diff --git a/src/dlangui/core/collections.d b/src/dlangui/core/collections.d index 5f2f857d..b60c2644 100644 --- a/src/dlangui/core/collections.d +++ b/src/dlangui/core/collections.d @@ -1,43 +1,43 @@ -// Written in the D programming language. - -/** - -This module implements object collection. - -Wrapper around array of objects, providing a set of useful operations, and handling of object ownership. - -Optionally can be owner of its items if instanciated with ownItems=true - will destroy removed items. - - -Synopsis: - ----- -import dlangui.core.collections; - -// add -Collection!Widget widgets; -widgets ~= new Widget("id1"); -widgets ~= new Widget("id2"); -Widget w3 = new Widget("id3"); -widgets ~= w3; - -// remove by index -widgets.remove(1); - -// foreach -foreach(w; widgets) - writeln("widget: ", w.id); - -// remove by value -widgets -= w3; -writeln(widgets[0].id); ----- - - -Copyright: Vadim Lopatin, 2014 -License: Boost License 1.0 -Authors: Vadim Lopatin, coolreader.org@gmail.com -*/ +// Written in the D programming language. + +/** + +This module implements object collection. + +Wrapper around array of objects, providing a set of useful operations, and handling of object ownership. + +Optionally can be owner of its items if instanciated with ownItems=true - will destroy removed items. + + +Synopsis: + +---- +import dlangui.core.collections; + +// add +Collection!Widget widgets; +widgets ~= new Widget("id1"); +widgets ~= new Widget("id2"); +Widget w3 = new Widget("id3"); +widgets ~= w3; + +// remove by index +widgets.remove(1); + +// foreach +foreach(w; widgets) + writeln("widget: ", w.id); + +// remove by value +widgets -= w3; +writeln(widgets[0].id); +---- + + +Copyright: Vadim Lopatin, 2014 +License: Boost License 1.0 +Authors: Vadim Lopatin, coolreader.org@gmail.com +*/ module dlangui.core.collections; import std.algorithm; @@ -70,10 +70,10 @@ struct Collection(T, bool ownItems = false) { // shrink static if (is(T == class) || is(T == struct)) { // clear items - for (size_t i = newSize; i < _len; i++) { - static if (ownItems) + for (size_t i = newSize; i < _len; i++) { + static if (ownItems) destroy(_items[i]); - _items[i] = T.init; + _items[i] = T.init; } } } else if (newSize > _len) { @@ -104,11 +104,11 @@ struct Collection(T, bool ownItems = false) { } _items[index] = item; _len++; - } - /// add all items from other collection - void addAll(ref Collection!(T, ownItems) v) { - for (int i = 0; i < v.length; i++) - add(v[i]); + } + /// add all items from other collection + void addAll(ref Collection!(T, ownItems) v) { + for (int i = 0; i < v.length; i++) + add(v[i]); } /// support for appending (~=, +=) and removing by value (-=) ref Collection opOpAssign(string op)(T item) { @@ -146,8 +146,8 @@ struct Collection(T, bool ownItems = false) { size_t index = indexOf(value); if (index == size_t.max) return false; - T res = remove(index); - static if (ownItems) + T res = remove(index); + static if (ownItems) destroy(res); return true; } @@ -165,10 +165,10 @@ struct Collection(T, bool ownItems = false) { void clear() { static if (is(T == class) || is(T == struct)) { /// clear references - for(size_t i = 0; i < _len; i++) { - static if (ownItems) + for(size_t i = 0; i < _len; i++) { + static if (ownItems) destroy(_items[i]); - _items[i] = T.init; + _items[i] = T.init; } } _len = 0; @@ -236,98 +236,103 @@ struct Collection(T, bool ownItems = false) { } -/** object list holder, owning its objects - on destroy of holder, all own objects will be destroyed */ -struct ObjectList(T) { - protected T[] _list; - protected int _count; - /** returns count of items */ - @property int count() const { return _count; } - /** get item by index */ - T get(int index) { - assert(index >= 0 && index < _count, "child index out of range"); - return _list[index]; - } - /// get item by index - T opIndex(int index) { - return get(index); - } - /** add item to list */ - T add(T item) { - if (_list.length <= _count) // resize - _list.length = _list.length < 4 ? 4 : _list.length * 2; - _list[_count++] = item; - return item; - } - /** add item to list */ - T insert(T item, int index = -1) { - if (index > _count || index < 0) - index = _count; - if (_list.length <= _count) // resize - _list.length = _list.length < 4 ? 4 : _list.length * 2; - for (int i = _count; i > index; i--) - _list[i] = _list[i - 1]; - _list[index] = item; - _count++; - return item; - } - /** find child index for item, return -1 if not found */ - int indexOf(T item) { - if (item is null) - return -1; - for (int i = 0; i < _count; i++) - if (_list[i] == item) - return i; - return -1; - } - /** find child index for item by id, return -1 if not found */ - static if (__traits(hasMember, T, "compareId")) { - int indexOf(string id) { - for (int i = 0; i < _count; i++) - if (_list[i].compareId(id)) - return i; - return -1; - } - } - /** remove item from list, return removed item */ - T remove(int index) { - assert(index >= 0 && index < _count, "child index out of range"); - T item = _list[index]; - for (int i = index; i < _count - 1; i++) - _list[i] = _list[i + 1]; - _count--; - return item; - } - /** Replace item with another value, destroy old value. */ - void replace(T item, int index) { - T old = _list[index]; - _list[index] = item; - destroy(old); - } - /** Replace item with another value, destroy old value. */ - void replace(T newItem, T oldItem) { - int idx = indexOf(oldItem); - if (newItem is null) { - if (idx >= 0) { - T item = remove(idx); - destroy(item); - } - } else { - if (idx >= 0) - replace(newItem, idx); - else - add(newItem); - } - } - /** remove and destroy all items */ - void clear() { - for (int i = 0; i < _count; i++) { - destroy(_list[i]); - _list[i] = null; - } - _count = 0; - } - ~this() { - clear(); - } -} - +/** object list holder, owning its objects - on destroy of holder, all own objects will be destroyed */ +struct ObjectList(T) { + protected T[] _list; + protected int _count; + /** returns count of items */ + @property int count() const { return _count; } + /** get item by index */ + T get(int index) { + assert(index >= 0 && index < _count, "child index out of range"); + return _list[index]; + } + /** get const item by index */ + const(T) get(int index) const { + assert(index >= 0 && index < _count, "child index out of range"); + return _list[index]; + } + /// get item by index + T opIndex(int index) { + return get(index); + } + /** add item to list */ + T add(T item) { + if (_list.length <= _count) // resize + _list.length = _list.length < 4 ? 4 : _list.length * 2; + _list[_count++] = item; + return item; + } + /** add item to list */ + T insert(T item, int index = -1) { + if (index > _count || index < 0) + index = _count; + if (_list.length <= _count) // resize + _list.length = _list.length < 4 ? 4 : _list.length * 2; + for (int i = _count; i > index; i--) + _list[i] = _list[i - 1]; + _list[index] = item; + _count++; + return item; + } + /** find child index for item, return -1 if not found */ + int indexOf(T item) { + if (item is null) + return -1; + for (int i = 0; i < _count; i++) + if (_list[i] == item) + return i; + return -1; + } + /** find child index for item by id, return -1 if not found */ + static if (__traits(hasMember, T, "compareId")) { + int indexOf(string id) { + for (int i = 0; i < _count; i++) + if (_list[i].compareId(id)) + return i; + return -1; + } + } + /** remove item from list, return removed item */ + T remove(int index) { + assert(index >= 0 && index < _count, "child index out of range"); + T item = _list[index]; + for (int i = index; i < _count - 1; i++) + _list[i] = _list[i + 1]; + _count--; + return item; + } + /** Replace item with another value, destroy old value. */ + void replace(T item, int index) { + T old = _list[index]; + _list[index] = item; + destroy(old); + } + /** Replace item with another value, destroy old value. */ + void replace(T newItem, T oldItem) { + int idx = indexOf(oldItem); + if (newItem is null) { + if (idx >= 0) { + T item = remove(idx); + destroy(item); + } + } else { + if (idx >= 0) + replace(newItem, idx); + else + add(newItem); + } + } + /** remove and destroy all items */ + void clear() { + for (int i = 0; i < _count; i++) { + destroy(_list[i]); + _list[i] = null; + } + _count = 0; + } + ~this() { + clear(); + } +} + diff --git a/src/dlangui/core/i18n.d b/src/dlangui/core/i18n.d index bd268e44..1390ee97 100644 --- a/src/dlangui/core/i18n.d +++ b/src/dlangui/core/i18n.d @@ -146,7 +146,7 @@ struct UIStringCollection { private int _length; /** Returns number of items */ - @property int length() { return _length; } + @property int length() const { return _length; } /** Slice */ UIString[] opIndex() { return _items[0 .. _length]; @@ -160,7 +160,7 @@ struct UIStringCollection { return _items[start .. end]; } /** Read item by index */ - UIString opIndex(size_t index) { + UIString opIndex(size_t index) const { return _items[index]; } /** Modify item by index */ @@ -169,7 +169,7 @@ struct UIStringCollection { return _items[index]; } /** Return unicode string for item by index */ - dstring get(size_t index) { + dstring get(size_t index) const { return _items[index].value; } /** Assign UIStringCollection */ @@ -247,7 +247,7 @@ struct UIStringCollection { _length--; } /** Return index of first item with specified text or -1 if not found. */ - int indexOf(dstring str) { + int indexOf(dstring str) const { for (int i = 0; i < _length; i++) { if (_items[i].value.equal(str)) return i; @@ -255,7 +255,7 @@ struct UIStringCollection { return -1; } /** Return index of first item with specified string resource id or -1 if not found. */ - int indexOf(string strId) { + int indexOf(string strId) const { for (int i = 0; i < _length; i++) { if (_items[i].id.equal(strId)) return i; @@ -263,7 +263,7 @@ struct UIStringCollection { return -1; } /** Return index of first item with specified string or -1 if not found. */ - int indexOf(UIString str) { + int indexOf(UIString str) const { if (str.id !is null) return indexOf(str.id); return indexOf(str.value); diff --git a/src/dlangui/dialogs/filedlg.d b/src/dlangui/dialogs/filedlg.d index c49da7ed..889f14fe 100644 --- a/src/dlangui/dialogs/filedlg.d +++ b/src/dlangui/dialogs/filedlg.d @@ -249,7 +249,7 @@ class FileDialog : Dialog, CustomGridCellAdapter { btn.orientation = Orientation.Vertical; btn.styleId = STYLE_TRANSPARENT_BUTTON_BACKGROUND; btn.focusable = false; - adapter.widgets.add(btn); + adapter.add(btn); } res.ownAdapter = adapter; res.layoutWidth(WRAP_CONTENT).layoutHeight(FILL_PARENT).layoutWeight(0); diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d index 7349e56b..94476959 100644 --- a/src/dlangui/widgets/lists.d +++ b/src/dlangui/widgets/lists.d @@ -22,32 +22,106 @@ import dlangui.widgets.widget; import dlangui.widgets.controls; import dlangui.core.signals; +/** interface - slot for onAdapterChangeListener */ +interface OnAdapterChangeHandler { + void onAdapterChange(ListAdapter source); +} + + /// list widget adapter provides items for list widgets interface ListAdapter { /// returns number of widgets in list - @property int itemCount(); + @property int itemCount() const; /// return list item widget by item index Widget itemWidget(int index); /// return list item's state flags - uint itemState(int index); + uint itemState(int index) const; /// set one or more list item's state flags, returns updated state uint setItemState(int index, uint flags); /// reset one or more list item's state flags, returns updated state uint resetItemState(int index, uint flags); /// returns integer item id by index (if supported) - int itemId(int index); + int itemId(int index) const; /// returns string item id by index (if supported) - string itemStringId(int index); + string itemStringId(int index) const; + /// remove all items + void clear(); + + /// connect adapter change handler + ListAdapter connect(OnAdapterChangeHandler handler); + /// disconnect adapter change handler + ListAdapter disconnect(OnAdapterChangeHandler handler); } /// List adapter for simple list of widget instances -class WidgetListAdapter : ListAdapter { +class ListAdapterBase : ListAdapter { + /** Handle items change */ + protected Signal!OnAdapterChangeHandler onAdapterChangeListener; + + /// connect adapter change handler + override ListAdapter connect(OnAdapterChangeHandler handler) { + onAdapterChangeListener.connect(handler); + return this; + } + /// disconnect adapter change handler + override ListAdapter disconnect(OnAdapterChangeHandler handler) { + onAdapterChangeListener.disconnect(handler); + return this; + } + /// returns integer item id by index (if supported) + override int itemId(int index) const { + return 0; + } + /// returns string item id by index (if supported) + override string itemStringId(int index) const { + return null; + } + + /// returns number of widgets in list + override @property int itemCount() const { + // override it + return 0; + } + + /// return list item widget by item index + override Widget itemWidget(int index) { + // override it + return null; + } + + /// return list item's state flags + override uint itemState(int index) const { + // override it + return State.Enabled; + } + /// set one or more list item's state flags, returns updated state + override uint setItemState(int index, uint flags) { + return 0; + } + /// reset one or more list item's state flags, returns updated state + override uint resetItemState(int index, uint flags) { + return 0; + } + + /// remove all items + override void clear() { + } + + /// notify listeners about list items changes + void updateViews() { + if (onAdapterChangeListener.assigned) + onAdapterChangeListener.emit(this); + } +} + +/// List adapter for simple list of widget instances +class WidgetListAdapter : ListAdapterBase { private WidgetList _widgets; /// list of widgets to display - @property ref WidgetList widgets() { return _widgets; } + @property ref const(WidgetList) widgets() { return _widgets; } /// returns number of widgets in list - @property override int itemCount() { + @property override int itemCount() const { return _widgets.count; } /// return list item widget by item index @@ -55,7 +129,7 @@ class WidgetListAdapter : ListAdapter { return _widgets.get(index); } /// return list item's state flags - override uint itemState(int index) { + override uint itemState(int index) const { return _widgets.get(index).state; } /// set one or more list item's state flags, returns updated state @@ -66,13 +140,23 @@ class WidgetListAdapter : ListAdapter { override uint resetItemState(int index, uint flags) { return _widgets.get(index).resetState(flags).state; } - /// returns integer item id by index (if supported) - override int itemId(int index) { - return 0; + /// add item + WidgetListAdapter add(Widget item, int index = -1) { + _widgets.insert(item, index); + updateViews(); + return this; } - /// returns string item id by index (if supported) - override string itemStringId(int index) { - return null; + /// remove item + WidgetListAdapter remove(int index) { + auto item = _widgets.remove(index); + destroy(item); + updateViews(); + return this; + } + /// remove all items + override void clear() { + _widgets.clear(); + updateViews(); } ~this() { //Log.d("Destroying WidgetListAdapter"); @@ -104,7 +188,7 @@ struct StringListValue { /** List adapter providing strings only. */ -class StringListAdapter : ListAdapter { +class StringListAdapter : ListAdapterBase { protected UIStringCollection _items; protected uint[] _states; protected int[] _intIds; @@ -148,21 +232,88 @@ class StringListAdapter : ListAdapter { updateStatesLength(); } + /// remove all items + override void clear() { + _items.clear(); + updateStatesLength(); + updateViews(); + } + + /// remove item by index + StringListAdapter remove(int index) { + if (index < 0 || index >= _items.length) + return this; + for (int i = 0; i < _items.length - 1; i++) { + _intIds[i] = _intIds[i + 1]; + _stringIds[i] = _stringIds[i + 1]; + _states[i] = _states[i + 1]; + } + _items.remove(index); + _intIds.length = items.length; + _states.length = _items.length; + _stringIds.length = items.length; + updateViews(); + return this; + } + + /// add new item + StringListAdapter add(UIString item, int index = -1) { + if (index < 0 || index > _items.length) + index = _items.length; + _items.add(item, index); + _intIds.length = items.length; + _states.length = _items.length; + _stringIds.length = items.length; + for (int i = _items.length - 1; i > index; i--) { + _intIds[i] = _intIds[i - 1]; + _stringIds[i] = _stringIds[i - 1]; + _states[i] = _states[i - 1]; + } + _intIds[index] = 0; + _stringIds[index] = null; + _states[index] = State.Enabled; + updateViews(); + return this; + } + /// add new string resource item + StringListAdapter add(string item, int index = -1) { + return add(UIString(item), index); + } + /// add new raw dstring item + StringListAdapter add(dstring item, int index = -1) { + return add(UIString(item), index); + } + /** Access to items collection. */ - @property ref UIStringCollection items() { return _items; } + @property ref const(UIStringCollection) items() { return _items; } + + /** Replace items collection. */ + @property StringListAdapter items(dstring[] values) { + _items = values; + _intIds.length = items.length; + _states.length = _items.length; + _stringIds.length = items.length; + for (int i = 0; i < _items.length; i++) { + _intIds[i] = 0; + _stringIds[i] = null; + _states[i] = State.Enabled; + } + updateViews(); + return this; + } /// returns number of widgets in list - @property override int itemCount() { + @property override int itemCount() const { return _items.length; } /// returns integer item id by index (if supported) - override int itemId(int index) { + override int itemId(int index) const { return index >= 0 && index < _intIds.length ? _intIds[index] : 0; } /// returns string item id by index (if supported) - override string itemStringId(int index) { + override string itemStringId(int index) const { return index >= 0 && index < _stringIds.length ? _stringIds[index] : null; } @@ -197,8 +348,9 @@ class StringListAdapter : ListAdapter { } /// return list item's state flags - override uint itemState(int index) { - updateStatesLength(); + override uint itemState(int index) const { + if (index < 0 || index >= _items.length) + return 0; return _states[index]; } @@ -238,7 +390,7 @@ interface OnItemClickHandler { /** List widget - shows content as hori*/ -class ListWidget : WidgetGroup, OnScrollHandler { +class ListWidget : WidgetGroup, OnScrollHandler, OnAdapterChangeHandler { /** Handle selection change. */ Signal!OnItemSelectedHandler onItemSelectedListener; @@ -326,20 +478,32 @@ class ListWidget : WidgetGroup, OnScrollHandler { @property ListAdapter adapter() { return _adapter; } /// set adapter @property ListWidget adapter(ListAdapter adapter) { + if (_adapter is adapter) + return this; // no changes + if (_adapter) + _adapter.disconnect(this); if (_adapter !is null && _ownAdapter) destroy(_adapter); _adapter = adapter; + if (_adapter) + _adapter.connect(this); _ownAdapter = false; - onAdapterChanged(); + onAdapterChange(_adapter); return this; } /// set adapter, which will be owned by list (destroy will be called for adapter on widget destroy) @property ListWidget ownAdapter(ListAdapter adapter) { + if (_adapter is adapter) + return this; // no changes + if (_adapter) + _adapter.disconnect(this); if (_adapter !is null && _ownAdapter) destroy(_adapter); _adapter = adapter; + if (_adapter) + _adapter.connect(this); _ownAdapter = true; - onAdapterChanged(); + onAdapterChange(_adapter); return this; } @@ -364,10 +528,6 @@ class ListWidget : WidgetGroup, OnScrollHandler { return false; } - void onAdapterChanged() { - requestLayout(); - } - /// empty parameter list constructor - for usage by factory this() { this(null); @@ -399,6 +559,11 @@ class ListWidget : WidgetGroup, OnScrollHandler { } } + /// item list is changed + override void onAdapterChange(ListAdapter source) { + requestLayout(); + } + /// override to handle change of selection protected void selectionChanged(int index, int previouslySelectedItem = -1) { if (onItemSelectedListener.assigned) @@ -555,6 +720,8 @@ class ListWidget : WidgetGroup, OnScrollHandler { } ~this() { + if (_adapter) + _adapter.disconnect(this); //Log.d("Destroying List ", _id); if (_adapter !is null && _ownAdapter) destroy(_adapter); diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d index 0a9442bd..7f7b2c01 100644 --- a/src/dlangui/widgets/menu.d +++ b/src/dlangui/widgets/menu.d @@ -438,7 +438,7 @@ class MenuWidgetBase : ListWidget { if (orientation == Orientation.Horizontal) widget.styleId = STYLE_MAIN_MENU_ITEM; widget.parent = this; - adapter.widgets.add(widget); + adapter.add(widget); } ownAdapter = adapter; }