FTXUI 6.1.9 C++ functional terminal UI.

Loading...
Searching...
No Matches
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.
4#include "ftxui/dom/flexbox_helper.hpp"
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
14 namespace ftxui::flexbox_helper {
15
16 namespace {
17 void SymmetryXY(FlexboxConfig& c) {
18 std::swap(c.gap_x, c.gap_y);
19 switch (c.direction) {
20 case FlexboxConfig::Direction::Row:
21 c.direction = FlexboxConfig::Direction::Column;
22 break;
23 case FlexboxConfig::Direction::RowInversed:
24 c.direction = FlexboxConfig::Direction::ColumnInversed;
25 break;
26 case FlexboxConfig::Direction::Column:
27 c.direction = FlexboxConfig::Direction::Row;
28 break;
29 case FlexboxConfig::Direction::ColumnInversed:
30 c.direction = FlexboxConfig::Direction::RowInversed;
31 break;
32 }
33}
34
35 void SymmetryX(FlexboxConfig& c) {
36 switch (c.direction) {
37 case FlexboxConfig::Direction::Row:
38 c.direction = FlexboxConfig::Direction::RowInversed;
39 break;
40 case FlexboxConfig::Direction::RowInversed:
41 c.direction = FlexboxConfig::Direction::Row;
42 break;
43 default:
44 break;
45 }
46}
47
48 void SymmetryY(FlexboxConfig& c) {
49 switch (c.wrap) {
50 case FlexboxConfig::Wrap::NoWrap:
51 break;
52 case FlexboxConfig::Wrap::Wrap:
53 c.wrap = FlexboxConfig::Wrap::WrapInversed;
54 break;
55 case FlexboxConfig::Wrap::WrapInversed:
56 c.wrap = FlexboxConfig::Wrap::Wrap;
57 break;
58 }
59}
60
61 void SymmetryXY(Global& g) {
62 SymmetryXY(g.config);
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
77 void SymmetryX(Global& g) {
78 SymmetryX(g.config);
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
87 void SymmetryY(Global& g) {
88 SymmetryY(g.config);
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
97 void 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 ==
106 FlexboxConfig::JustifyContent::Stretch
107 ? 1
108 : 0;
109 element.flex_shrink = block->flex_shrink_x;
110 elements.push_back(element);
111 }
112
113 box_helper::Compute(
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)
133 void 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) {
161 case FlexboxConfig::AlignContent::FlexStart: {
162 break;
163 }
164
165 case FlexboxConfig::AlignContent::FlexEnd: {
166 for (size_t i = 0; i < ys.size(); ++i) { // NOLINT
167 ys[i] += remaining_space;
168 }
169 break;
170 }
171
172 case FlexboxConfig::AlignContent::Center: {
173 for (size_t i = 0; i < ys.size(); ++i) { // NOLINT
174 ys[i] += remaining_space / 2;
175 }
176 break;
177 }
178
179 case FlexboxConfig::AlignContent::Stretch: {
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;
185 remaining_space -= consumed;
186 }
187 break;
188 }
189
190 case FlexboxConfig::AlignContent::SpaceBetween: {
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
198 case FlexboxConfig::AlignContent::SpaceAround: {
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
206 case FlexboxConfig::AlignContent::SpaceEvenly: {
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 ||
221 g.config.align_content == FlexboxConfig::AlignContent::Stretch;
222 const int size =
223 stretch ? element.size : std::min(element.size, block->min_size_y);
224 switch (g.config.align_items) {
225 case FlexboxConfig::AlignItems::FlexStart: {
226 block->y = ys[i];
227 block->dim_y = size;
228 break;
229 }
230
231 case FlexboxConfig::AlignItems::Center: {
232 block->y = ys[i] + (element.size - size) / 2;
233 block->dim_y = size;
234 break;
235 }
236
237 case FlexboxConfig::AlignItems::FlexEnd: {
238 block->y = ys[i] + element.size - size;
239 block->dim_y = size;
240 break;
241 }
242
243 case FlexboxConfig::AlignItems::Stretch: {
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
259 void 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) {
264 case FlexboxConfig::JustifyContent::FlexStart:
265 case FlexboxConfig::JustifyContent::Stretch:
266 break;
267
268 case FlexboxConfig::JustifyContent::FlexEnd: {
269 for (auto* block : line.blocks) {
270 block->x += remaining_space;
271 }
272 break;
273 }
274
275 case FlexboxConfig::JustifyContent::Center: {
276 for (auto* block : line.blocks) {
277 block->x += remaining_space / 2;
278 }
279 break;
280 }
281
282 case FlexboxConfig::JustifyContent::SpaceBetween: {
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
290 case FlexboxConfig::JustifyContent::SpaceAround: {
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
298 case FlexboxConfig::JustifyContent::SpaceEvenly: {
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
309 void Compute1(Global& global);
310 void Compute2(Global& global);
311 void Compute3(Global& global);
312
313 void Compute1(Global& global) {
314 if (global.config.direction == FlexboxConfig::Direction::RowInversed) {
315 SymmetryX(global);
316 Compute2(global);
317 SymmetryX(global);
318 return;
319 }
320 Compute2(global);
321}
322
323 void Compute2(Global& global) {
324 if (global.config.wrap == FlexboxConfig::Wrap::WrapInversed) {
325 SymmetryY(global);
326 Compute3(global);
327 SymmetryY(global);
328 return;
329 }
330 Compute3(global);
331}
332
333 void 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
369 void Compute(Global& global) {
370 if (global.config.direction == FlexboxConfig::Direction::Column ||
371 global.config.direction == FlexboxConfig::Direction::ColumnInversed) {
372 SymmetryXY(global);
373 Compute1(global);
374 SymmetryXY(global);
375 return;
376 }
377 Compute1(global);
378}
379
380} // namespace ftxui::flexbox_helper
box_helper.hpp
flexbox_config.hpp
flexbox_helper.hpp
ftxui::FlexboxConfig::align_content
AlignContent align_content
Definition flexbox_config.hpp:105
ftxui::FlexboxConfig::gap_x
int gap_x
Definition flexbox_config.hpp:107
ftxui::FlexboxConfig::align_items
AlignItems align_items
Definition flexbox_config.hpp:89
ftxui::FlexboxConfig::AlignContent::SpaceEvenly
@ SpaceEvenly
ftxui::FlexboxConfig::AlignContent::Center
@ Center
items are centered along the cross axis.
ftxui::FlexboxConfig::AlignContent::FlexStart
@ FlexStart
items are placed at the start of the cross axis.
ftxui::FlexboxConfig::AlignContent::FlexEnd
@ FlexEnd
items are placed at the end of the cross axis.
ftxui::FlexboxConfig::AlignContent::SpaceAround
@ SpaceAround
ftxui::FlexboxConfig::AlignContent::SpaceBetween
@ SpaceBetween
items are evenly distributed in the cross axis.
ftxui::FlexboxConfig::AlignContent::Stretch
@ Stretch
items are stretched to fill the cross axis.
ftxui::FlexboxConfig::Direction::Column
@ Column
Flex items are laid out in a column.
ftxui::FlexboxConfig::Direction::ColumnInversed
@ ColumnInversed
ftxui::FlexboxConfig::Direction::Row
@ Row
Flex items are laid out in a row.
ftxui::FlexboxConfig::Direction::RowInversed
@ RowInversed
Flex items are laid out in a row, but in reverse order.
ftxui::FlexboxConfig::direction
Direction direction
Definition flexbox_config.hpp:39
ftxui::FlexboxConfig::wrap
Wrap wrap
Definition flexbox_config.hpp:49
ftxui::FlexboxConfig::Wrap::NoWrap
@ NoWrap
Flex items will all try to fit onto one line.
ftxui::FlexboxConfig::Wrap::Wrap
@ Wrap
Flex items will wrap onto multiple lines.
ftxui::FlexboxConfig::Wrap::WrapInversed
@ WrapInversed
ftxui::FlexboxConfig::AlignItems::Center
@ Center
items are centered along the cross axis.
ftxui::FlexboxConfig::AlignItems::FlexStart
@ FlexStart
items are placed at the start of the cross axis.
ftxui::FlexboxConfig::AlignItems::FlexEnd
@ FlexEnd
items are placed at the end of the cross axis.
ftxui::FlexboxConfig::AlignItems::Stretch
@ Stretch
items are stretched to fill the cross axis.
ftxui::FlexboxConfig::gap_y
int gap_y
Definition flexbox_config.hpp:108
ftxui::FlexboxConfig::justify_content
JustifyContent justify_content
Definition flexbox_config.hpp:78
ftxui::FlexboxConfig::JustifyContent::SpaceEvenly
@ SpaceEvenly
ftxui::FlexboxConfig::JustifyContent::Center
@ Center
Items are centered along the line.
ftxui::FlexboxConfig::JustifyContent::FlexStart
@ FlexStart
Items are aligned to the start of flexbox's direction.
ftxui::FlexboxConfig::JustifyContent::FlexEnd
@ FlexEnd
Items are aligned to the end of flexbox's direction.
ftxui::FlexboxConfig::JustifyContent::SpaceAround
@ SpaceAround
ftxui::FlexboxConfig::JustifyContent::SpaceBetween
@ SpaceBetween
Items are evenly distributed in the line; first item is on the start.
ftxui::FlexboxConfig::JustifyContent::Stretch
@ Stretch
Items are stretched to fill the line.
ftxui::size
Decorator size(WidthOrHeight, Constraint, int value)
Apply a constraint on the size of an element.
Definition src/ftxui/dom/size.cpp:89
ftxui::FlexboxConfig
FlexboxConfig is a configuration structure that defines the layout properties for a flexbox container...
Definition flexbox_config.hpp:27
ftxui::box_helper::Compute
void Compute(std::vector< Element > *elements, int target_size)
Definition box_helper.cpp:65
ftxui::flexbox_helper
Definition flexbox_helper.cpp:14
ftxui::flexbox_helper::Compute
void Compute(Global &global)
Definition flexbox_helper.cpp:369
ftxui::box_helper::Element
Definition box_helper.hpp:12
ftxui::box_helper::Element::flex_grow
int flex_grow
Definition box_helper.hpp:15
ftxui::box_helper::Element::flex_shrink
int flex_shrink
Definition box_helper.hpp:16
ftxui::box_helper::Element::min_size
int min_size
Definition box_helper.hpp:14
ftxui::flexbox_helper::Block
Definition flexbox_helper.hpp:13
ftxui::flexbox_helper::Block::x
int x
Definition flexbox_helper.hpp:25
ftxui::flexbox_helper::Block::dim_x
int dim_x
Definition flexbox_helper.hpp:27
ftxui::flexbox_helper::Global
Definition flexbox_helper.hpp:41
ftxui::flexbox_helper::Global::size_y
int size_y
Definition flexbox_helper.hpp:46
ftxui::flexbox_helper::Global::lines
std::vector< Line > lines
Definition flexbox_helper.hpp:43
ftxui::flexbox_helper::Global::blocks
std::vector< Block > blocks
Definition flexbox_helper.hpp:42
ftxui::flexbox_helper::Global::config
FlexboxConfig config
Definition flexbox_helper.hpp:44
ftxui::flexbox_helper::Global::size_x
int size_x
Definition flexbox_helper.hpp:45
ftxui::flexbox_helper::Line
Definition flexbox_helper.hpp:33
ftxui::flexbox_helper::Line::blocks
std::vector< Block * > blocks
Definition flexbox_helper.hpp:34