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