FTXUI  5.0.0
C++ functional terminal UI.
gridbox.cpp
Go to the documentation of this file.
1 // Copyright 2020 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 <algorithm> // for max, min
5 #include <cstddef> // for size_t
6 #include <memory> // for __shared_ptr_access, shared_ptr, make_shared, allocator_traits<>::value_type
7 #include <utility> // for move
8 #include <vector> // for vector, __alloc_traits<>::value_type
9 
10 #include "ftxui/dom/box_helper.hpp" // for Element, Compute
11 #include "ftxui/dom/elements.hpp" // for Elements, filler, Element, gridbox
12 #include "ftxui/dom/node.hpp" // for Node
13 #include "ftxui/dom/requirement.hpp" // for Requirement
14 #include "ftxui/screen/box.hpp" // for Box
15 
16 namespace ftxui {
17 class Screen;
18 
19 namespace {
20 
21 // Accumulate the values of a list U[n] into v[n]. So that:
22 // V[0] = 0;
23 // V[n+1] = v[n] + U[n]
24 // return the sum of U[n].
25 int Integrate(std::vector<int>& elements) {
26  int accu = 0;
27  for (auto& i : elements) {
28  const int old_accu = accu;
29  accu += i;
30  i = old_accu;
31  }
32  return accu;
33 }
34 
35 class GridBox : public Node {
36  public:
37  explicit GridBox(std::vector<Elements> lines) : lines_(std::move(lines)) {
38  y_size = lines_.size();
39  for (const auto& line : lines_) {
40  x_size = std::max(x_size, int(line.size()));
41  }
42 
43  // Fill in empty cells, in case the user did not used the API correctly:
44  for (auto& line : lines_) {
45  while (line.size() < size_t(x_size)) {
46  line.push_back(filler());
47  }
48  }
49  }
50 
51  void ComputeRequirement() override {
52  requirement_.min_x = 0;
53  requirement_.min_y = 0;
54  requirement_.flex_grow_x = 0;
55  requirement_.flex_grow_y = 0;
56  requirement_.flex_shrink_x = 0;
57  requirement_.flex_shrink_y = 0;
58 
59  for (auto& line : lines_) {
60  for (auto& cell : line) {
61  cell->ComputeRequirement();
62  }
63  }
64 
65  // Compute the size of each columns/row.
66  std::vector<int> size_x(x_size, 0);
67  std::vector<int> size_y(y_size, 0);
68  for (int x = 0; x < x_size; ++x) {
69  for (int y = 0; y < y_size; ++y) {
70  size_x[x] = std::max(size_x[x], lines_[y][x]->requirement().min_x);
71  size_y[y] = std::max(size_y[y], lines_[y][x]->requirement().min_y);
72  }
73  }
74 
75  requirement_.min_x = Integrate(size_x);
76  requirement_.min_y = Integrate(size_y);
77 
78  // Forward the selected/focused child state:
79  requirement_.selection = Requirement::NORMAL;
80  for (int x = 0; x < x_size; ++x) {
81  for (int y = 0; y < y_size; ++y) {
82  if (requirement_.selection >= lines_[y][x]->requirement().selection) {
83  continue;
84  }
85  requirement_.selection = lines_[y][x]->requirement().selection;
86  requirement_.selected_box = lines_[y][x]->requirement().selected_box;
87  requirement_.selected_box.x_min += size_x[x];
88  requirement_.selected_box.x_max += size_x[x];
89  requirement_.selected_box.y_min += size_y[y];
90  requirement_.selected_box.y_max += size_y[y];
91  }
92  }
93  }
94 
95  void SetBox(Box box) override {
96  Node::SetBox(box);
97 
99  init.min_size = 0;
100  init.flex_grow = 1024; // NOLINT
101  init.flex_shrink = 1024; // NOLINT
102  std::vector<box_helper::Element> elements_x(x_size, init);
103  std::vector<box_helper::Element> elements_y(y_size, init);
104 
105  for (int y = 0; y < y_size; ++y) {
106  for (int x = 0; x < x_size; ++x) {
107  const auto& cell = lines_[y][x];
108  const auto& requirement = cell->requirement();
109  auto& e_x = elements_x[x];
110  auto& e_y = elements_y[y];
111  e_x.min_size = std::max(e_x.min_size, requirement.min_x);
112  e_y.min_size = std::max(e_y.min_size, requirement.min_y);
113  e_x.flex_grow = std::min(e_x.flex_grow, requirement.flex_grow_x);
114  e_y.flex_grow = std::min(e_y.flex_grow, requirement.flex_grow_y);
115  e_x.flex_shrink = std::min(e_x.flex_shrink, requirement.flex_shrink_x);
116  e_y.flex_shrink = std::min(e_y.flex_shrink, requirement.flex_shrink_y);
117  }
118  }
119 
120  const int target_size_x = box.x_max - box.x_min + 1;
121  const int target_size_y = box.y_max - box.y_min + 1;
122  box_helper::Compute(&elements_x, target_size_x);
123  box_helper::Compute(&elements_y, target_size_y);
124 
125  Box box_y = box;
126  int y = box_y.y_min;
127  for (int iy = 0; iy < y_size; ++iy) {
128  box_y.y_min = y;
129  y += elements_y[iy].size;
130  box_y.y_max = y - 1;
131 
132  Box box_x = box_y;
133  int x = box_x.x_min;
134  for (int ix = 0; ix < x_size; ++ix) {
135  box_x.x_min = x;
136  x += elements_x[ix].size;
137  box_x.x_max = x - 1;
138  lines_[iy][ix]->SetBox(box_x);
139  }
140  }
141  }
142 
143  void Render(Screen& screen) override {
144  for (auto& line : lines_) {
145  for (auto& cell : line) {
146  cell->Render(screen);
147  }
148  }
149  }
150 
151  int x_size = 0;
152  int y_size = 0;
153  std::vector<Elements> lines_;
154 };
155 } // namespace
156  //
157 /// @brief A container displaying a grid of elements.
158 /// @param lines A list of lines, each line being a list of elements.
159 /// @return The container.
160 ///
161 /// #### Example
162 ///
163 /// ```cpp
164 /// auto cell = [](const char* t) { return text(t) | border; };
165 /// auto document = gridbox({
166 /// {cell("north-west") , cell("north") , cell("north-east")} ,
167 /// {cell("west") , cell("center") , cell("east")} ,
168 /// {cell("south-west") , cell("south") , cell("south-east")} ,
169 /// });
170 /// ```
171 /// Output:
172 /// ```
173 /// ╭──────────╮╭──────╮╭──────────╮
174 /// │north-west││north ││north-east│
175 /// ╰──────────╯╰──────╯╰──────────╯
176 /// ╭──────────╮╭──────╮╭──────────╮
177 /// │west ││center││east │
178 /// ╰──────────╯╰──────╯╰──────────╯
179 /// ╭──────────╮╭──────╮╭──────────╮
180 /// │south-west││south ││south-east│
181 /// ╰──────────╯╰──────╯╰──────────╯
182 /// ```
183 Element gridbox(std::vector<Elements> lines) {
184  return std::make_shared<GridBox>(std::move(lines));
185 }
186 
187 } // namespace ftxui
virtual void SetBox(Box box)
Assign a position and a dimension to an element for drawing.
Definition: node.cpp:26
Screen(int dimx, int dimy)
Definition: screen.cpp:395
void Compute(std::vector< Element > *elements, int target_size)
Definition: box_helper.cpp:64
std::shared_ptr< Node > Element
Definition: elements.hpp:23
Element gridbox(std::vector< Elements > lines)
A container displaying a grid of elements.
Definition: gridbox.cpp:183
Element filler()
An element that will take expand proportionnally to the space left in a container.
Definition: flex.cpp:98
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition: node.cpp:47