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 }