1 /**
2 * Module for a container.
3 *
4 * Authors:
5 *   Jacob Jensen
6 * License:
7 *   https://github.com/PoisonEngine/poison-ui/blob/master/LICENSE
8 */
9 module poison.ui.container;
10 
11 import std.algorithm : filter;
12 import std.array : array;
13 
14 public import dsfml.graphics : RenderWindow;
15 
16 import poison.ui.component;
17 import poison.core : Size, Point, ActionArgs, executeUI;
18 
19 /// A container component.
20 class Container : Component {
21   private:
22   /// The components.
23   Component[][] _components;
24 
25   public:
26   /**
27   * Creates a new container.
28   * Params:
29   *   name =        The name of the container.
30   *   initialSize = The initial size of the container.
31   *   layers =      The layers of the container.
32   */
33   this(string name, Size initialSize, size_t layers) {
34     assert(layers > 0);
35 
36     super(name, initialSize);
37 
38     _components = new Component[][layers];
39 
40     foreach (i; 0 .. _components.length) {
41       _components[i] = [];
42     }
43 
44     addSelector("container");
45   }
46 
47   /**
48   * Creates a new container.
49   * Params:
50   *   name =        The name of the container.
51   *   layers =      The layers of the container.
52   */
53   this(string name, size_t layers) {
54     this(name, new Size(100, 100), layers);
55   }
56 
57   @property {
58     /// Gets a boolean determining whether the container is disabled or not.
59     override bool disabled() { return super.disabled; }
60 
61     /// Sets a boolean determining whether the container is disabled or not.
62     override void disabled(bool isDisabled) {
63       super.disabled = isDisabled;
64 
65       foreach (children; _components) {
66         if (children) {
67           foreach (component; children) {
68             if (component) {
69               component.disabled = isDisabled;
70             }
71           }
72         }
73       }
74     }
75   }
76 
77   /**
78   * Adds a component to the container.
79   * Params:
80   *   child = The child component to add.
81   *   layer = The layer to add the component to.
82   */
83   void add(Component child, size_t layer) {
84     assert(layer >= 0);
85     assert(layer < _components.length);
86     assert(child !is null);
87     assert(child.layer == -1);
88 
89     executeUI({
90       _components[layer] ~= child;
91 
92       child.layer = cast(ptrdiff_t)layer;
93       child.parentWindow = parentWindow;
94       child.parentContainer = this;
95       parentWindow._windowComponents[child.id] = child;
96 
97       child.disabled = child.disabled; // HACK: calls updateStyles() internally.
98       child.render();
99       child.show();
100     });
101   }
102 
103   /**
104   * Adds an array of components to the container.
105   * Params:
106   *   components = The child components to add.
107   *   layer = The layer to add the components to.
108   */
109   void add(Component[] components, size_t layer) {
110     assert(components && components.length);
111 
112     executeUI({
113       foreach (component; components) {
114         add(component, layer);
115       }
116     });
117   }
118 
119   /**
120   * Removes a component from the container.
121   * Params:
122   *   child = The child component to remove.
123   */
124   void remove(Component child) {
125     assert(child.layer != -1);
126 
127     executeUI({
128       auto children = _components[child.layer];
129 
130       if (children) {
131         children = children.filter!((c) { return c.id == child.id; }).array;
132 
133         _components[layer] = children;
134       }
135 
136       child.layer = -1;
137       child.parentWindow._windowComponents.remove(child.id);
138       child.parentWindow = null;
139       child.parentContainer = null;
140     });
141   }
142 
143   /**
144   * Removes a component from the container.
145   * Params:
146   *   name =  The name of the component.
147   */
148   void remove(string name) {
149     executeUI({
150       foreach (ref children; _components) {
151         if (children) {
152           children = children.filter!((c) { return c.name == name; }).array;
153         }
154       }
155     });
156   }
157 
158   /// Clears the component for children.
159   void clear() {
160     executeUI({
161       foreach (children; _components) {
162         if (children) {
163           foreach (component; children) {
164             if (component) {
165               component.layer = -1;
166             }
167           }
168         }
169       }
170 
171       _components = new Component[][_components.length];
172     });
173   }
174 
175   /**
176   * Clears the components for a specific layer.
177   * Params:
178   *   layer = The layer to clear.
179   */
180   void clear(size_t layer) {
181     assert(layer >= 0);
182     assert(layer < _components.length);
183 
184     executeUI({
185       auto children = _components[layer];
186 
187       if (children) {
188         foreach (component; children) {
189           if (component) {
190             component.layer = -1;
191           }
192         }
193       }
194 
195       _components[layer] = [];
196     });
197   }
198 
199   protected:
200   /**
201   * Processes the container during application cycles.
202   * Params:
203   *   window =        The render window to process.
204   */
205   override void process(RenderWindow window) {
206     super.process(window);
207 
208     foreach (children; _components) {
209       if (children) {
210         foreach (component; children) {
211           if (component) {
212             component.processInternal(window);
213           }
214         }
215       }
216     }
217   }
218 }