FifeGUI 0.2.0
A C++ GUI library designed for games.
flowcontainer.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2004 - 2008 Olof Naessén and Per Larsson
3// SPDX-FileCopyrightText: 2013 - 2026 Fifengine contributors
4
5#include "fifechan/widgets/flowcontainer.hpp"
6
7#include <algorithm>
8#include <list>
9#include <vector>
10
11#include "fifechan/exception.hpp"
12
13namespace fcn
14{
15
16 FlowContainer::FlowContainer()
17 {
18 setOpaque(true);
19 }
20
21 FlowContainer::~FlowContainer() = default;
22
24 {
25 mAlignment = alignment;
26 }
27
32
34 {
35 // diff means border, padding, ...
36 int const diffW = std::abs(getDimension().width - getChildrenArea().width);
37 int const diffH = std::abs(getDimension().height - getChildrenArea().height);
38
39 int const containerW = getChildrenArea().width;
40 int const containerH = getChildrenArea().height;
41
42 // calculates max layout size per column or row
43 std::vector<int> layoutMax;
44 int tmpSize = 0;
45 int x = 0;
46 int y = 0;
47 int visibleChilds = 0;
48 std::list<Widget*>::const_iterator currChild(mChildren.begin());
49 std::list<Widget*>::const_iterator endChildren(mChildren.end());
50 for (; currChild != endChildren; ++currChild) {
51 Widget* child = *currChild;
52 if (!child->isVisible()) {
53 continue;
54 }
55 ++visibleChilds;
56 Rectangle const & rec = child->getDimension();
57 if (mLayout == LayoutPolicy::Vertical) {
58 // new column
59 // negative bottom margin have no effect here
60 if (y + child->getMarginTop() + rec.height +
61 (child->getMarginBottom() > 0 ? child->getMarginBottom() : 0) >
62 containerH) {
63 y = 0;
64 layoutMax.push_back(tmpSize);
65 tmpSize = 0;
66 }
67 y += rec.height + child->getMarginTop() + child->getMarginBottom() + getVerticalSpacing();
68 tmpSize = std::max(
69 tmpSize,
70 rec.width + child->getMarginLeft() + (child->getMarginRight() > 0 ? child->getMarginRight() : 0));
71 } else {
72 // new row
73 // negative right margin have no effect here
74 if (x + rec.width + child->getMarginLeft() +
75 (child->getMarginRight() > 0 ? child->getMarginRight() : 0) >
76 containerW) {
77 x = 0;
78 layoutMax.push_back(tmpSize);
79 tmpSize = 0;
80 }
81 x += rec.width + child->getMarginLeft() + child->getMarginRight() + getHorizontalSpacing();
82 tmpSize = std::max(
83 tmpSize,
84 rec.height + child->getMarginTop() + (child->getMarginBottom() > 0 ? child->getMarginBottom() : 0));
85 }
86 }
87 if (tmpSize != 0) {
88 layoutMax.push_back(tmpSize);
89 }
90
91 // places all widgets
92 x = 0;
93 y = 0;
94 int totalW = 0;
95 int totalH = 0;
96 unsigned int layout = 0;
97 if (mLayout == LayoutPolicy::Vertical && visibleChilds > 0) {
98 currChild = mChildren.begin();
99 endChildren = mChildren.end();
100 for (; currChild != endChildren; ++currChild) {
101 if (!(*currChild)->isVisible()) {
102 continue;
103 }
104 int columnW = layoutMax[layout];
105 int const layoutW = (*currChild)->getWidth() + (*currChild)->getMarginLeft() +
106 ((*currChild)->getMarginRight() > 0 ? (*currChild)->getMarginRight() : 0);
107 Rectangle dim(
108 (*currChild)->getMarginLeft(), (*currChild)->getMarginTop(), layoutW, (*currChild)->getHeight());
109
110 // new column
111 // negative bottom margin have no effect here
112 if (y + (*currChild)->getMarginTop() + dim.height +
113 ((*currChild)->getMarginBottom() > 0 ? (*currChild)->getMarginBottom() : 0) >
114 containerH) {
115 x += columnW + getHorizontalSpacing();
116 y = 0;
117 ++layout;
118 columnW = layoutMax[layout];
119 }
120 dim.y += y;
121
122 switch (getAlignment()) {
123 case Alignment::Left:
124 dim.x += x;
125 break;
126 case Alignment::Center:
127 dim.x += x + (columnW - layoutW) / 2;
128 break;
129 case Alignment::Right:
130 dim.x += x + (columnW - layoutW);
131 break;
132 default:
133 throwException("Unknown alignment.");
134 }
135
136 (*currChild)->setDimension(dim);
137 y += (*currChild)->getHeight() + (*currChild)->getMarginTop() + (*currChild)->getMarginBottom() +
139 totalW = std::max(totalW, dim.x + dim.width);
140 totalH = std::max(totalH, y);
141 }
142 // remove last spacing
143 totalH -= getVerticalSpacing();
144 // always expand height but width only if horizontal expand is enabled
145 totalH = std::max(totalH, containerH);
146 if (isHorizontalExpand()) {
147 totalW = std::max(totalW, containerW);
148 }
149 } else if (mLayout == LayoutPolicy::Horizontal && visibleChilds > 0) {
150 currChild = mChildren.begin();
151 endChildren = mChildren.end();
152 for (; currChild != endChildren; ++currChild) {
153 if (!(*currChild)->isVisible()) {
154 continue;
155 }
156 int rowH = layoutMax[layout];
157 int const layoutH = (*currChild)->getHeight() + (*currChild)->getMarginTop() +
158 ((*currChild)->getMarginBottom() > 0 ? (*currChild)->getMarginBottom() : 0);
159 Rectangle dim(
160 (*currChild)->getMarginLeft(), (*currChild)->getMarginTop(), (*currChild)->getWidth(), layoutH);
161
162 // new row
163 // negative right margin have no effect here
164 if (x + (*currChild)->getMarginLeft() + dim.width +
165 ((*currChild)->getMarginRight() > 0 ? (*currChild)->getMarginRight() : 0) >
166 containerW) {
167 x = 0;
168 y += rowH + getVerticalSpacing();
169 ++layout;
170 rowH = layoutMax[layout];
171 }
172 dim.x += x;
173
174 switch (getAlignment()) {
175 case Alignment::Top:
176 dim.y += y;
177 break;
178 case Alignment::Center:
179 dim.y += y + (rowH - layoutH) / 2;
180 break;
181 case Alignment::Bottom:
182 dim.y += y + (rowH - layoutH);
183 break;
184 default:
185 throwException("Unknown alignment.");
186 }
187
188 (*currChild)->setDimension(dim);
189 x += (*currChild)->getWidth() + (*currChild)->getMarginLeft() + (*currChild)->getMarginRight() +
191 totalW = std::max(totalW, x);
192 totalH = std::max(totalH, dim.y + dim.height);
193 }
194 // remove last spacing
195 totalW -= getHorizontalSpacing();
196 // always expand width but height only if vertical expand is enabled
197 totalW = std::max(totalW, containerW);
198 if (isVerticalExpand()) {
199 totalH = std::max(totalH, containerH);
200 }
201 }
202
203 // resize container
204 totalW += diffW;
205 totalH += diffH;
206 setSize(totalW, totalH);
207 }
208
210 {
211 if (policy == LayoutPolicy::Circular) {
212 throwException("Circular layout is not implemented for the FlowContainer.");
213 } else {
214 Container::setLayout(policy);
215 }
216 }
217
219 {
220 if (recursion) {
221 std::list<Widget*>::const_iterator currChild(mChildren.begin());
222 std::list<Widget*>::const_iterator const endChildren(mChildren.end());
223 for (; currChild != endChildren; ++currChild) {
224 if (!(*currChild)->isVisible()) {
225 continue;
226 }
227 (*currChild)->resizeToContent(recursion);
228 }
229 }
230
231 if (mLayout != Container::LayoutPolicy::Absolute) {
232 if (getParent() != nullptr) {
234 }
235 }
236 }
237
238 void FlowContainer::expandContent(bool recursion)
239 {
240 if (mLayout != Container::LayoutPolicy::Absolute) {
242 }
243 // not really needed
244 if (recursion) {
245 std::list<Widget*>::const_iterator currChild(mChildren.begin());
246 std::list<Widget*>::const_iterator const endChildren(mChildren.end());
247 for (; currChild != endChildren; ++currChild) {
248 if (!(*currChild)->isVisible()) {
249 continue;
250 }
251 (*currChild)->expandContent(recursion);
252 }
253 }
254 }
255
256} // namespace fcn
virtual void setLayout(LayoutPolicy policy)
Sets the layout of the container.
LayoutPolicy mLayout
Layout.
LayoutPolicy
The layout policy of the container.
Definition container.hpp:38
Rectangle getChildrenArea() override
Gets the area of the widget occupied by the widget's children.
virtual unsigned int getHorizontalSpacing() const
Get the horizontal spacing between rows.
virtual void setOpaque(bool opaque)
Sets the container to be opaque or not.
Definition container.cpp:56
virtual unsigned int getVerticalSpacing() const
Get the vertical spacing between rows.
Alignment mAlignment
Current alignment used when laying out child widgets.
virtual void setAlignment(FlowContainer::Alignment alignment)
Sets the alignment of the widgets.
virtual FlowContainer::Alignment getAlignment() const
Gets the alignment of the widgets.
void expandContent()
Expands the child widgets to the size of this widget, calls recursively all childs.
Definition widget.hpp:1440
void setLayout(Container::LayoutPolicy policy) override
Sets the layout of the container.
void resizeToContent()
Resizes the widget's size to fit the content exactly, calls recursively all childs.
Definition widget.hpp:1417
virtual void adjustContent()
Sets the size of the container and sorts the children.
Alignment
Alignments for widgets.
Represents a rectangular area (X, Y, Width, Height).
Definition rectangle.hpp:20
int width
Holds the width of the rectangle.
int y
Holds the x coordinate of the rectangle.
int x
Holds the x coordinate of the rectangle.
int height
Holds the height of the rectangle.
Abstract base class defining the common behavior, properties, and lifecycle of all GUI elements.
Definition widget.hpp:45
bool isHorizontalExpand() const
Gets if widget is horizontal expandable.
Definition widget.cpp:347
int getWidth() const
Gets the width of the widget.
Definition widget.cpp:170
virtual Widget * getParent() const
Gets the widget's parent container.
Definition widget.cpp:157
int getMarginBottom() const
Gets the bottom margin.
Definition widget.cpp:419
void setSize(int width, int height)
Sets the size of the widget.
Definition widget.cpp:855
bool isVisible() const
Checks if the widget is visible.
Definition widget.cpp:578
int getMarginLeft() const
Gets the left margin.
Definition widget.cpp:429
int getMarginRight() const
Gets the right margin.
Definition widget.cpp:409
Rectangle const & getDimension() const
Gets the dimension of the widget.
Definition widget.cpp:482
Size const & getMinSize() const
Gets the minimal dimension of the widget.
Definition widget.cpp:277
std::list< Widget * > mChildren
Holds all children of the widget.
Definition widget.hpp:1842
int getMarginTop() const
Gets the top margin.
Definition widget.cpp:399
int getHeight() const
Gets the height of the widget.
Definition widget.cpp:183
bool isVerticalExpand() const
Gets if widget is vertical expandable.
Definition widget.cpp:337