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