FTXUI  6.0.2
C++ functional terminal UI.
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
flexbox_helper.cpp
Go to the documentation of this file.
1// Copyright 2021 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
5
6#include <algorithm> // for max, min
7#include <cstddef> // for size_t
8#include <ftxui/dom/flexbox_config.hpp> // for FlexboxConfig, FlexboxConfig::Direction, FlexboxConfig::AlignContent, FlexboxConfig::JustifyContent, FlexboxConfig::Wrap, FlexboxConfig::Direction::RowInversed, FlexboxConfig::AlignItems, FlexboxConfig::Direction::Row, FlexboxConfig::Direction::Column, FlexboxConfig::Direction::ColumnInversed, FlexboxConfig::Wrap::WrapInversed, FlexboxConfig::AlignContent::Stretch, FlexboxConfig::JustifyContent::Stretch, FlexboxConfig::Wrap::Wrap, FlexboxConfig::AlignContent::Center, FlexboxConfig::AlignContent::FlexEnd, FlexboxConfig::AlignContent::FlexStart, FlexboxConfig::AlignContent::SpaceAround, FlexboxConfig::AlignContent::SpaceBetween, FlexboxConfig::AlignContent::SpaceEvenly, FlexboxConfig::AlignItems::Center, FlexboxConfig::AlignItems::FlexEnd, FlexboxConfig::AlignItems::FlexStart, FlexboxConfig::AlignItems::Stretch, FlexboxConfig::JustifyContent::Center, FlexboxConfig::JustifyContent::FlexEnd, FlexboxConfig::JustifyContent::FlexStart, FlexboxConfig::JustifyContent::SpaceAround, FlexboxConfig::JustifyContent::SpaceBetween, FlexboxConfig::JustifyContent::SpaceEvenly, FlexboxConfig::Wrap::NoWrap
9#include <utility> // for swap, move
10#include <vector>
11
12#include "ftxui/dom/box_helper.hpp" // for Element, Compute
13
15
16namespace {
18 std::swap(c.gap_x, c.gap_y);
19 switch (c.direction) {
22 break;
25 break;
28 break;
31 break;
32 }
33}
34
35void SymmetryX(FlexboxConfig& c) {
36 switch (c.direction) {
39 break;
42 break;
43 default:
44 break;
45 }
46}
47
48void SymmetryY(FlexboxConfig& c) {
49 switch (c.wrap) {
51 break;
54 break;
57 break;
58 }
59}
60
61void SymmetryXY(Global& g) {
63 std::swap(g.size_x, g.size_y);
64 for (auto& b : g.blocks) {
65 std::swap(b.min_size_x, b.min_size_y);
66 std::swap(b.flex_grow_x, b.flex_grow_y);
67 std::swap(b.flex_shrink_x, b.flex_shrink_y);
68 std::swap(b.x, b.y);
69 std::swap(b.dim_x, b.dim_y);
70 }
71 for (auto& l : g.lines) {
72 std::swap(l.x, l.y);
73 std::swap(l.dim_x, l.dim_y);
74 }
75}
76
77void SymmetryX(Global& g) {
79 for (auto& b : g.blocks) {
80 b.x = g.size_x - b.x - b.dim_x;
81 }
82 for (auto& l : g.lines) {
83 l.x = g.size_x - l.x - l.dim_x;
84 }
85}
86
87void SymmetryY(Global& g) {
89 for (auto& b : g.blocks) {
90 b.y = g.size_y - b.y - b.dim_y;
91 }
92 for (auto& l : g.lines) {
93 l.y = g.size_y - l.y - l.dim_y;
94 }
95}
96
97void SetX(Global& global) {
98 for (auto& line : global.lines) {
99 std::vector<box_helper::Element> elements;
100 elements.reserve(line.blocks.size());
101 for (auto* block : line.blocks) {
102 box_helper::Element element;
103 element.min_size = block->min_size_x;
104 element.flex_grow =
105 block->flex_grow_x != 0 || global.config.justify_content ==
107 ? 1
108 : 0;
109 element.flex_shrink = block->flex_shrink_x;
110 elements.push_back(element);
111 }
112
114 &elements,
115 global.size_x - global.config.gap_x * (int(line.blocks.size()) - 1));
116
117 int x = 0;
118 for (size_t i = 0; i < line.blocks.size(); ++i) {
119 line.blocks[i]->x = x;
120 line.blocks[i]->dim_x = elements[i].size;
121 x += elements[i].size;
122 x += global.config.gap_x;
123 }
124 }
125
126 for (auto& line : global.lines) {
127 line.x = 0;
128 line.dim_x = global.size_x;
129 }
130}
131
132// NOLINTNEXTLINE(readability-function-cognitive-complexity)
133void SetY(Global& g) {
134 std::vector<box_helper::Element> elements;
135 elements.reserve(g.lines.size());
136 for (auto& line : g.lines) {
137 box_helper::Element element;
138 element.flex_shrink = line.blocks.front()->flex_shrink_y;
139 element.flex_grow = line.blocks.front()->flex_grow_y;
140 for (auto* block : line.blocks) {
141 element.min_size = std::max(element.min_size, block->min_size_y);
142 element.flex_shrink = std::min(element.flex_shrink, block->flex_shrink_y);
143 element.flex_grow = std::min(element.flex_grow, block->flex_grow_y);
144 }
145 elements.push_back(element);
146 }
147
148 // box_helper::Compute(&elements, g.size_y);
149 box_helper::Compute(&elements, 10000); // NOLINT
150
151 // [Align-content]
152 std::vector<int> ys(elements.size());
153 int y = 0;
154 for (size_t i = 0; i < elements.size(); ++i) {
155 ys[i] = y;
156 y += elements[i].size;
157 y += g.config.gap_y;
158 }
159 int remaining_space = std::max(0, g.size_y - y);
160 switch (g.config.align_content) {
162 break;
163 }
164
166 for (size_t i = 0; i < ys.size(); ++i) { // NOLINT
167 ys[i] += remaining_space;
168 }
169 break;
170 }
171
173 for (size_t i = 0; i < ys.size(); ++i) { // NOLINT
174 ys[i] += remaining_space / 2;
175 }
176 break;
177 }
178
180 for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
181 const int shifted = remaining_space * (i + 0) / (i + 1);
182 ys[i] += shifted;
183 const int consumed = remaining_space - shifted;
184 elements[i].size += consumed;
186 }
187 break;
188 }
189
191 for (int i = static_cast<int>(ys.size()) - 1; i >= 1; --i) { // NOLINT
192 ys[i] += remaining_space;
193 remaining_space = remaining_space * (i - 1) / i;
194 }
195 break;
196 }
197
199 for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
200 ys[i] += remaining_space * (2 * i + 1) / (2 * i + 2);
201 remaining_space = remaining_space * (2 * i) / (2 * i + 2);
202 }
203 break;
204 }
205
207 for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
208 ys[i] += remaining_space * (i + 1) / (i + 2);
209 remaining_space = remaining_space * (i + 1) / (i + 2);
210 }
211 break;
212 }
213 }
214
215 // [Align items]
216 for (size_t i = 0; i < g.lines.size(); ++i) {
217 auto& element = elements[i];
218 for (auto* block : g.lines[i].blocks) {
219 const bool stretch =
220 block->flex_grow_y != 0 ||
222 const int size =
223 stretch ? element.size : std::min(element.size, block->min_size_y);
224 switch (g.config.align_items) {
226 block->y = ys[i];
227 block->dim_y = size;
228 break;
229 }
230
232 block->y = ys[i] + (element.size - size) / 2;
233 block->dim_y = size;
234 break;
235 }
236
238 block->y = ys[i] + element.size - size;
239 block->dim_y = size;
240 break;
241 }
242
244 block->y = ys[i];
245 block->dim_y = element.size;
246 break;
247 }
248 }
249 }
250 }
251
252 ys.push_back(g.size_y);
253 for (size_t i = 0; i < g.lines.size(); ++i) {
254 g.lines[i].y = ys[i];
255 g.lines[i].dim_y = ys[i + 1] - ys[i];
256 }
257}
258
259void JustifyContent(Global& g) {
260 for (auto& line : g.lines) {
261 Block* last = line.blocks.back();
262 int remaining_space = g.size_x - last->x - last->dim_x;
263 switch (g.config.justify_content) {
266 break;
267
269 for (auto* block : line.blocks) {
271 }
272 break;
273 }
274
276 for (auto* block : line.blocks) {
277 block->x += remaining_space / 2;
278 }
279 break;
280 }
281
283 for (int i = (int)line.blocks.size() - 1; i >= 1; --i) {
284 line.blocks[i]->x += remaining_space;
285 remaining_space = remaining_space * (i - 1) / i;
286 }
287 break;
288 }
289
291 for (int i = (int)line.blocks.size() - 1; i >= 0; --i) {
292 line.blocks[i]->x += remaining_space * (2 * i + 1) / (2 * i + 2);
293 remaining_space = remaining_space * (2 * i) / (2 * i + 2);
294 }
295 break;
296 }
297
299 for (int i = (int)line.blocks.size() - 1; i >= 0; --i) {
300 line.blocks[i]->x += remaining_space * (i + 1) / (i + 2);
301 remaining_space = remaining_space * (i + 1) / (i + 2);
302 }
303 break;
304 }
305 }
306 }
307}
308
309void Compute1(Global& global);
310void Compute2(Global& global);
311void Compute3(Global& global);
312
313void Compute1(Global& global) {
314 if (global.config.direction == FlexboxConfig::Direction::RowInversed) {
318 return;
319 }
321}
322
323void Compute2(Global& global) {
324 if (global.config.wrap == FlexboxConfig::Wrap::WrapInversed) {
328 return;
329 }
331}
332
333void Compute3(Global& global) {
334 // Step 1: Lay out every elements into rows:
335 {
336 Line line;
337 int x = 0;
338 for (auto& block : global.blocks) {
339 // Does it fit the end of the row?
340 // No? Then we need to start a new one:
341 if (x + block.min_size_x > global.size_x) {
342 x = 0;
343 if (!line.blocks.empty()) {
344 global.lines.push_back(std::move(line));
345 }
346 line = Line();
347 }
348
349 block.line = static_cast<int>(global.lines.size());
350 block.line_position = static_cast<int>(line.blocks.size());
351 line.blocks.push_back(&block);
352 x += block.min_size_x + global.config.gap_x;
353 }
354 if (!line.blocks.empty()) {
355 global.lines.push_back(std::move(line));
356 }
357 }
358
359 // Step 2: Set positions on the X axis.
360 SetX(global);
361 JustifyContent(global); // Distribute remaining space.
362
363 // Step 3: Set positions on the Y axis.
364 SetY(global);
365}
366
367} // namespace
368
370 if (global.config.direction == FlexboxConfig::Direction::Column ||
375 return;
376 }
378}
379
380} // namespace ftxui::flexbox_helper
void Compute(std::vector< Element > *elements, int target_size)
void Compute(Global &global)
std::vector< Block * > blocks
Decorator size(WidthOrHeight, Constraint, int value)
Apply a constraint on the size of an element.
Definition size.cpp:89
std::shared_ptr< T > Make(Args &&... args)
Definition component.hpp:26
@ Center
items are centered along the cross axis.
@ FlexStart
items are placed at the start of the cross axis.
@ FlexEnd
items are placed at the end of the cross axis.
@ SpaceBetween
items are evenly distributed in the cross axis.
@ Stretch
items are stretched to fill the cross axis.
@ Column
Flex items are laid out in a column.
@ Row
Flex items are laid out in a row.
@ RowInversed
Flex items are laid out in a row, but in reverse order.
@ NoWrap
Flex items will all try to fit onto one line.
@ Wrap
Flex items will wrap onto multiple lines.
@ Center
items are centered along the cross axis.
@ FlexStart
items are placed at the start of the cross axis.
@ FlexEnd
items are placed at the end of the cross axis.
@ Stretch
items are stretched to fill the cross axis.
JustifyContent justify_content
@ Center
Items are centered along the line.
@ FlexStart
Items are aligned to the start of flexbox's direction.
@ FlexEnd
Items are aligned to the end of flexbox's direction.
@ SpaceBetween
Items are evenly distributed in the line; first item is on the start.
@ Stretch
Items are stretched to fill the line.