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 }