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