FifeGUI 0.3.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// Corresponding header include
6#include "fifechan/widgets/flowcontainer.hpp"
7
8// Standard library includes
9#include <algorithm>
10#include <cassert>
11#include <list>
12#include <vector>
13
14// Project headers (subdirs before local)
15#include "fifechan/exception.hpp"
16
17namespace fcn
18{
19
20 FlowContainer::FlowContainer()
21 {
22 setOpaque(true);
23 }
24
25 FlowContainer::~FlowContainer() = default;
26
28 {
29 mAlignment = alignment;
30 }
31
36
38 {
39 // diff means border, padding, ...
40 int const diffW = std::abs(getDimension().width - getChildrenArea().width);
41 int const diffH = std::abs(getDimension().height - getChildrenArea().height);
42
43 int const containerW = getChildrenArea().width;
44 int const containerH = getChildrenArea().height;
45
46 // calculates max layout size per column or row
47 std::vector<int> layoutMax;
48 int tmpSize = 0;
49 int x = 0;
50 int y = 0;
51 int visibleChilds = 0;
52 std::list<Widget*>::const_iterator currChild(mChildren.begin());
53 std::list<Widget*>::const_iterator endChildren(mChildren.end());
54 for (; currChild != endChildren; ++currChild) {
55 Widget const * child = *currChild;
56 if (!child->isVisible()) {
57 continue;
58 }
59 ++visibleChilds;
60 Rectangle const & rec = child->getDimension();
61 if (mLayout == LayoutPolicy::Vertical) {
62 // new column
63 // negative bottom margin have no effect here
64 if (y + child->getMarginTop() + rec.height +
65 (child->getMarginBottom() > 0 ? child->getMarginBottom() : 0) >
66 containerH) {
67 y = 0;
68 layoutMax.push_back(tmpSize);
69 tmpSize = 0;
70 }
71 y += rec.height + child->getMarginTop() + child->getMarginBottom() + getVerticalSpacing();
72 tmpSize = std::max(
73 tmpSize,
74 rec.width + child->getMarginLeft() + (child->getMarginRight() > 0 ? child->getMarginRight() : 0));
75 } else {
76 // new row
77 // negative right margin have no effect here
78 if (x + rec.width + child->getMarginLeft() +
79 (child->getMarginRight() > 0 ? child->getMarginRight() : 0) >
80 containerW) {
81 x = 0;
82 layoutMax.push_back(tmpSize);
83 tmpSize = 0;
84 }
85 x += rec.width + child->getMarginLeft() + child->getMarginRight() + getHorizontalSpacing();
86 tmpSize = std::max(
87 tmpSize,
88 rec.height + child->getMarginTop() + (child->getMarginBottom() > 0 ? child->getMarginBottom() : 0));
89 }
90 }
91 if (tmpSize != 0) {
92 layoutMax.push_back(tmpSize);
93 }
94
95 // places all widgets
96 x = 0;
97 y = 0;
98 int totalW = 0;
99 int totalH = 0;
100 unsigned int layout = 0;
101 if (mLayout == LayoutPolicy::Vertical && visibleChilds > 0) {
102 currChild = mChildren.begin();
103 endChildren = mChildren.end();
104 for (; currChild != endChildren; ++currChild) {
105 if (!(*currChild)->isVisible()) {
106 continue;
107 }
108 int columnW = layoutMax.at(layout);
109 int const layoutW = (*currChild)->getWidth() + (*currChild)->getMarginLeft() +
110 ((*currChild)->getMarginRight() > 0 ? (*currChild)->getMarginRight() : 0);
111 Rectangle dim(
112 (*currChild)->getMarginLeft(), (*currChild)->getMarginTop(), layoutW, (*currChild)->getHeight());
113
114 // new column
115 // negative bottom margin have no effect here
116 if (y + (*currChild)->getMarginTop() + dim.height +
117 ((*currChild)->getMarginBottom() > 0 ? (*currChild)->getMarginBottom() : 0) >
118 containerH) {
119 x += columnW + getHorizontalSpacing();
120 y = 0;
121 ++layout;
122 columnW = layoutMax.at(layout);
123 }
124 dim.y += y;
125
126 switch (getAlignment()) {
127 case Alignment::Left:
128 dim.x += x;
129 break;
130 case Alignment::Center:
131 dim.x += x + ((columnW - layoutW) / 2);
132 break;
133 case Alignment::Right:
134 dim.x += x + (columnW - layoutW);
135 break;
136 default:
137 throwException("Unknown alignment.");
138 }
139
140 (*currChild)->setDimension(dim);
141 y += (*currChild)->getHeight() + (*currChild)->getMarginTop() + (*currChild)->getMarginBottom() +
143 totalW = std::max(totalW, dim.x + dim.width);
144 totalH = std::max(totalH, y);
145 }
146 // remove last spacing
147 totalH -= getVerticalSpacing();
148 // always expand height but width only if horizontal expand is enabled
149 totalH = std::max(totalH, containerH);
150 if (isHorizontalExpand()) {
151 totalW = std::max(totalW, containerW);
152 }
153 } else if (mLayout == LayoutPolicy::Horizontal && visibleChilds > 0) {
154 currChild = mChildren.begin();
155 endChildren = mChildren.end();
156 for (; currChild != endChildren; ++currChild) {
157 if (!(*currChild)->isVisible()) {
158 continue;
159 }
160 int rowH = layoutMax.at(layout);
161 int const layoutH = (*currChild)->getHeight() + (*currChild)->getMarginTop() +
162 ((*currChild)->getMarginBottom() > 0 ? (*currChild)->getMarginBottom() : 0);
163 Rectangle dim(
164 (*currChild)->getMarginLeft(), (*currChild)->getMarginTop(), (*currChild)->getWidth(), layoutH);
165
166 // new row
167 // negative right margin have no effect here
168 if (x + (*currChild)->getMarginLeft() + dim.width +
169 ((*currChild)->getMarginRight() > 0 ? (*currChild)->getMarginRight() : 0) >
170 containerW) {
171 x = 0;
172 y += rowH + getVerticalSpacing();
173 ++layout;
174 rowH = layoutMax.at(layout);
175 }
176 dim.x += x;
177
178 switch (getAlignment()) {
179 case Alignment::Top:
180 dim.y += y;
181 break;
182 case Alignment::Center:
183 dim.y += y + ((rowH - layoutH) / 2);
184 break;
185 case Alignment::Bottom:
186 dim.y += y + (rowH - layoutH);
187 break;
188 default:
189 throwException("Unknown alignment.");
190 }
191
192 (*currChild)->setDimension(dim);
193 x += (*currChild)->getWidth() + (*currChild)->getMarginLeft() + (*currChild)->getMarginRight() +
195 totalW = std::max(totalW, x);
196 totalH = std::max(totalH, dim.y + dim.height);
197 }
198 // remove last spacing
199 totalW -= getHorizontalSpacing();
200 // always expand width but height only if vertical expand is enabled
201 totalW = std::max(totalW, containerW);
202 if (isVerticalExpand()) {
203 totalH = std::max(totalH, containerH);
204 }
205 }
206
207 // resize container
208 totalW += diffW;
209 totalH += diffH;
210 setSize(totalW, totalH);
211 }
212
214 {
215 if (policy == LayoutPolicy::Circular) {
216 throwException("Circular layout is not implemented for the FlowContainer.");
217 } else {
218 Container::setLayout(policy);
219 }
220 }
221
223 {
224 if (recursion) {
225 std::list<Widget*>::const_iterator currChild(mChildren.begin());
226 std::list<Widget*>::const_iterator const endChildren(mChildren.end());
227 for (; currChild != endChildren; ++currChild) {
228 if (!(*currChild)->isVisible()) {
229 continue;
230 }
231 (*currChild)->resizeToContent(recursion);
232 }
233 }
234
235 if (mLayout != Container::LayoutPolicy::Absolute) {
236 if (getParent() != nullptr) {
238 }
239 }
240 }
241
242 void FlowContainer::expandContent(bool recursion)
243 {
244 if (mLayout != Container::LayoutPolicy::Absolute) {
246 }
247 // not really needed
248 if (recursion) {
249 std::list<Widget*>::const_iterator currChild(mChildren.begin());
250 std::list<Widget*>::const_iterator const endChildren(mChildren.end());
251 for (; currChild != endChildren; ++currChild) {
252 if (!(*currChild)->isVisible()) {
253 continue;
254 }
255 (*currChild)->expandContent(recursion);
256 }
257 }
258 }
259
260} // 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:50
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:83
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 resizeToContent(bool recursion=true) override
Resize flow layout to fit its children.
void expandContent()
Expands the child widgets to the size of this widget, calls recursively all childs.
Definition widget.hpp:1597
void setLayout(Container::LayoutPolicy policy) override
Sets the layout of the container.
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:22
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:56
bool isHorizontalExpand() const
Gets if widget is horizontal expandable.
Definition widget.cpp:440
int getWidth() const
Gets the width of the widget.
Definition widget.cpp:252
virtual Widget * getParent() const
Gets the widget's parent container.
Definition widget.cpp:239
int getMarginBottom() const
Gets the bottom margin.
Definition widget.cpp:546
virtual void setSize(int width, int height)
Sets the size of the widget.
Definition widget.cpp:1065
bool isVisible() const
Checks if the widget is visible.
Definition widget.cpp:724
int getMarginLeft() const
Gets the left margin.
Definition widget.cpp:556
int getMarginRight() const
Gets the right margin.
Definition widget.cpp:536
Rectangle const & getDimension() const
Gets the dimension of the widget.
Definition widget.cpp:609
Size const & getMinSize() const
Gets the minimal dimension of the widget.
Definition widget.cpp:369
std::list< Widget * > mChildren
Holds all children of the widget.
Definition widget.hpp:2099
int getMarginTop() const
Gets the top margin.
Definition widget.cpp:526
int getHeight() const
Gets the height of the widget.
Definition widget.cpp:265
bool isVerticalExpand() const
Gets if widget is vertical expandable.
Definition widget.cpp:430
Used replacement tokens by configure_file():
void throwException(std::string const &message, std::source_location location=std::source_location::current())
Throw an Exception capturing the current source location.