1 /// This module defines the `Children` struct which will catch mutations made to it 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 _isLocked; 20 21 } 22 23 @safe: 24 25 this(inout(Children) old) inout { 26 27 this._children = old._children; 28 this._isLocked = 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(!_isLocked, 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(!_isLocked, 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(!_isLocked, mutateError); 79 _children = newList; 80 81 } 82 83 // Indexing and slicing is allowed 84 inout(GluiNode[]) opIndex() inout { 85 86 return _children[]; 87 88 } 89 ref auto opIndex(Args...)(Args args) inout { 90 91 return _children[args]; 92 93 } 94 95 @property 96 size_t opDollar() const { 97 98 return _children.length; 99 100 } 101 102 deprecated("getChildren will be removed in 0.6.0, please use Children directly, as if it was an array") 103 ref GluiNode[] getChildren() return { 104 105 debug assert(!_isLocked, "Can't get a mutable reference to children while rendering. Consider doing this in " 106 ~ "input handling methods like mouseImpl/keyboardImpl which happen after rendering is complete. But if " 107 ~ "this is necessary, you may use `glui.children.asConst` instead. Note, iterating over the (mutable) " 108 ~ "children is still legal. You can also use `node.remove` if you want to simply remove a node."); 109 return _children; 110 111 } 112 113 int opApply(scope int delegate(GluiNode node) @trusted dg) { 114 115 foreach (child; _children) { 116 117 if (auto result = dg(child)) return result; 118 119 } 120 121 return 0; 122 123 } 124 125 int opApply(scope int delegate(size_t index, GluiNode node) @trusted dg) { 126 127 foreach (i, child; _children) { 128 129 if (auto result = dg(i, child)) return result; 130 131 } 132 133 return 0; 134 135 } 136 137 alias getChildren this; 138 139 } 140 141 else alias Children = GluiNode[]; 142 143 static assert(isInputRange!Children); 144 145 146 pragma(inline) 147 void lock(ref Children children) { 148 149 debug children._isLocked = true; 150 151 assertLocked(children); 152 153 } 154 155 pragma(inline) 156 void unlock(ref Children children) { 157 158 debug { 159 160 assert(children._isLocked, "Already unlocked."); 161 162 children._isLocked = false; 163 164 } 165 166 } 167 168 pragma(inline) 169 void assertLocked(ref Children children) { 170 171 // debug just to de sure lol 172 // pretty sure you can fiddle with the compiler flags enough to make this not compile 173 debug assert(children._isLocked); 174 175 } 176 177 /// Get the children list as const. 178 pragma(inline) 179 const(GluiNode[]) asConst(Children children) { 180 181 debug return children._children; 182 else return children; 183 184 } 185 186 /// Get a reference to the children list forcefully, ignoring the lock. 187 pragma(inline) 188 ref GluiNode[] forceMutable(return ref Children children) @system { 189 190 debug return children._children; 191 else return children; 192 193 } 194 195 /// Get a reference to child within the parent. 196 deprecated("childRef will be removed in 0.6.0. Use the @safe GluiNodeSlot instead.") 197 ref GluiNode childRef(ref Children children, size_t index) @system { 198 199 debug assert(!children._isLocked, "Can't get reference to a locked child."); 200 201 debug return children._children[index]; 202 else return children[index]; 203 204 }