Simple GUI Library
box_container.cpp
Go to the documentation of this file.
1/**
2 * @author Nikita Mochalov (github.com/tralf-strues)
3 * @file box_container.cpp
4 * @date 2021-11-09
5 *
6 * @copyright Copyright (c) 2021
7 */
8
10
11namespace Sgl
12{
13 BoxContainer::BoxContainer(Direction direction)
14 : m_Direction(direction) {}
15
16 BoxContainer::Direction BoxContainer::getDirection() const { return m_Direction; }
17 void BoxContainer::setDirection(Direction direction) { m_Direction = direction; }
18
19 bool BoxContainer::getFillAcross() const { return m_FillAcross; }
20 void BoxContainer::setFillAcross(bool fillAcross) { m_FillAcross = fillAcross; }
21
22 int32_t BoxContainer::getSpacing() const { return m_Spacing; }
23 void BoxContainer::setSpacing(int32_t spacing) { m_Spacing = spacing; }
24
25 void BoxContainer::setGrowPriority(Component* component, GrowPriority priority)
26 {
27 m_GrowPriorities[component] = priority;
28 }
29
30 BoxContainer::GrowPriority BoxContainer::getGrowPriority(Component* component) const
31 {
32 auto priority = m_GrowPriorities.find(component);
33 return priority == m_GrowPriorities.end() ? GrowPriority::NEVER : (*priority).second;
34 }
35
36 void BoxContainer::pushBackSpacer(uint32_t weight)
37 {
38 Component* prevComponent = m_Children.empty() ? nullptr : m_Children.back();
39 m_Spacers.emplace_back(prevComponent, weight);
40
41 mergeSpacers(prevComponent);
42 }
43
44 void BoxContainer::pushFrontSpacer(uint32_t weight)
45 {
46 m_Spacers.emplace_back(nullptr, weight);
47
48 mergeSpacers(nullptr);
49 }
50
51 void BoxContainer::layoutChildren()
52 {
53 Sml::Rectangle<int32_t> contentArea = getContentArea();
54
55 size_t countSpacers = m_Spacers.size();
56 size_t countGrowingComponents = computeGrowingComponentsCount(GrowPriority::ALWAYS);
57 uint32_t totalSpacersWeight = computeTotalSpacersWeight();
58
59 if (m_Direction == Direction::LEFT_TO_RIGHT)
60 {
61 int32_t freeWidth = getLayoutWidth() - computeCustomPrefWidth();
62 int32_t widthForSpacers = (countGrowingComponents == 0) ? freeWidth : 0;
63 int32_t widthForGrowingComponents = freeWidth - widthForSpacers;
64 int32_t widthForGrowingComponent = widthForGrowingComponents / countGrowingComponents;
65
66 int32_t curX = contentArea.pos.x + computeSpacerSize(nullptr, totalSpacersWeight,
67 widthForSpacers);
68 int32_t centerY = contentArea.pos.y + contentArea.height / 2;
69
70 for (Component* child : m_Children)
71 {
72 child->setLayoutX(curX);
73 child->setLayoutWidth(child->computePrefWidth());
74
75 if (getGrowPriority(child) == GrowPriority::ALWAYS)
76 {
77 child->setLayoutWidth(child->getLayoutWidth() + widthForGrowingComponent);
78 }
79
80 curX += child->getLayoutWidth() + getSpacing() +
81 computeSpacerSize(child, totalSpacersWeight, widthForSpacers);
82
83 child->setLayoutHeight(m_FillAcross ? contentArea.height : child->computePrefHeight());
84 child->setLayoutY(centerY - child->getLayoutHeight() / 2);
85 }
86 }
87 else if (m_Direction == Direction::TOP_TO_BOTTOM)
88 {
89 int32_t freeHeight = getLayoutHeight() - computeCustomPrefHeight();
90 int32_t heightForSpacers = (countGrowingComponents == 0) ? freeHeight : 0;
91 int32_t heightForGrowingComponents = freeHeight - heightForSpacers;
92 int32_t heightForGrowingComponent = heightForGrowingComponents / countGrowingComponents;
93
94 int32_t curY = contentArea.pos.y + computeSpacerSize(nullptr, totalSpacersWeight,
95 heightForSpacers);
96
97 for (Component* child : m_Children)
98 {
99 child->setLayoutX(contentArea.pos.x);
100 child->setLayoutWidth(m_FillAcross ? contentArea.width : child->computePrefWidth());
101
102 child->setLayoutY(curY);
103 child->setLayoutHeight(child->computePrefHeight());
104
105 if (getGrowPriority(child) == GrowPriority::ALWAYS)
106 {
107 child->setLayoutHeight(child->getLayoutHeight() + heightForGrowingComponent);
108 }
109
110 curY += child->getLayoutHeight() + getSpacing() +
111 computeSpacerSize(child, totalSpacersWeight, heightForSpacers);
112 }
113 }
114 }
115
116 int32_t BoxContainer::computeTotalPrefSizeWithSpacing(SizeDimension sizeDimension) const
117 {
118 int32_t totalPrefSize = getSpacing() * (m_Children.size() - 1);
119
120 for (Component* child : m_Children)
121 {
122 if (sizeDimension == SizeDimension::WIDTH)
123 {
124 totalPrefSize += child->computePrefWidth();
125 }
126 else
127 {
128 totalPrefSize += child->computePrefHeight();
129 }
130 }
131
132 return totalPrefSize;
133 }
134
135 int32_t BoxContainer::computeMaximumPrefSize(SizeDimension sizeDimension) const
136 {
137 int32_t maxPrefSize = 0;
138
139 for (Component* child : m_Children)
140 {
141 int32_t prefSize = 0;
142
143 if (sizeDimension == SizeDimension::WIDTH)
144 {
145 prefSize = child->computePrefWidth();
146 }
147 else
148 {
149 prefSize = child->computePrefHeight();
150 }
151
152 if (prefSize > maxPrefSize)
153 {
154 maxPrefSize = prefSize;
155 }
156 }
157
158 return maxPrefSize;
159 }
160
161 int32_t BoxContainer::computeCustomPrefWidth(int32_t height) const
162 {
163 int32_t prefWidth = getInsets().left + getInsets().right;
164
165 if (m_Children.empty())
166 {
167 return prefWidth;
168 }
169
170 if (m_Direction == Direction::LEFT_TO_RIGHT)
171 {
172 prefWidth += computeTotalPrefSizeWithSpacing(SizeDimension::WIDTH);
173 }
174 else if (m_Direction == Direction::TOP_TO_BOTTOM)
175 {
176 prefWidth += computeMaximumPrefSize(SizeDimension::WIDTH);
177 }
178
179 return prefWidth;
180 }
181
182 int32_t BoxContainer::computeCustomPrefHeight(int32_t width) const
183 {
184 int32_t prefHeight = getInsets().top + getInsets().bottom;
185
186 if (m_Children.empty())
187 {
188 return prefHeight;
189 }
190
191 if (m_Direction == Direction::LEFT_TO_RIGHT)
192 {
193 prefHeight += computeMaximumPrefSize(SizeDimension::HEIGHT);
194 }
195 else if (m_Direction == Direction::TOP_TO_BOTTOM)
196 {
197 prefHeight += computeTotalPrefSizeWithSpacing(SizeDimension::HEIGHT);
198 }
199
200 return prefHeight;
201 }
202
203 // TODO: implement
204 int32_t BoxContainer::computeCustomMinWidth(int32_t height) const
205 {
206 return 0;
207 }
208
209 // TODO: implement
210 int32_t BoxContainer::computeCustomMinHeight(int32_t width) const
211 {
212 return 0;
213 }
214
215 uint32_t BoxContainer::computeTotalSpacersWeight() const
216 {
217 uint32_t totalWeight = 0;
218 for (const Spacer& spacer : m_Spacers)
219 {
220 totalWeight += spacer.weight;
221 }
222
223 return totalWeight;
224 }
225
226 int32_t BoxContainer::computeSpacerSize(Component* prev, uint32_t totalWeight,
227 int32_t totalSpacersSize) const
228 {
229 if (totalWeight == 0) { return 0; }
230
231 for (const Spacer& spacer : m_Spacers)
232 {
233 if (spacer.prevComponent == prev)
234 {
235 return totalSpacersSize * (static_cast<float>(spacer.weight) /
236 static_cast<float>(totalWeight));
237 }
238 }
239
240 return 0;
241 }
242
243 void BoxContainer::mergeSpacers(Component* prev)
244 {
245 static std::vector<std::list<Spacer>::iterator> spacersToErase;
246 spacersToErase.clear();
247
248 Spacer newSpacer{prev, 0};
249 for (auto it = m_Spacers.begin(); it != m_Spacers.end(); ++it)
250 {
251 if (it->prevComponent == prev)
252 {
253 newSpacer.weight += it->weight;
254 spacersToErase.push_back(it);
255 }
256 }
257
258 for (auto spacerToErase : spacersToErase)
259 {
260 m_Spacers.erase(spacerToErase);
261 }
262
263 if (newSpacer.weight > 0)
264 {
265 m_Spacers.push_back(newSpacer);
266 }
267 }
268
269 size_t BoxContainer::computeGrowingComponentsCount(GrowPriority priority) const
270 {
271 size_t count = 0;
272
273 for (auto pair : m_GrowPriorities)
274 {
275 if (pair.second == priority)
276 {
277 ++count;
278 }
279 }
280
281 return count;
282 }
283
284 HBox::HBox() : BoxContainer(BoxContainer::Direction::LEFT_TO_RIGHT) {}
285
286 VBox::VBox() : BoxContainer(BoxContainer::Direction::TOP_TO_BOTTOM) {}
287}