1 /// This module defines the `Children` struct which will catch mutations to it made while drawing, and defines utils 2 /// for operating on children. 3 module glui.children; 4 5 import std.range; 6 7 import glui.node; 8 9 @safe pure: 10 11 debug struct Children { 12 13 private enum mutateError = "Cannot mutate children list while its being rendered. This should be done in an event " 14 ~ "handler, such as the mouseImpl/keyboardImpl methods."; 15 16 private { 17 18 GluiNode[] _children; 19 bool _locked; 20 21 } 22 23 @safe: 24 25 this(inout(Children) old) inout { 26 27 this._children = old._children; 28 this._locked = false; 29 30 } 31 32 @property { 33 34 size_t length() const { 35 36 return _children.length; 37 38 } 39 40 size_t length(size_t value) { 41 42 debug assert(!_locked, mutateError); 43 44 return _children.length = value; 45 46 } 47 48 } 49 50 @property 51 bool empty() const { 52 53 return _children.length == 0; 54 55 } 56 57 /// Remove the first item. 58 void popFront() { 59 60 debug assert(!_locked, mutateError); 61 assert(!empty, "Can't pop an empty children list"); 62 63 _children.popFront(); 64 65 } 66 67 /// Get the first child. 68 ref inout(GluiNode) front() inout { 69 70 assert(!empty, "Can't get the first item of an empty children list"); 71 72 return _children[0]; 73 74 } 75 76 void opAssign(GluiNode[] newList) { 77 78 debug assert(!_locked, mutateError); 79 _children = newList; 80 81 } 82 83 // Indexing and slicing is allowed 84 GluiNode[] opIndex() { 85 86 return _children[]; 87 88 } 89 auto opIndex(Args...)(Args args) { 90 91 return _children[args]; 92 93 } 94 95 @property 96 size_t opDollar() const { 97 98 return _children.length; 99 100 } 101 102 ref GluiNode[] getChildren() return { 103 104 debug assert(!_locked, "Can't get a mutable reference to children while rendering. Consider doing this in " 105 ~ "input handling methods like mouseImpl/keyboardImpl which happen after rendering is complete. But if " 106 ~ "this is necessary, you may use `glui.children.asConst` instead. Note, iterating over the (mutable) " 107 ~ "children is still legal. You can also use `node.remove` if you want to simply remove a node."); 108 return _children; 109 110 } 111 112 int opApply(int delegate(GluiNode node) @trusted dg) { 113 114 foreach (child; _children) { 115 116 if (auto result = dg(child)) return result; 117 118 } 119 120 return 0; 121 122 } 123 124 int opApply(int delegate(size_t index, GluiNode node) @trusted dg) { 125 126 foreach (i, child; _children) { 127 128 if (auto result = dg(i, child)) return result; 129 130 } 131 132 return 0; 133 134 } 135 136 alias getChildren this; 137 138 } 139 140 else alias Children = GluiNode[]; 141 142 static assert(isInputRange!Children); 143 144 145 pragma(inline) 146 void lock(ref Children children) { 147 148 debug children._locked = true; 149 150 assertLocked(children); 151 152 } 153 154 pragma(inline) 155 void unlock(ref Children children) { 156 157 debug { 158 159 assert(children._locked, "Already unlocked."); 160 161 children._locked = false; 162 163 } 164 165 } 166 167 pragma(inline) 168 void assertLocked(ref Children children) { 169 170 // debug just to de sure lol 171 // pretty sure you can fiddle with the compiler flags enough to make this not compile 172 debug assert(children._locked); 173 174 } 175 176 /// Get the children list as const. 177 pragma(inline) 178 const(GluiNode[]) asConst(Children children) { 179 180 debug return children._children; 181 else return children; 182 183 } 184 185 /// Get a reference to the children list forcefully, ignoring the lock. 186 pragma(inline) 187 ref GluiNode[] forceMutable(return ref Children children) @system { 188 189 debug return children._children; 190 else return children; 191 192 } 193 194 /// Get a reference to child within the parent. 195 /// 196 /// This will probably be soon deprecated in favor of a "placeholder" node to fulfill this purpose. 197 ref GluiNode childRef(ref Children children, size_t index) @system { 198 199 debug assert(!children._locked, "Can't get reference to a locked child."); 200 201 debug return children._children[index]; 202 else return children[index]; 203 204 }