From c18f9ee89034a7220436465a3ba9ccf907f0257a Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Wed, 16 Apr 2014 12:26:33 +0400 Subject: [PATCH] signals support --- dlanguilib.visualdproj | 2 + src/dlangui/core/collections.d | 121 ++++++++++++++++++++++++ src/dlangui/core/signals.d | 167 +++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 src/dlangui/core/collections.d create mode 100644 src/dlangui/core/signals.d diff --git a/dlanguilib.visualdproj b/dlanguilib.visualdproj index 61c775da..2d9c732d 100644 --- a/dlanguilib.visualdproj +++ b/dlanguilib.visualdproj @@ -299,10 +299,12 @@ + + diff --git a/src/dlangui/core/collections.d b/src/dlangui/core/collections.d new file mode 100644 index 00000000..99642945 --- /dev/null +++ b/src/dlangui/core/collections.d @@ -0,0 +1,121 @@ +module dlangui.core.collections; + +import std.algorithm; + +/// array based collection of items +/// retains item order when during add/remove operations +struct Collection(T) { + private T[] _items; + private size_t _len; + /// returns number of items in collection + @property size_t length() { return _len; } + /// returns currently allocated capacity (may be more than length) + @property size_t size() { return _items.length; } + /// change capacity (e.g. to reserve big space to avoid multiple reallocations) + @property void size(size_t newSize) { + if (_len > newSize) + length = newSize; // shrink + _items.length = newSize; + } + /// returns number of items in collection + @property void length(size_t newSize) { + if (newSize < _len) { + // shrink + static if (is(T == class) || is(T == struct)) { + // clear items + for (size_t i = newSize; i < _len; i++) + _items[i] = T.init; + } + } else if (newSize > _len) { + // expand + if (_items.length < newSize) + _items.length = newSize; + } + _len = newSize; + } + /// access item by index + ref T opIndex(size_t index) { + assert(index < _len); + return _items[index]; + } + /// insert new item in specified position + void add(T item, size_t index = size_t.max) { + if (index > _len) + index = _len; + if (_items.length >= _len) { + if (_items.length < 4) + _items.length = 4; + else + _items.length = _items.length * 2; + } + if (index < _len) { + for (size_t i = _len; i > index; i--) + _items[i] = _items[i - 1]; + } + _items[index] = item; + _len++; + } + /// support for appending (~=, +=) and removing by value (-=) + ref Collection opOpAssign(string op)(T item) { + static if (op.equal("~") || op.equal("+")) { + // append item to end of collection + add(item); + return this; + } else if (op.equal("-")) { + // remove item from collection, if present + removeValue(item); + return this; + } else { + assert(false, "not supported opOpAssign " ~ op); + } + } + /// returns index of first occurence of item, size_t.max if not found + size_t indexOf(T item) { + for (size_t i = 0; i < _len; i++) + if (_items[i] == item) + return i; + return size_t.max; + } + /// remove single item, returning removed item + T remove(size_t index) { + assert(index < _len); + T result = _items[index]; + for (size_t i = index; i + 1 < _len; i++) + _items[i] = _items[i + 1]; + _items[_len] = T.init; + _len--; + return result; + } + /// remove single item by value - if present in collection, returning true if item was found and removed + bool removeValue(T value) { + size_t index = indexOf(value); + if (index == size_t.max) + return false; + remove(index); + return true; + } + /// support of foreach with reference + int opApply(int delegate(ref T param) op) { + int result = 0; + for (size_t i = 0; i < _len; i++) { + result = op(_items[i]); + if (result) + break; + } + return result; + } + /// remove all items + void clear() { + static if (is(T == class) || is(T == struct)) { + /// clear references + for(size_t i = 0; i < _len; i++) + _items[i] = T.init; + } + _len = 0; + _items = null; + } + ~this() { + clear(); + } +} + diff --git a/src/dlangui/core/signals.d b/src/dlangui/core/signals.d new file mode 100644 index 00000000..fb3a801f --- /dev/null +++ b/src/dlangui/core/signals.d @@ -0,0 +1,167 @@ +module dlangui.core.signals; + +import std.traits; +import dlangui.core.collections; + +/// parameter is interface with single method +struct Listener(T1) if (is(T1 == interface) && __traits(allMembers, T1).length == 1) { + alias return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0])); + alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0])); + alias slot_t = return_t delegate(params_t); + private slot_t _listener; + /// returns true if listener is assigned + final bool assigned() { + return _listener !is null; + } + /// assign delegate + final void opAssign(slot_t listenerDelegate) { + _listener = listenerDelegate; + } + /// assign object implementing interface + final void opAssign(T1 listenerObject) { + _listener = &(__traits(getMember, listenerObject, __traits(allMembers, T1)[0])); + } + final return_t opCall(params_t params) { + static if (is(return_t == void)) { + if (_listener !is null) + _listener(params); + } else { + if (_listener !is null) + return _listener(params); + return return_t.init; + } + } + final slot_t get() { + return _listener; + } + alias get this; +} + +/// implicitly specified return and parameter types +struct Listener(RETURN_T, T1...) +{ + alias slot_t = RETURN_T delegate(T1); + private slot_t _listener; + /// returns true if listener is assigned + final bool assigned() { + return _listener !is null; + } + final void opAssign(slot_t listener) { + _listener = listener; + } + final RETURN_T opCall(T1 params) { + static if (is (RETURN_T == void)) { + if (_listener !is null) + _listener(params); + } else { + if (_listener !is null) + return _listener(params); + return RETURN_T.init; + } + } + final slot_t get() { + return _listener; + } + alias get this; +} + +/// implicitly specified return and parameter types +struct Signal(T1) if (is(T1 == interface) && __traits(allMembers, T1).length == 1) { + alias return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0])); + alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0])); + alias slot_t = return_t delegate(params_t); + private Collection!slot_t _listeners; + /// returns true if listener is assigned + final bool assigned() { + return _listeners.length > 0; + } + final void opAssign(slot_t listener) { + _listeners.clear(); + _listeners ~= listener; + } + /// call all listeners; for signals having non-void return type, stop iterating when first return value is nonzero + static if (is (return_t == void)) { + // call all listeners + final return_t opCall(params_t params) { + foreach(listener; _listeners) + listener(params); + } + // call all listeners + final return_t emit(params_t params) { + foreach(listener; _listeners) + listener(params); + } + } else { + // call listeners, stop calling on first non-zero -- if (res) return res + final return_t opCall(params_t params) { + return emit(params); + } + // call listeners, stop calling on first non-zero -- if (res) return res + final return_t emit(params_t params) { + foreach(listener; _listeners) { + return_t res = listener(params); + if (res) + return res; + } + return return_t.init; + } + } + /// add listener + final void connect(slot_t listener) { + _listeners ~= listener; + } + /// remove listener + final void disconnect(slot_t listener) { + _listeners -= listener; + } +} + +/// implicitly specified return and parameter types +struct Signal(RETURN_T, T1...) +{ + alias slot_t = RETURN_T delegate(T1); + private Collection!slot_t _listeners; + /// returns true if listener is assigned + final bool assigned() { + return _listeners.length > 0; + } + final void opAssign(slot_t listener) { + _listeners.clear(); + _listeners ~= listener; + } + /// call all listeners; for signals having non-void return type, stop iterating when first return value is nonzero + static if (is (RETURN_T == void)) { + // call all listeners + final RETURN_T opCall(T1 params) { + foreach(listener; _listeners) + listener(params); + } + // call all listeners + final RETURN_T emit(T1 params) { + foreach(listener; _listeners) + listener(params); + } + } else { + // call listeners, stop calling on first non-zero -- if (res) return res + final RETURN_T opCall(T1 params) { + return emit(params); + } + // call listeners, stop calling on first non-zero -- if (res) return res + final RETURN_T emit(T1 params) { + foreach(listener; _listeners) { + RETURN_T res = listener(params); + if (res) + return res; + } + return RETURN_T.init; + } + } + /// add listener + final void connect(slot_t listener) { + _listeners ~= listener; + } + /// remove listener + final void disconnect(slot_t listener) { + _listeners -= listener; + } +}