1 module sily.tui.elements.element;
2 
3 import std.conv: to;
4 import std.array: insertInPlace;
5 import std.algorithm: remove, countUntil, canFind;
6 
7 import sily.tui;
8 
9 import sily.vector;
10 import sily.property;
11 
12 /// Input event struct
13 struct InputEvent {
14     enum Type {keyboard, mouse, mouseMotion}
15     Type type = Type.keyboard;
16     int key = 0; 
17     bool isProcessed = false;
18 }
19 
20 /// Element style
21 struct Style {
22 
23 }
24 
25 /**
26 Should be used to inherit constructor, since D doesn't do that
27 
28 Inserts `this(A...)(A args) { super(args); }` in place of mixin.
29 If you'd to supply incorrect args compilation will fail, so
30 it's fairly safe way to solve that problem
31 Usage:
32 ---
33 class NewElement: Element {
34     // inherits constructors of Element
35     mixin inheritConstructor;
36     // your constructors
37     this(string thing, int notthing) { /+ ... +/ }
38 }
39 ---
40 */
41 mixin template inheritConstructor( ) {
42     this(A...)(A args) { super(args); }
43 }
44 
45 /// Base for all TUI elements
46 class Element {
47     private App _app;
48     private Element _parent = null;
49     private Element[] _children = [];
50     private bool _isRoot = false;
51     private bool _isInit = false;
52     private uvec2 _pos;
53 
54     /// Returns App element is attached to
55     mixin getter!_app;
56     /// Returns current parent
57     mixin getter!_parent;
58     /// Returns array of children
59     mixin getter!_children;
60     /// Returns true if element is root
61     mixin getter!_isRoot;
62     /// Returns true if Element was initialized
63     mixin getter!_isInit;
64     /// Element position property
65     mixin property!_pos;
66 
67     this() {}
68 
69     /// Adds child to element
70     public final void addChild(Element child) {
71         _children ~= child;
72         if (!child.isInit) {
73             child.setApp(app);
74             child.propagateCreate();
75         }
76     }
77 
78     /// Removes child from element
79     public final void removeChild(Element child) {
80         if (!hasChild(child)) return;
81         size_t p = children.countUntil(child);
82         children.remove(p);
83     }
84 
85     /// Moves child to position
86     public final void moveChild(Element child, size_t index) {
87         if (!hasChild(child)) return;
88         removeChild(child);
89         _children.insertInPlace(index, child);
90     }
91 
92     /// Returns true if this has child
93     public final bool hasChild(Element child) {
94         if (children.length == 0) return false;
95         return children.canFind(child);
96     }
97 
98     /// Returns amount of children element has
99     public final size_t childCount() {
100         return children.length;
101     }
102 
103     /// Returns child at pos
104     public final Element getChild(size_t index) {
105         return children[index];
106     }
107 
108     /// Returns child at pos
109     public final long getChildPos(Element child) {
110         if (!hasChild(child)) return -1;
111         return children.countUntil(child);
112     }
113     
114     /// Reparents element if it's not root
115     public final void setParent(Element p_parent) {
116         if (isRoot) return;
117         if (_parent !is null) _parent.removeChild(this);
118         p_parent.addChild(this);
119         _parent = p_parent;
120     }
121 
122     /// Sets element as root if there's no root already
123     public final void setRoot() {
124         if (app is null) return;
125         if (app.rootElement !is null) return;
126         _isRoot = true;
127     }
128 
129     /// Sets App element attached to if it's not already defined
130     public final void setApp(App p_app) {
131         if (app !is null) return;
132         _app = p_app;
133     }
134 
135     /// Propagates internal function calls
136     public final void propagateCreate() {
137         _create();
138         create();
139         foreach (child; children) if (!child.isInit) { child.propagateCreate(); }
140         _isInit = true;
141     }
142 
143     /// Ditto
144     public final void propagateDestroy() {
145         _destroy();
146         destroy();
147         foreach (child; children) child.propagateDestroy();
148         if (_parent !is null) _parent.removeChild(this);
149     }
150     
151     /// Ditto
152     public final void propagateUpdate(float delta) {
153         _update(delta);
154         update(delta);
155         foreach (child; children) child.propagateUpdate(delta);
156     }
157     
158     /// Ditto
159     public final void propagateInput(InputEvent e) {
160         if (e.isProcessed) return;
161         _input(e);
162         if (e.isProcessed) return;
163         input(e);
164         if (e.isProcessed) return;
165         foreach (child; children) {
166             child.propagateInput(e);
167             if (e.isProcessed) return;
168         }
169     }
170     
171     /// Ditto
172     public final void propagateRender() {
173         _render();
174         render();
175         foreach (child; children) child.propagateRender();
176     }
177     
178     /**
179     Public create method. Can be overriden. 
180     Called when element is first added as child
181     */
182     public void create() {}
183     /**
184     Public destroy method. Can be overriden. 
185     Called when element is queued for deletion or 
186     when App is being closed
187     */
188     public void destroy() {}
189     /**
190     Public update method. Can be overriden.
191     Called each frame
192     */
193     public void update(float delta) {}
194     /**
195     Public input method. Can be overriden.
196     Called each frame if there's unprocessed
197     input event
198     */
199     public void input(InputEvent e) {}
200     /**
201     Public render method. Can be overriden.
202     Called each frame if render is needed
203     */
204     public void render() {}
205 
206     /**
207     Private create method. Used for internal logic
208     Called when element is first added as child
209     */
210     protected void _create() {}
211     /**
212     Private destroy method. Used for internal logic
213     Called when element is queued for deletion or 
214     when App is being closed
215     */
216     protected void _destroy() {}
217     /**
218     Private update method. Used for internal logic
219     Called each frame
220     */
221     protected void _update(float delta) {}
222     /**
223     Private input method. Used for internal logic
224     Called each frame if there's unprocessed
225     input event
226     */
227     protected void _input(InputEvent e) {}
228     /**
229     Private render method. Used for internal logic
230     Called each frame if render is needed
231     */
232     protected void _render() {}
233 }