list adapters: support adding/removing items, fix issue #80

This commit is contained in:
Vadim Lopatin 2015-03-31 13:06:14 +03:00
parent 433263133c
commit b28a1e95b7
6 changed files with 373 additions and 188 deletions

View File

@ -467,12 +467,12 @@ extern (C) int UIAppMain(string[] args) {
StringListAdapter stringList = new StringListAdapter(); StringListAdapter stringList = new StringListAdapter();
WidgetListAdapter listAdapter = new WidgetListAdapter(); WidgetListAdapter listAdapter = new WidgetListAdapter();
listAdapter.widgets.add((new TextWidget()).text("This is a list of widgets"d).styleId("LIST_ITEM")); listAdapter.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); stringList.add("This is a list of strings from StringListAdapter"d);
for (int i = 1; i < 1000; i++) { for (int i = 1; i < 1000; i++) {
dstring label = "List item "d ~ to!dstring(i); dstring label = "List item "d ~ to!dstring(i);
listAdapter.widgets.add((new TextWidget()).text("Widget list - "d ~ label).styleId("LIST_ITEM")); listAdapter.add((new TextWidget()).text("Widget list - "d ~ label).styleId("LIST_ITEM"));
stringList.items.add("Simple string - "d ~ label); stringList.add("Simple string - "d ~ label);
} }
list.ownAdapter = listAdapter; list.ownAdapter = listAdapter;
listAdapter.resetItemState(0, State.Enabled); listAdapter.resetItemState(0, State.Enabled);
@ -492,6 +492,19 @@ extern (C) int UIAppMain(string[] args) {
list2.selectItem(0); list2.selectItem(0);
longLists.addChild(list2); 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); tabs.addTab(longLists, "TAB_LONG_LIST"c);
} }

View File

@ -247,6 +247,11 @@ struct ObjectList(T) {
assert(index >= 0 && index < _count, "child index out of range"); assert(index >= 0 && index < _count, "child index out of range");
return _list[index]; 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 /// get item by index
T opIndex(int index) { T opIndex(int index) {
return get(index); return get(index);

View File

@ -146,7 +146,7 @@ struct UIStringCollection {
private int _length; private int _length;
/** Returns number of items */ /** Returns number of items */
@property int length() { return _length; } @property int length() const { return _length; }
/** Slice */ /** Slice */
UIString[] opIndex() { UIString[] opIndex() {
return _items[0 .. _length]; return _items[0 .. _length];
@ -160,7 +160,7 @@ struct UIStringCollection {
return _items[start .. end]; return _items[start .. end];
} }
/** Read item by index */ /** Read item by index */
UIString opIndex(size_t index) { UIString opIndex(size_t index) const {
return _items[index]; return _items[index];
} }
/** Modify item by index */ /** Modify item by index */
@ -169,7 +169,7 @@ struct UIStringCollection {
return _items[index]; return _items[index];
} }
/** Return unicode string for item by index */ /** Return unicode string for item by index */
dstring get(size_t index) { dstring get(size_t index) const {
return _items[index].value; return _items[index].value;
} }
/** Assign UIStringCollection */ /** Assign UIStringCollection */
@ -247,7 +247,7 @@ struct UIStringCollection {
_length--; _length--;
} }
/** Return index of first item with specified text or -1 if not found. */ /** 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++) { for (int i = 0; i < _length; i++) {
if (_items[i].value.equal(str)) if (_items[i].value.equal(str))
return i; return i;
@ -255,7 +255,7 @@ struct UIStringCollection {
return -1; return -1;
} }
/** Return index of first item with specified string resource id or -1 if not found. */ /** 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++) { for (int i = 0; i < _length; i++) {
if (_items[i].id.equal(strId)) if (_items[i].id.equal(strId))
return i; return i;
@ -263,7 +263,7 @@ struct UIStringCollection {
return -1; return -1;
} }
/** Return index of first item with specified string or -1 if not found. */ /** 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) if (str.id !is null)
return indexOf(str.id); return indexOf(str.id);
return indexOf(str.value); return indexOf(str.value);

View File

@ -249,7 +249,7 @@ class FileDialog : Dialog, CustomGridCellAdapter {
btn.orientation = Orientation.Vertical; btn.orientation = Orientation.Vertical;
btn.styleId = STYLE_TRANSPARENT_BUTTON_BACKGROUND; btn.styleId = STYLE_TRANSPARENT_BUTTON_BACKGROUND;
btn.focusable = false; btn.focusable = false;
adapter.widgets.add(btn); adapter.add(btn);
} }
res.ownAdapter = adapter; res.ownAdapter = adapter;
res.layoutWidth(WRAP_CONTENT).layoutHeight(FILL_PARENT).layoutWeight(0); res.layoutWidth(WRAP_CONTENT).layoutHeight(FILL_PARENT).layoutWeight(0);

View File

@ -22,32 +22,106 @@ import dlangui.widgets.widget;
import dlangui.widgets.controls; import dlangui.widgets.controls;
import dlangui.core.signals; import dlangui.core.signals;
/** interface - slot for onAdapterChangeListener */
interface OnAdapterChangeHandler {
void onAdapterChange(ListAdapter source);
}
/// list widget adapter provides items for list widgets /// list widget adapter provides items for list widgets
interface ListAdapter { interface ListAdapter {
/// returns number of widgets in list /// returns number of widgets in list
@property int itemCount(); @property int itemCount() const;
/// return list item widget by item index /// return list item widget by item index
Widget itemWidget(int index); Widget itemWidget(int index);
/// return list item's state flags /// 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 /// set one or more list item's state flags, returns updated state
uint setItemState(int index, uint flags); uint setItemState(int index, uint flags);
/// reset one or more list item's state flags, returns updated state /// reset one or more list item's state flags, returns updated state
uint resetItemState(int index, uint flags); uint resetItemState(int index, uint flags);
/// returns integer item id by index (if supported) /// 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) /// 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 /// 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; private WidgetList _widgets;
/// list of widgets to display /// list of widgets to display
@property ref WidgetList widgets() { return _widgets; } @property ref const(WidgetList) widgets() { return _widgets; }
/// returns number of widgets in list /// returns number of widgets in list
@property override int itemCount() { @property override int itemCount() const {
return _widgets.count; return _widgets.count;
} }
/// return list item widget by item index /// return list item widget by item index
@ -55,7 +129,7 @@ class WidgetListAdapter : ListAdapter {
return _widgets.get(index); return _widgets.get(index);
} }
/// return list item's state flags /// return list item's state flags
override uint itemState(int index) { override uint itemState(int index) const {
return _widgets.get(index).state; return _widgets.get(index).state;
} }
/// set one or more list item's state flags, returns updated 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) { override uint resetItemState(int index, uint flags) {
return _widgets.get(index).resetState(flags).state; return _widgets.get(index).resetState(flags).state;
} }
/// returns integer item id by index (if supported) /// add item
override int itemId(int index) { WidgetListAdapter add(Widget item, int index = -1) {
return 0; _widgets.insert(item, index);
updateViews();
return this;
} }
/// returns string item id by index (if supported) /// remove item
override string itemStringId(int index) { WidgetListAdapter remove(int index) {
return null; auto item = _widgets.remove(index);
destroy(item);
updateViews();
return this;
}
/// remove all items
override void clear() {
_widgets.clear();
updateViews();
} }
~this() { ~this() {
//Log.d("Destroying WidgetListAdapter"); //Log.d("Destroying WidgetListAdapter");
@ -104,7 +188,7 @@ struct StringListValue {
/** List adapter providing strings only. */ /** List adapter providing strings only. */
class StringListAdapter : ListAdapter { class StringListAdapter : ListAdapterBase {
protected UIStringCollection _items; protected UIStringCollection _items;
protected uint[] _states; protected uint[] _states;
protected int[] _intIds; protected int[] _intIds;
@ -148,21 +232,88 @@ class StringListAdapter : ListAdapter {
updateStatesLength(); 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. */ /** 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 /// returns number of widgets in list
@property override int itemCount() { @property override int itemCount() const {
return _items.length; return _items.length;
} }
/// returns integer item id by index (if supported) /// 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; return index >= 0 && index < _intIds.length ? _intIds[index] : 0;
} }
/// returns string item id by index (if supported) /// 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; return index >= 0 && index < _stringIds.length ? _stringIds[index] : null;
} }
@ -197,8 +348,9 @@ class StringListAdapter : ListAdapter {
} }
/// return list item's state flags /// return list item's state flags
override uint itemState(int index) { override uint itemState(int index) const {
updateStatesLength(); if (index < 0 || index >= _items.length)
return 0;
return _states[index]; return _states[index];
} }
@ -238,7 +390,7 @@ interface OnItemClickHandler {
/** List widget - shows content as hori*/ /** List widget - shows content as hori*/
class ListWidget : WidgetGroup, OnScrollHandler { class ListWidget : WidgetGroup, OnScrollHandler, OnAdapterChangeHandler {
/** Handle selection change. */ /** Handle selection change. */
Signal!OnItemSelectedHandler onItemSelectedListener; Signal!OnItemSelectedHandler onItemSelectedListener;
@ -326,20 +478,32 @@ class ListWidget : WidgetGroup, OnScrollHandler {
@property ListAdapter adapter() { return _adapter; } @property ListAdapter adapter() { return _adapter; }
/// set adapter /// set adapter
@property ListWidget adapter(ListAdapter adapter) { @property ListWidget adapter(ListAdapter adapter) {
if (_adapter is adapter)
return this; // no changes
if (_adapter)
_adapter.disconnect(this);
if (_adapter !is null && _ownAdapter) if (_adapter !is null && _ownAdapter)
destroy(_adapter); destroy(_adapter);
_adapter = adapter; _adapter = adapter;
if (_adapter)
_adapter.connect(this);
_ownAdapter = false; _ownAdapter = false;
onAdapterChanged(); onAdapterChange(_adapter);
return this; return this;
} }
/// set adapter, which will be owned by list (destroy will be called for adapter on widget destroy) /// set adapter, which will be owned by list (destroy will be called for adapter on widget destroy)
@property ListWidget ownAdapter(ListAdapter adapter) { @property ListWidget ownAdapter(ListAdapter adapter) {
if (_adapter is adapter)
return this; // no changes
if (_adapter)
_adapter.disconnect(this);
if (_adapter !is null && _ownAdapter) if (_adapter !is null && _ownAdapter)
destroy(_adapter); destroy(_adapter);
_adapter = adapter; _adapter = adapter;
if (_adapter)
_adapter.connect(this);
_ownAdapter = true; _ownAdapter = true;
onAdapterChanged(); onAdapterChange(_adapter);
return this; return this;
} }
@ -364,10 +528,6 @@ class ListWidget : WidgetGroup, OnScrollHandler {
return false; return false;
} }
void onAdapterChanged() {
requestLayout();
}
/// empty parameter list constructor - for usage by factory /// empty parameter list constructor - for usage by factory
this() { this() {
this(null); 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 /// override to handle change of selection
protected void selectionChanged(int index, int previouslySelectedItem = -1) { protected void selectionChanged(int index, int previouslySelectedItem = -1) {
if (onItemSelectedListener.assigned) if (onItemSelectedListener.assigned)
@ -555,6 +720,8 @@ class ListWidget : WidgetGroup, OnScrollHandler {
} }
~this() { ~this() {
if (_adapter)
_adapter.disconnect(this);
//Log.d("Destroying List ", _id); //Log.d("Destroying List ", _id);
if (_adapter !is null && _ownAdapter) if (_adapter !is null && _ownAdapter)
destroy(_adapter); destroy(_adapter);

View File

@ -438,7 +438,7 @@ class MenuWidgetBase : ListWidget {
if (orientation == Orientation.Horizontal) if (orientation == Orientation.Horizontal)
widget.styleId = STYLE_MAIN_MENU_ITEM; widget.styleId = STYLE_MAIN_MENU_ITEM;
widget.parent = this; widget.parent = this;
adapter.widgets.add(widget); adapter.add(widget);
} }
ownAdapter = adapter; ownAdapter = adapter;
} }