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();
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);
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}