Simple GUI Library
anchor_pane.cpp
Go to the documentation of this file.
1/**
2 * @author Nikita Mochalov (github.com/tralf-strues)
3 * @file anchor_pane.cpp
4 * @date 2021-12-01
5 *
6 * @copyright Copyright (c) 2021
7 */
8
10
11namespace Sgl
12{
13 #define CONSTRAINTS_SET(property, value) \
14 assert(child); \
15 auto constraints = m_Constraints.find(child); \
16 if (constraints != m_Constraints.end()) \
17 { \
18 (*constraints).second.property = value; \
19 } \
20 else \
21 { \
22 m_Constraints[child] = Constraints{}; \
23 m_Constraints[child].property = value; \
24 }
25
26 #define CONSTRAINTS_GET(property, defaultValue) \
27 assert(child); \
28 auto constraints = m_Constraints.find(child); \
29 return (constraints != m_Constraints.end()) ? (*constraints).second.property : \
30 defaultValue;
31
32 AnchorPane::Constraints AnchorPane::getConstraints(Component* child) const
33 {
34 assert(child);
35
36 auto constraints = m_Constraints.find(child);
37 if (constraints != m_Constraints.end())
38 {
39 return (*constraints).second;
40 }
41
42 return Constraints{};
43 }
44
45 bool AnchorPane::hasConstraints(Component* child) const
46 {
47 assert(child);
48
49 auto constraints = m_Constraints.find(child);
50 if (constraints == m_Constraints.end())
51 {
52 return false;
53 }
54
55 const Anchors& anchors = (*constraints).second.anchors;
56
57 return anchors.topAnchor != ANCHOR_NOT_ENABLED ||
58 anchors.rightAnchor != ANCHOR_NOT_ENABLED ||
59 anchors.bottomAnchor != ANCHOR_NOT_ENABLED ||
60 anchors.leftAnchor != ANCHOR_NOT_ENABLED;
61 }
62
63 void AnchorPane::setHorizontalRelativePositioning(Component* child, bool enabled)
64 {
65 CONSTRAINTS_SET(isHorizontalRelative, enabled);
66 }
67
68 void AnchorPane::setVerticalRelativePositioning(Component* child, bool enabled)
69 {
70 CONSTRAINTS_SET(isVerticalRelative, enabled);
71 }
72
73 bool AnchorPane::isHorizontalRelativePositioning(Component* child) const
74 {
75 CONSTRAINTS_GET(isHorizontalRelative, false);
76 }
77
78 bool AnchorPane::isVerticalRelativePositioning(Component* child) const
79 {
80 CONSTRAINTS_GET(isVerticalRelative, false);
81 }
82
83 void AnchorPane::setTopAnchor(Component* child, int32_t anchor)
84 {
85 CONSTRAINTS_SET(anchors.topAnchor, anchor);
86 }
87
88 void AnchorPane::setRightAnchor(Component* child, int32_t anchor)
89 {
90 CONSTRAINTS_SET(anchors.rightAnchor, anchor);
91 }
92
93 void AnchorPane::setBottomAnchor(Component* child, int32_t anchor)
94 {
95 CONSTRAINTS_SET(anchors.bottomAnchor, anchor);
96 }
97
98 void AnchorPane::setLeftAnchor(Component* child, int32_t anchor)
99 {
100 CONSTRAINTS_SET(anchors.leftAnchor, anchor);
101 }
102
103 int32_t AnchorPane::getTopAnchor(Component* child) const
104 {
105 CONSTRAINTS_GET(anchors.topAnchor, ANCHOR_NOT_ENABLED);
106 }
107
108 int32_t AnchorPane::getRightAnchor(Component* child) const
109 {
110 CONSTRAINTS_GET(anchors.rightAnchor, ANCHOR_NOT_ENABLED);
111 }
112
113 int32_t AnchorPane::getBottomAnchor(Component* child) const
114 {
115 CONSTRAINTS_GET(anchors.bottomAnchor, ANCHOR_NOT_ENABLED);
116 }
117
118 int32_t AnchorPane::getLeftAnchor(Component* child) const
119 {
120 CONSTRAINTS_GET(anchors.leftAnchor, ANCHOR_NOT_ENABLED);
121 }
122
123 void AnchorPane::layoutChildren()
124 {
125 Sml::Rectangle<int32_t> contentArea = getContentArea();
126
127 for (auto child : m_Children)
128 {
129 Constraints constraints = getConstraints(child);
130
131 if (!hasConstraints(child))
132 {
133 child->setLayoutWidth(child->computePrefWidth());
134 child->setLayoutHeight(child->computePrefHeight());
135 continue;
136 }
137
138 // Horizontal layout
139 if (isHorizontalRelativePositioning(child))
140 {
141 int32_t prefWidth = child->computePrefWidth();
142 int32_t freeWidth = contentArea.width - prefWidth;
143
144 child->setLayoutWidth(prefWidth);
145
146 int32_t totalAnchors = constraints.anchors.leftAnchor + constraints.anchors.rightAnchor;
147 int32_t leftOffset = freeWidth * (static_cast<float>(constraints.anchors.leftAnchor) / totalAnchors);
148
149 child->setLayoutX(contentArea.pos.x + leftOffset);
150 }
151 else if (constraints.anchors.leftAnchor != ANCHOR_NOT_ENABLED &&
152 constraints.anchors.rightAnchor != ANCHOR_NOT_ENABLED)
153 {
154 child->setLayoutX(contentArea.pos.x + constraints.anchors.leftAnchor);
155 child->setLayoutWidth(contentArea.width -
156 constraints.anchors.leftAnchor -
157 constraints.anchors.rightAnchor);
158 }
159 else if (constraints.anchors.leftAnchor != ANCHOR_NOT_ENABLED &&
160 constraints.anchors.rightAnchor == ANCHOR_NOT_ENABLED)
161 {
162 child->setLayoutX(contentArea.pos.x + constraints.anchors.leftAnchor);
163 child->setLayoutWidth(child->computePrefWidth());
164 }
165 else if (constraints.anchors.leftAnchor == ANCHOR_NOT_ENABLED &&
166 constraints.anchors.rightAnchor != ANCHOR_NOT_ENABLED)
167 {
168 child->setLayoutWidth(child->computePrefWidth());
169 child->setLayoutX(contentArea.pos.x + contentArea.width - child->getLayoutWidth() -
170 constraints.anchors.rightAnchor);
171 }
172 else
173 {
174 child->setLayoutWidth(child->computePrefWidth());
175 }
176
177 // Vertical layout
178 if (isVerticalRelativePositioning(child))
179 {
180 int32_t prefHeight = child->computePrefHeight();
181 int32_t freeHeight = contentArea.height - prefHeight;
182
183 child->setLayoutHeight(prefHeight);
184
185 int32_t totalAnchors = constraints.anchors.topAnchor + constraints.anchors.bottomAnchor;
186 int32_t topOffset = freeHeight * (static_cast<float>(constraints.anchors.topAnchor) / totalAnchors);
187
188 child->setLayoutY(contentArea.pos.y + topOffset);
189 }
190 else if (constraints.anchors.topAnchor != ANCHOR_NOT_ENABLED &&
191 constraints.anchors.bottomAnchor != ANCHOR_NOT_ENABLED)
192 {
193 child->setLayoutY(contentArea.pos.y + constraints.anchors.topAnchor);
194 child->setLayoutHeight(contentArea.height -
195 constraints.anchors.topAnchor -
196 constraints.anchors.bottomAnchor);
197 }
198 else if (constraints.anchors.topAnchor != ANCHOR_NOT_ENABLED &&
199 constraints.anchors.bottomAnchor == ANCHOR_NOT_ENABLED)
200 {
201 child->setLayoutY(contentArea.pos.y + constraints.anchors.topAnchor);
202 child->setLayoutHeight(child->computePrefHeight());
203 }
204 else if (constraints.anchors.topAnchor == ANCHOR_NOT_ENABLED &&
205 constraints.anchors.bottomAnchor != ANCHOR_NOT_ENABLED)
206 {
207 child->setLayoutHeight(child->computePrefHeight());
208 child->setLayoutY(contentArea.pos.y + contentArea.height - child->getLayoutHeight() -
209 constraints.anchors.bottomAnchor);
210 }
211 else
212 {
213 child->setLayoutHeight(child->computePrefHeight());
214 }
215 }
216 }
217
218 int32_t AnchorPane::computeCustomPrefWidth(int32_t height) const
219 {
220 int32_t maxNeededWidth = 0;
221 for (auto child : m_Children)
222 {
223 int32_t leftOffset = getLeftAnchor(child);
224 int32_t rightOffset = getRightAnchor(child);
225
226 if (leftOffset == ANCHOR_NOT_ENABLED && rightOffset == ANCHOR_NOT_ENABLED)
227 {
228 continue;
229 }
230
231 int32_t neededWidth = child->computePrefWidth();
232 if (!isHorizontalRelativePositioning(child))
233 {
234 if (leftOffset != ANCHOR_NOT_ENABLED) { neededWidth += leftOffset; }
235 if (rightOffset != ANCHOR_NOT_ENABLED) { neededWidth += rightOffset; }
236 }
237
238 if (neededWidth > maxNeededWidth)
239 {
240 maxNeededWidth = neededWidth;
241 }
242 }
243
244 int32_t defaultPrefWidth = Container::computeCustomPrefWidth(height);
245
246 return std::max(defaultPrefWidth, getInsets().left + getInsets().right + maxNeededWidth);
247 }
248
249 int32_t AnchorPane::computeCustomPrefHeight(int32_t width) const
250 {
251 int32_t maxNeededHeight = 0;
252 for (auto child : m_Children)
253 {
254 int32_t topOffset = getTopAnchor(child);
255 int32_t bottomOffset = getBottomAnchor(child);
256
257 if (topOffset == ANCHOR_NOT_ENABLED && bottomOffset == ANCHOR_NOT_ENABLED)
258 {
259 continue;
260 }
261
262 int32_t neededHeight = child->computePrefHeight();
263 if (!isVerticalRelativePositioning(child))
264 {
265 if (topOffset != ANCHOR_NOT_ENABLED) { neededHeight += topOffset; }
266 if (bottomOffset != ANCHOR_NOT_ENABLED) { neededHeight += bottomOffset; }
267 }
268
269 if (neededHeight > maxNeededHeight)
270 {
271 maxNeededHeight = neededHeight;
272 }
273 }
274
275 int32_t defaultPrefHeight = Container::computeCustomPrefHeight(width);
276
277 return std::max(defaultPrefHeight, getInsets().top + getInsets().bottom + maxNeededHeight);
278 }
279
280 // TODO: implement
281 int32_t AnchorPane::computeCustomMinWidth(int32_t height) const
282 {
283 return 0;
284 }
285
286 // TODO: implement
287 int32_t AnchorPane::computeCustomMinHeight(int32_t width) const
288 {
289 return 0;
290 }
291}