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 }