FTXUI 6.1.9
C++ functional terminal UI.
Loading...
Searching...
No Matches
src/ftxui/dom/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
16namespace ftxui {
17class Screen;
18
19namespace {
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].
25int 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
35class GridBox : public Node {
36 public:
37 explicit GridBox(std::vector<Elements> lines) : lines_(std::move(lines)) {
38 y_size = static_cast<int>(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 // Add children to properly forward non overridden methods from Node.
51 for (auto& line : lines_) {
52 for (auto& cell : line) {
53 children_.push_back(cell);
54 }
55 }
56 }
57
58 void ComputeRequirement() override {
59 requirement_ = Requirement{};
60 for (auto& line : lines_) {
61 for (auto& cell : line) {
62 cell->ComputeRequirement();
63 }
64 }
65
66 // Compute the size of each columns/row.
67 std::vector<int> size_x(x_size, 0);
68 std::vector<int> size_y(y_size, 0);
69 for (int x = 0; x < x_size; ++x) {
70 for (int y = 0; y < y_size; ++y) {
71 size_x[x] = std::max(size_x[x], lines_[y][x]->requirement().min_x);
72 size_y[y] = std::max(size_y[y], lines_[y][x]->requirement().min_y);
73 }
74 }
75
76 requirement_.min_x = Integrate(size_x);
77 requirement_.min_y = Integrate(size_y);
78
79 // Forward the focused/focused child state:
80 for (int x = 0; x < x_size; ++x) {
81 for (int y = 0; y < y_size; ++y) {
82 if (requirement_.focused.enabled ||
83 !lines_[y][x]->requirement().focused.enabled) {
84 continue;
85 }
86 requirement_.focused = lines_[y][x]->requirement().focused;
87 requirement_.focused.box.Shift(size_x[x], size_y[y]);
88 }
89 }
90 }
91
92 void SetBox(Box box) override {
93 Node::SetBox(box);
94
95 box_helper::Element init;
96 init.min_size = 0;
97 init.flex_grow = 1024; // NOLINT
98 init.flex_shrink = 1024; // NOLINT
99 std::vector<box_helper::Element> elements_x(x_size, init);
100 std::vector<box_helper::Element> elements_y(y_size, init);
101
102 for (int y = 0; y < y_size; ++y) {
103 for (int x = 0; x < x_size; ++x) {
104 const auto& cell = lines_[y][x];
105 const auto& requirement = cell->requirement();
106 auto& e_x = elements_x[x];
107 auto& e_y = elements_y[y];
108 e_x.min_size = std::max(e_x.min_size, requirement.min_x);
109 e_y.min_size = std::max(e_y.min_size, requirement.min_y);
110 e_x.flex_grow = std::min(e_x.flex_grow, requirement.flex_grow_x);
111 e_y.flex_grow = std::min(e_y.flex_grow, requirement.flex_grow_y);
112 e_x.flex_shrink = std::min(e_x.flex_shrink, requirement.flex_shrink_x);
113 e_y.flex_shrink = std::min(e_y.flex_shrink, requirement.flex_shrink_y);
114 }
115 }
116
117 const int target_size_x = box.x_max - box.x_min + 1;
118 const int target_size_y = box.y_max - box.y_min + 1;
119 box_helper::Compute(&elements_x, target_size_x);
120 box_helper::Compute(&elements_y, target_size_y);
121
122 Box box_y = box;
123 int y = box_y.y_min;
124 for (int iy = 0; iy < y_size; ++iy) {
125 box_y.y_min = y;
126 y += elements_y[iy].size;
127 box_y.y_max = y - 1;
128
129 Box box_x = box_y;
130 int x = box_x.x_min;
131 for (int ix = 0; ix < x_size; ++ix) {
132 box_x.x_min = x;
133 x += elements_x[ix].size;
134 box_x.x_max = x - 1;
135 lines_[iy][ix]->SetBox(box_x);
136 }
137 }
138 }
139
140 void Render(Screen& screen) override {
141 for (auto& line : lines_) {
142 for (auto& cell : line) {
143 cell->Render(screen);
144 }
145 }
146 }
147
148 int x_size = 0;
149 int y_size = 0;
150 std::vector<Elements> lines_;
151};
152} // namespace
153 //
154/// @brief A container displaying a grid of elements.
155/// @param lines A list of lines, each line being a list of elements.
156/// @return The container.
157///
158/// #### Example
159///
160/// ```cpp
161/// auto cell = [](const char* t) { return text(t) | border; };
162/// auto document = gridbox({
163/// {cell("north-west") , cell("north") , cell("north-east")} ,
164/// {cell("west") , cell("center") , cell("east")} ,
165/// {cell("south-west") , cell("south") , cell("south-east")} ,
166/// });
167/// ```
168/// Output:
169/// ```
170/// ╭──────────╮╭──────╮╭──────────╮
171/// │north-west││north ││north-east│
172/// ╰──────────╯╰──────╯╰──────────╯
173/// ╭──────────╮╭──────╮╭──────────╮
174/// │west ││center││east │
175/// ╰──────────╯╰──────╯╰──────────╯
176/// ╭──────────╮╭──────╮╭──────────╮
177/// │south-west││south ││south-east│
178/// ╰──────────╯╰──────╯╰──────────╯
179/// ```
180Element gridbox(std::vector<Elements> lines) {
181 return std::make_shared<GridBox>(std::move(lines));
182}
183
184} // namespace ftxui
The FTXUI ftxui:: namespace.
Definition animation.hpp:10
std::shared_ptr< Node > Element
Definition elements.hpp:23
Element gridbox(std::vector< Elements > lines)
A container displaying a grid of elements.
std::vector< Elements > lines_