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