FTXUI  4.1.1
C++ functional terminal UI.
Loading...
Searching...
No Matches
flexbox_helper.cpp
Go to the documentation of this file.
2
3#include <algorithm> // for max, min
4#include <cstddef> // for size_t
5#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
6#include <memory> // for allocator_traits<>::value_type
7#include <utility> // for swap, move
8
9#include "ftxui/dom/box_helper.hpp" // for Element, Compute
10
12
13namespace {
14void SymmetryXY(FlexboxConfig& c) {
15 std::swap(c.gap_x, c.gap_y);
16 switch (c.direction) {
19 break;
22 break;
25 break;
28 break;
29 }
30}
31
32void SymmetryX(FlexboxConfig& c) {
33 switch (c.direction) {
36 break;
39 break;
40 default:
41 break;
42 }
43}
44
45void SymmetryY(FlexboxConfig& c) {
46 switch (c.wrap) {
48 break;
51 break;
54 break;
55 }
56}
57
58void SymmetryXY(Global& g) {
59 SymmetryXY(g.config);
60 std::swap(g.size_x, g.size_y);
61 for (auto& b : g.blocks) {
62 std::swap(b.min_size_x, b.min_size_y);
63 std::swap(b.flex_grow_x, b.flex_grow_y);
64 std::swap(b.flex_shrink_x, b.flex_shrink_y);
65 std::swap(b.x, b.y);
66 std::swap(b.dim_x, b.dim_y);
67 }
68}
69
70void SymmetryX(Global& g) {
71 SymmetryX(g.config);
72 for (auto& b : g.blocks) {
73 b.x = g.size_x - b.x - b.dim_x;
74 }
75}
76
77void SymmetryY(Global& g) {
78 SymmetryY(g.config);
79 for (auto& b : g.blocks) {
80 b.y = g.size_y - b.y - b.dim_y;
81 }
82}
83
84struct Line {
85 std::vector<Block*> blocks;
86};
87
88void SetX(Global& global, std::vector<Line> lines) {
89 for (auto& line : lines) {
90 std::vector<box_helper::Element> elements;
91 for (auto* block : line.blocks) {
92 box_helper::Element element;
93 element.min_size = block->min_size_x;
94 element.flex_grow =
95 block->flex_grow_x != 0 || global.config.justify_content ==
97 ? 1
98 : 0;
99 element.flex_shrink = block->flex_shrink_x;
100 elements.push_back(element);
101 }
102
104 &elements,
105 global.size_x - global.config.gap_x * (int(line.blocks.size()) - 1));
106
107 int x = 0;
108 for (size_t i = 0; i < line.blocks.size(); ++i) {
109 line.blocks[i]->dim_x = elements[i].size;
110 line.blocks[i]->x = x;
111 x += elements[i].size;
112 x += global.config.gap_x;
113 }
114 }
115}
116
117// NOLINTNEXTLINE(readability-function-cognitive-complexity)
118void SetY(Global& g, std::vector<Line> lines) {
119 std::vector<box_helper::Element> elements;
120 for (auto& line : lines) {
121 box_helper::Element element;
122 element.flex_shrink = line.blocks.front()->flex_shrink_y;
123 element.flex_grow = line.blocks.front()->flex_grow_y;
124 for (auto* block : line.blocks) {
125 element.min_size = std::max(element.min_size, block->min_size_y);
126 element.flex_shrink = std::min(element.flex_shrink, block->flex_shrink_y);
127 element.flex_grow = std::min(element.flex_grow, block->flex_grow_y);
128 }
129 elements.push_back(element);
130 }
131
132 // box_helper::Compute(&elements, g.size_y);
133 box_helper::Compute(&elements, 10000); // NOLINT
134
135 // [Align-content]
136 std::vector<int> ys(elements.size());
137 int y = 0;
138 for (size_t i = 0; i < elements.size(); ++i) {
139 ys[i] = y;
140 y += elements[i].size;
141 y += g.config.gap_y;
142 }
143 int remaining_space = std::max(0, g.size_y - y);
144 switch (g.config.align_content) {
146 break;
147 }
148
150 for (size_t i = 0; i < ys.size(); ++i) { // NOLINT
151 ys[i] += remaining_space;
152 }
153 break;
154 }
155
157 for (size_t i = 0; i < ys.size(); ++i) { // NOLINT
158 ys[i] += remaining_space / 2;
159 }
160 break;
161 }
162
164 for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
165 const int shifted = remaining_space * (i + 0) / (i + 1);
166 ys[i] += shifted;
167 const int consumed = remaining_space - shifted;
168 elements[i].size += consumed;
169 remaining_space -= consumed;
170 }
171 break;
172 }
173
175 for (int i = static_cast<int>(ys.size()) - 1; i >= 1; --i) { // NOLINT
176 ys[i] += remaining_space;
177 remaining_space = remaining_space * (i - 1) / i;
178 }
179 break;
180 }
181
183 for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
184 ys[i] += remaining_space * (2 * i + 1) / (2 * i + 2);
185 remaining_space = remaining_space * (2 * i) / (2 * i + 2);
186 }
187 break;
188 }
189
191 for (int i = static_cast<int>(ys.size()) - 1; i >= 0; --i) { // NOLINT
192 ys[i] += remaining_space * (i + 1) / (i + 2);
193 remaining_space = remaining_space * (i + 1) / (i + 2);
194 }
195 break;
196 }
197 }
198
199 // [Align items]
200 for (size_t i = 0; i < lines.size(); ++i) {
201 auto& element = elements[i];
202 for (auto* block : lines[i].blocks) {
203 const bool stretch =
204 block->flex_grow_y != 0 ||
206 const int size =
207 stretch ? element.size : std::min(element.size, block->min_size_y);
208 switch (g.config.align_items) {
210 block->y = ys[i];
211 block->dim_y = size;
212 break;
213 }
214
216 block->y = ys[i] + (element.size - size) / 2;
217 block->dim_y = size;
218 break;
219 }
220
222 block->y = ys[i] + element.size - size;
223 block->dim_y = size;
224 break;
225 }
226
228 block->y = ys[i];
229 block->dim_y = element.size;
230 break;
231 }
232 }
233 }
234 }
235}
236
237void JustifyContent(Global& g, std::vector<Line> lines) {
238 for (auto& line : lines) {
239 Block* last = line.blocks.back();
240 int remaining_space = g.size_x - last->x - last->dim_x;
241 switch (g.config.justify_content) {
244 break;
245
247 for (auto* block : line.blocks) {
248 block->x += remaining_space;
249 }
250 break;
251 }
252
254 for (auto* block : line.blocks) {
255 block->x += remaining_space / 2;
256 }
257 break;
258 }
259
261 for (int i = (int)line.blocks.size() - 1; i >= 1; --i) {
262 line.blocks[i]->x += remaining_space;
263 remaining_space = remaining_space * (i - 1) / i;
264 }
265 break;
266 }
267
269 for (int i = (int)line.blocks.size() - 1; i >= 0; --i) {
270 line.blocks[i]->x += remaining_space * (2 * i + 1) / (2 * i + 2);
271 remaining_space = remaining_space * (2 * i) / (2 * i + 2);
272 }
273 break;
274 }
275
277 for (int i = (int)line.blocks.size() - 1; i >= 0; --i) {
278 line.blocks[i]->x += remaining_space * (i + 1) / (i + 2);
279 remaining_space = remaining_space * (i + 1) / (i + 2);
280 }
281 break;
282 }
283 }
284 }
285}
286} // namespace
287
288namespace {
289
290void Compute1(Global& global);
291void Compute2(Global& global);
292void Compute3(Global& global);
293
294void Compute1(Global& global) {
296 SymmetryX(global);
297 Compute2(global);
298 SymmetryX(global);
299 return;
300 }
301 Compute2(global);
302}
303
304void Compute2(Global& global) {
306 SymmetryY(global);
307 Compute3(global);
308 SymmetryY(global);
309 return;
310 }
311 Compute3(global);
312}
313
314void Compute3(Global& global) {
315 // Step 1: Lay out every elements into rows:
316 std::vector<Line> lines;
317 {
318 Line line;
319 int x = 0;
320 for (auto& block : global.blocks) {
321 // Does it fit the end of the row?
322 // No? Then we need to start a new one:
323 if (x + block.min_size_x > global.size_x) {
324 x = 0;
325 if (!line.blocks.empty()) {
326 lines.push_back(std::move(line));
327 }
328 line = Line();
329 }
330
331 block.line = (int)lines.size();
332 block.line_position = (int)line.blocks.size();
333 line.blocks.push_back(&block);
334 x += block.min_size_x + global.config.gap_x;
335 }
336 if (!line.blocks.empty()) {
337 lines.push_back(std::move(line));
338 }
339 }
340
341 // Step 2: Set positions on the X axis.
342 SetX(global, lines);
343 JustifyContent(global, lines); // Distribute remaining space.
344
345 // Step 3: Set positions on the Y axis.
346 SetY(global, lines);
347}
348
349} // namespace
350
351void Compute(Global& global) {
354 SymmetryXY(global);
355 Compute1(global);
356 SymmetryXY(global);
357 return;
358 }
359 Compute1(global);
360}
361
362} // namespace ftxui::flexbox_helper
363
364// Copyright 2021 Arthur Sonzogni. All rights reserved.
365// Use of this source code is governed by the MIT license that can be found in
366// the LICENSE file.
void Compute(std::vector< Element > *elements, int target_size)
void Compute(Global &global)
Decorator size(Direction, Constraint, int value)
Apply a constraint on the size of an element.
Definition size.cpp:85
@ 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.