1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module atelier.ui.container.gridcontainer;
7 
8 import std.conv : to;
9 import std.algorithm.comparison : max;
10 import atelier.render, atelier.core, atelier.common;
11 import atelier.ui.gui_element;
12 
13 /// Grid container. \
14 /// Align its children left-to-right then top-to-bottom without changing their size. \
15 /// Resized automatically to fits its children.
16 class GridContainer : GuiElement {
17     protected {
18         Vec2f _spacing = Vec2f.zero;
19         uint _maxElementsPerLine = 4u;
20     }
21 
22     @property {
23         /// Space between each child.
24         Vec2f spacing() const {
25             return _spacing;
26         }
27         /// Ditto
28         Vec2f spacing(Vec2f newPadding) {
29             _spacing = newPadding;
30             resize();
31             return _spacing;
32         }
33 
34         /// The number of children per line.
35         uint maxElementsPerLine() const {
36             return _maxElementsPerLine;
37         }
38         /// Ditto
39         uint maxElementsPerLine(uint maxElementsPerLine_) {
40             _maxElementsPerLine = maxElementsPerLine_;
41             resize();
42             return _maxElementsPerLine;
43         }
44     }
45 
46     /// Ctor
47     this() {
48     }
49 
50     override void appendChild(GuiElement gui) {
51         gui.setAlign(GuiAlignX.left, GuiAlignY.top);
52         super.appendChild(gui);
53         resize();
54     }
55 
56     override void update(float deltatime) {
57         resize();
58     }
59 
60     override void onSize() {
61         resize();
62     }
63 
64     private bool _isResizeCalled;
65     protected void resize() {
66         if (_isResizeCalled)
67             return;
68         _isResizeCalled = true;
69 
70         if (!_children.length) {
71             size = Vec2f.zero;
72             _isResizeCalled = false;
73             return;
74         }
75         Vec2f[] lineSizes;
76         uint xCount, i;
77         Vec2f lineSize = Vec2f.zero, totalSize = Vec2f.zero;
78         foreach (GuiElement gui; _children) {
79             lineSize.x += gui.scaledSize.x + _spacing.x;
80             lineSize.y = max(lineSize.y, gui.scaledSize.y);
81             xCount++;
82             i++;
83             if (xCount == _maxElementsPerLine || i == _children.length) {
84                 lineSizes ~= lineSize;
85                 totalSize.x = max(totalSize.x, lineSize.x);
86                 totalSize.y += lineSize.y + _spacing.y;
87                 lineSize = Vec2f.zero;
88                 xCount = 0u;
89             }
90         }
91         size = totalSize + _spacing;
92         Vec2f currentPosition = _spacing;
93         xCount = 0u;
94         uint yCount;
95         foreach (GuiElement gui; _children) {
96             gui.setAlign(GuiAlignX.left, GuiAlignY.top);
97             gui.position = currentPosition;
98             currentPosition += Vec2f(gui.scaledSize.x + _spacing.x, 0f);
99             xCount++;
100             if (xCount == _maxElementsPerLine) {
101                 currentPosition.x = _spacing.x;
102                 currentPosition.y += lineSizes[yCount].y + _spacing.y;
103                 yCount++;
104                 xCount = 0u;
105             }
106         }
107         _isResizeCalled = false;
108     }
109 }