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