1 module spasm.array; 2 3 import spasm.event; 4 import spasm.node; 5 import spasm.ct; 6 import spasm.node; 7 import spasm.dom; 8 import spasm.types; 9 import std.range : only; 10 import std.algorithm : joiner; 11 import spasm.rt.array; 12 13 nothrow: 14 @safe: 15 16 template extractEventPaths(T, Ts...) { 17 import std.meta : staticMap, AliasSeq; 18 import std.traits : getSymbolsByUDA; 19 alias events = getSymbolsByUDA!(T, eventemitter); 20 alias children = getChildren!T; 21 template recur(string NextT) { 22 alias recur = extractEventPaths!(typeof(__traits(getMember, T, NextT)), AliasSeq!(Ts, NextT)); 23 } 24 template prefixNames(string Event) { 25 import spasm.ct : tuple; 26 enum prefixNames = tuple(Ts, Event); 27 } 28 alias eventNames = staticMap!(getName, AliasSeq!(events)); 29 alias prefixed = staticMap!(prefixNames, eventNames); 30 alias extractEventPaths = AliasSeq!(prefixed,staticMap!(recur, getChildren!T)); 31 } 32 33 template join(string delimiter, elems...) { 34 static if (elems.length == 0) 35 enum join = ""; 36 else static if (elems.length == 1) 37 enum join = elems[0]; 38 else 39 enum join = elems[0] ~ delimiter ~ join!(delimiter, elems[1..$]); 40 } 41 42 mixin template ArrayItemEvents(T) { 43 static foreach(path; extractEventPaths!(T)) { 44 mixin Slot!(join!("_",path.expand)); 45 mixin("void __" ~ join!("_", path.expand) ~ "(size_t addr) { " ~ join!("_", path.expand) ~ ".emit(this.getIndexInArray(addr));}"); 46 } 47 } 48 49 void assignEventListeners(T)(ref Array!T arr, ref T item) { 50 alias eventPaths = extractEventPaths!(T); 51 static foreach(path; eventPaths) { 52 mixin("item." ~ join!(".", path.expand) ~ ".add(&arr.__" ~ join!("_", path.expand) ~ ");"); 53 } 54 } 55 56 auto getIndexInArray(T)(auto ref Array!T arr, size_t ptr) { 57 import std.algorithm : countUntil; 58 return arr[].countUntil!((ref T* item) { 59 auto baseAddr = cast(size_t)(cast(void*)item); 60 return baseAddr <= ptr && (baseAddr + T.sizeof) > ptr; 61 }); 62 } 63 64 struct Array(T) { 65 @child DynamicArray!(T*) appender; 66 mixin ArrayItemEvents!T; 67 alias appender this; 68 void put(T* t) { 69 this.assignEventListeners(*t); 70 appender.put(t); 71 } 72 } 73 74 struct Updater(T) { 75 private { 76 T* list; 77 size_t idx; 78 } 79 this(T* list) { 80 this.list=list; 81 foreach(i; list.items) { 82 i.node.marked = true; 83 } 84 } 85 ~this() { 86 if (idx < list.items.length) 87 foreach (i; list.items[idx .. $]) { 88 if (i.node.marked) 89 unmount(*i); 90 } 91 list.items.shrinkTo(idx); 92 } 93 void put(Item)(Item* t) { 94 size_t i = idx++; 95 t.node.marked = false; 96 if (list.items.length > i) { 97 if (list.items[i] == t) { 98 return; 99 } 100 // TODO: here we can use replaceChild 101 if (list.items[i].node.marked) { 102 unmount(*list.items[i]); 103 } 104 if (idx+1 < list.items.length) { 105 if (list.items[i+1] == t) { 106 list.items.remove(i); 107 return; 108 } else { 109 list.items[i] = t; 110 list.items.assignEventListeners(*t); 111 if (list.items[i+1].node.marked) 112 list.node.renderBefore((*list.items[i]), list.items[i+1].node); 113 else { 114 size_t off = 2; 115 while (i+off < list.items.length) { 116 if (list.items[i+off].node.marked) { 117 list.node.renderBefore((*list.items[i]), list.items[i+off].node); 118 return; 119 } 120 off+=1; 121 } 122 list.node.render(*list.items[i]); 123 return; 124 } 125 } 126 } else { 127 list.items[i] = t; 128 list.items.assignEventListeners(*t); 129 list.node.render(*list.items[i]); 130 } 131 } else { 132 list.put(t); 133 } 134 } 135 } 136 137 import std.traits : hasMember, isPointer; 138 bool removeItem(T)(ref T app, size_t idx) { //}if (hasMember!(T, "remove")) { 139 app.remove(idx); 140 return true; 141 } 142 143 bool removeItem(T,U)(ref T app, ref U t) if (isPointer!(U)) { 144 import std.algorithm : countUntil, remove; 145 auto idx = app[0..$].countUntil(t); 146 if (idx == -1) 147 return false; 148 return app.removeItem(idx); 149 } 150 151 struct List(T, string tag) { 152 mixin Node!tag; 153 @child Array!(T) items; 154 void put(T* t) { 155 items.put(t); 156 spasm.dom.render(node,*items[$-1]); 157 } 158 void shrinkTo(size_t size) { 159 if (size < items.length) 160 foreach(i; items[size..$]) { 161 unmount(*i); 162 } 163 items.shrinkTo(size); 164 } 165 void remove(size_t idx) { 166 .removeChild(items.appender[idx]); 167 import std.algorithm : remove; 168 items.appender.removeItem(idx); 169 } 170 } 171 172 alias UnorderedList(T) = List!(T,"ul");