1 /**
2 * Copyright © Novelate 2020
3 * License: MIT (https://github.com/Novelate/NovelateEngine/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 * Website: https://novelate.com/
6 * ------
7 * Novelate is a free and open-source visual novel engine and framework written in the D programming language.
8 * It can be used freely for both personal and commercial projects.
9 * ------
10 * Module Description:
11 * A layer consists of multiple components that have equal importance in terms of rendering and event delegation.
12 */
13 module novelate.layer;
14 
15 import novelate.component;
16 
17 /// A layer of components.
18 final class Layer
19 {
20   private:
21   /// The components.
22   Component[string] _components;
23   /// The width of the layer. Usually the resolution width.
24   size_t _width;
25   /// The height of the layer. Usually the resolution height.
26   size_t _height;
27   /// The current mouse position within the window.
28   FloatVector _mousePosition;
29 
30   public:
31   final:
32   package (novelate)
33   {
34     /**
35     * Creates a new layer.
36     * Params:
37     *   width = The width of the layer.
38     *   height = The height of the layer.
39     */
40     this(size_t width, size_t height)
41     {
42       _width = width;
43       _height = height;
44     }
45   }
46 
47   @property
48   {
49     /// Gets the width of the layer. Usually the resolution width.
50     size_t width() { return _width; }
51 
52     /// Gets the height of the layer. Usually the resolution height.
53     size_t height() { return _height; }
54 
55     /// Gets the amount of components within the layer.
56     size_t length() { return _components ? _components.length : 0; }
57   }
58 
59   /// Clears the layer for its components.
60   void clear()
61   {
62     if (!_components)
63     {
64       return;
65     }
66 
67     _components.clear();
68   }
69 
70   /**
71   * Adds a component to the layer.
72   * Params:
73   *   component = The component to add.
74   *   name = The name of the component. This must be unique for the layer, otherwise an existing component will be replaced.
75   */
76   void addComponent(Component component, string name)
77   {
78     _components[name] = component;
79   }
80 
81   /**
82   * Removes a component from the layer.
83   * Params:
84   *   name = The name of the component to remove.
85   */
86   void removeComponent(string name)
87   {
88     if (!_components)
89     {
90       return;
91     }
92 
93     _components.remove(name);
94   }
95 
96   /**
97   * Gets a component from the layer. You msut cast to the original component type for it to be useful.
98   * Params:
99   *   name = The name of the component to get.
100   * Returns:
101   *   The component if found, null otherwise.
102   */
103   Component getComponent(string name)
104   {
105     if (!_components)
106     {
107       return null;
108     }
109 
110     return _components.get(name, null);
111   }
112 
113   /**
114   * Will render the components of the layer to the given window.
115   * Params:
116   *   window = The window to render the components at.
117   */
118   void render(ExternalWindow window)
119   {
120     if (!window)
121     {
122       return;
123     }
124 
125     if (!_components)
126     {
127       return;
128     }
129 
130     foreach (k,v; _components)
131     {
132       v.render(window);
133     }
134   }
135 
136   /**
137   * Refreshes the layer and its components with a given width and height. Usually the resolution width and height.
138   * Params:
139   *   width = The width to refresh with.
140   *   height = The height to refresh with.
141   */
142   void refresh(size_t width, size_t height)
143   {
144     _width = width;
145     _height = height;
146 
147     if (!_components)
148     {
149       return;
150     }
151 
152     foreach (k,v; _components)
153     {
154       v.refresh(_width, _height);
155     }
156   }
157 
158   /**
159   * Handles key press events and delegates them to its components.
160   * Params:
161   *   key = The key pressed.
162   *   stopEvent = (ref) Boolean determining whether the event handling should no longer be delegated.
163   */
164   void keyPress(KeyboardKey key, ref bool stopEvent)
165   {
166     if (!_components)
167     {
168       return;
169     }
170 
171     foreach (k,v; _components)
172     {
173       if (v.globalKeyPress)
174       {
175         v.globalKeyPress(key, stopEvent);
176 
177         if (stopEvent)
178         {
179           break;
180         }
181       }
182 
183       if (v.keyPress && v.intersect(_mousePosition))
184       {
185         v.keyPress(key, stopEvent);
186 
187         if (stopEvent)
188         {
189           break;
190         }
191       }
192     }
193   }
194 
195   /**
196   * Handles key release events and delegates them to its components.
197   * Params:
198   *   key = The key released.
199   *   stopEvent = (ref) Boolean determining whether the event handling should no longer be delegated.
200   */
201   void keyRelease(KeyboardKey key, ref bool stopEvent)
202   {
203     if (!_components)
204     {
205       return;
206     }
207 
208     foreach (k,v; _components)
209     {
210       if (v.globalKeyRelease)
211       {
212         v.globalKeyRelease(key, stopEvent);
213 
214         if (stopEvent)
215         {
216           break;
217         }
218       }
219 
220       if (v.keyRelease && v.intersect(_mousePosition))
221       {
222         v.keyRelease(key, stopEvent);
223 
224         if (stopEvent)
225         {
226           break;
227         }
228       }
229     }
230   }
231 
232   /**
233   * Handles mouse button press events and delegates them to its components.
234   * Params:
235   *   button = The mouse button pressed.
236   *   stopEvent = (ref) Boolean determining whether the event handling should no longer be delegated.
237   */
238   void mousePress(MouseButton button, ref bool stopEvent)
239   {
240     if (!_components)
241     {
242       return;
243     }
244 
245     foreach (k,v; _components)
246     {
247       if (v.globalMousePress)
248       {
249         v.globalMousePress(button, stopEvent);
250 
251         if (stopEvent)
252         {
253           break;
254         }
255       }
256 
257       if (v.mousePress && v.intersect(_mousePosition))
258       {
259         v.mousePress(button, stopEvent);
260 
261         if (stopEvent)
262         {
263           break;
264         }
265       }
266     }
267   }
268 
269   /**
270   * Handles mouse button release events and delegates them to its components.
271   * Params:
272   *   button = The mouse button released.
273   *   stopEvent = (ref) Boolean determining whether the event handling should no longer be delegated.
274   */
275   void mouseRelease(MouseButton button, ref bool stopEvent)
276   {
277     if (!_components)
278     {
279       return;
280     }
281 
282     foreach (k,v; _components)
283     {
284       if (v.globalMouseRelease)
285       {
286         v.globalMouseRelease(button, stopEvent);
287 
288         if (stopEvent)
289         {
290           break;
291         }
292       }
293 
294       if (v.mouseRelease && v.intersect(_mousePosition))
295       {
296         v.mouseRelease(button, stopEvent);
297 
298         if (stopEvent)
299         {
300           break;
301         }
302       }
303     }
304   }
305 
306   /**
307   * Handles mouse move events and delegates them to its components.
308   * Params:
309   *   x = The x coordinate of the mouse cursor.
310   *   y = The y coordinate of the mouse cursor.
311   *   stopEvent = (ref) Boolean determining whether the event handling should no longer be delegated.
312   */
313   void mouseMove(ptrdiff_t x, ptrdiff_t y, ref bool stopEvent)
314   {
315     _mousePosition = FloatVector(x, y);
316 
317     if (!_components)
318     {
319       return;
320     }
321 
322     foreach (k,v; _components)
323     {
324       if (v.globalMouseMove)
325       {
326         v.globalMouseMove(_mousePosition, stopEvent);
327 
328         if (stopEvent)
329         {
330           break;
331         }
332       }
333 
334       if (v.mouseMove && v.intersect(_mousePosition))
335       {
336         v.mouseMove(_mousePosition, stopEvent);
337 
338         if (stopEvent)
339         {
340           break;
341         }
342       }
343     }
344   }
345 }