FTXUI  6.0.2
C++ functional terminal UI.
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
table.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.
4#include "ftxui/dom/table.hpp"
5
6#include <algorithm> // for max
7#include <initializer_list> // for initializer_list
8#include <memory> // for allocator, shared_ptr, allocator_traits<>::value_type
9#include <utility> // for move, swap
10#include <vector> // for vector
11
12#include "ftxui/dom/elements.hpp" // for Element, operator|, text, separatorCharacter, Elements, BorderStyle, Decorator, emptyElement, size, gridbox, EQUAL, flex, flex_shrink, HEIGHT, WIDTH
13
14namespace ftxui {
15namespace {
16
17bool IsCell(int x, int y) {
18 return x % 2 == 1 && y % 2 == 1;
19}
20
21// NOLINTNEXTLINE
22static std::string charset[6][6] = {
23 {"┌", "┐", "└", "┘", "─", "│"}, // LIGHT
24 {"┏", "┓", "┗", "┛", "╍", "╏"}, // DASHED
25 {"┏", "┓", "┗", "┛", "━", "┃"}, // HEAVY
26 {"╔", "╗", "╚", "╝", "═", "║"}, // DOUBLE
27 {"╭", "╮", "╰", "╯", "─", "│"}, // ROUNDED
28 {" ", " ", " ", " ", " ", " "}, // EMPTY
29};
30
31int Wrap(int input, int modulo) {
32 input %= modulo;
33 input += modulo;
34 input %= modulo;
35 return input;
36}
37
38void Order(int& a, int& b) {
39 if (a >= b) {
40 std::swap(a, b);
41 }
42}
43
44} // namespace
45
46/// @brief Create an empty table.
47/// @ingroup dom
49 Initialize({});
50}
51
52/// @brief Create a table from a vector of vector of string.
53/// @param input The input data.
54/// @ingroup dom
55Table::Table(std::vector<std::vector<std::string>> input) {
56 std::vector<std::vector<Element>> output;
57 output.reserve(input.size());
58 for (auto& row : input) {
59 output.emplace_back();
60 auto& output_row = output.back();
61 output_row.reserve(row.size());
62 for (auto& cell : row) {
63 output_row.push_back(text(std::move(cell)));
64 }
65 }
66 Initialize(std::move(output));
67}
68
69/// @brief Create a table from a vector of vector of Element
70/// @param input The input elements.
71/// @ingroup dom
72Table::Table(std::vector<std::vector<Element>> input) {
73 Initialize(std::move(input));
74}
75
76// @brief Create a table from a list of list of string.
77// @param init The input data.
78// @ingroup dom
79Table::Table(std::initializer_list<std::vector<std::string>> init) {
80 std::vector<std::vector<Element>> input;
81 for (const auto& row : init) {
82 std::vector<Element> output_row;
83 output_row.reserve(row.size());
84 for (const auto& cell : row) {
85 output_row.push_back(text(cell));
86 }
87 input.push_back(std::move(output_row));
88 }
89 Initialize(std::move(input));
90}
91
92// private
93void Table::Initialize(std::vector<std::vector<Element>> input) {
94 input_dim_y_ = static_cast<int>(input.size());
95 input_dim_x_ = 0;
96 for (auto& row : input) {
97 input_dim_x_ = std::max(input_dim_x_, int(row.size()));
98 }
99
100 dim_y_ = 2 * input_dim_y_ + 1;
101 dim_x_ = 2 * input_dim_x_ + 1;
102
103 // Reserve space.
104 elements_.resize(dim_y_);
105 for (int y = 0; y < dim_y_; ++y) {
106 elements_[y].resize(dim_x_);
107 }
108
109 // Transfert elements_ from |input| toward |elements_|.
110 {
111 int y = 1;
112 for (auto& row : input) {
113 int x = 1;
114 for (auto& cell : row) {
115 elements_[y][x] = std::move(cell);
116 x += 2;
117 }
118 y += 2;
119 }
120 }
121
122 // Add empty element for the border.
123 for (int y = 0; y < dim_y_; ++y) {
124 for (int x = 0; x < dim_x_; ++x) {
125 auto& element = elements_[y][x];
126
127 if (IsCell(x, y)) {
128 if (!element) {
129 element = emptyElement();
130 }
131 continue;
132 }
133
134 element = emptyElement();
135 }
136 }
137}
138
139/// @brief Select a row of the table.
140/// @param index The index of the row to select.
141/// @note You can use negative index to select from the end.
142/// @ingroup dom
144 return SelectRectangle(0, -1, index, index);
145}
146
147/// @brief Select a range of rows of the table.
148/// @param row_min The first row to select.
149/// @param row_max The last row to select.
150/// @note You can use negative index to select from the end.
151/// @ingroup dom
155
156/// @brief Select a column of the table.
157/// @param index The index of the column to select.
158/// @note You can use negative index to select from the end.
159/// @ingroup dom
161 return SelectRectangle(index, index, 0, -1);
162}
163
164/// @brief Select a range of columns of the table.
165/// @param column_min The first column to select.
166/// @param column_max The last column to select.
167/// @note You can use negative index to select from the end.
168/// @ingroup dom
172
173/// @brief Select a cell of the table.
174/// @param column The column of the cell to select.
175/// @param row The row of the cell to select.
176/// @note You can use negative index to select from the end.
177/// @ingroup dom
181
182/// @brief Select a rectangle of the table.
183/// @param column_min The first column to select.
184/// @param column_max The last column to select.
185/// @param row_min The first row to select.
186/// @param row_max The last row to select.
187/// @note You can use negative index to select from the end.
188/// @ingroup dom
190 int column_max,
191 int row_min,
192 int row_max) {
193 column_min = Wrap(column_min, input_dim_x_);
194 column_max = Wrap(column_max, input_dim_x_);
196 row_min = Wrap(row_min, input_dim_y_);
197 row_max = Wrap(row_max, input_dim_y_);
199
200 TableSelection output; // NOLINT
201 output.table_ = this;
202 output.x_min_ = 2 * column_min;
203 output.x_max_ = 2 * column_max + 2;
204 output.y_min_ = 2 * row_min;
205 output.y_max_ = 2 * row_max + 2;
206 return output;
207}
208
209/// @brief Select all the table.
210/// @ingroup dom
212 TableSelection output; // NOLINT
213 output.table_ = this;
214 output.x_min_ = 0;
215 output.x_max_ = dim_x_ - 1;
216 output.y_min_ = 0;
217 output.y_max_ = dim_y_ - 1;
218 return output;
219}
220
221/// @brief Render the table.
222/// @return The rendered table. This is an element you can draw.
223/// @ingroup dom
225 for (int y = 0; y < dim_y_; ++y) {
226 for (int x = 0; x < dim_x_; ++x) {
227 auto& it = elements_[y][x];
228
229 // Line
230 if ((x + y) % 2 == 1) {
231 it = std::move(it) | flex;
232 continue;
233 }
234
235 // Cells
236 if ((x % 2) == 1 && (y % 2) == 1) {
237 it = std::move(it) | flex_shrink;
238 continue;
239 }
240
241 // Corners
242 it = std::move(it) | size(WIDTH, EQUAL, 0) | size(HEIGHT, EQUAL, 0);
243 }
244 }
245 dim_x_ = 0;
246 dim_y_ = 0;
247 return gridbox(std::move(elements_));
248}
249
250/// @brief Apply the `decorator` to the selection.
251/// This decorate both the cells, the lines and the corners.
252/// @param decorator The decorator to apply.
253/// @ingroup dom
254// NOLINTNEXTLINE
256 for (int y = y_min_; y <= y_max_; ++y) {
257 for (int x = x_min_; x <= x_max_; ++x) {
258 Element& e = table_->elements_[y][x];
259 e = std::move(e) | decorator;
260 }
261 }
262}
263
264/// @brief Apply the `decorator` to the selection.
265/// @param decorator The decorator to apply.
266/// This decorate only the cells.
267/// @ingroup dom
268// NOLINTNEXTLINE
270 for (int y = y_min_; y <= y_max_; ++y) {
271 for (int x = x_min_; x <= x_max_; ++x) {
272 if (y % 2 == 1 && x % 2 == 1) {
273 Element& e = table_->elements_[y][x];
274 e = std::move(e) | decorator;
275 }
276 }
277 }
278}
279
280/// @brief Apply the `decorator` to the selection.
281/// This decorate only the lines modulo `modulo` with a shift of `shift`.
282/// @param decorator The decorator to apply.
283/// @param modulo The modulo of the lines to decorate.
284/// @param shift The shift of the lines to decorate.
285/// @ingroup dom
286// NOLINTNEXTLINE
288 int modulo,
289 int shift) {
290 for (int y = y_min_; y <= y_max_; ++y) {
291 for (int x = x_min_; x <= x_max_; ++x) {
292 if (y % 2 == 1 && (x / 2) % modulo == shift) {
293 Element& e = table_->elements_[y][x];
294 e = std::move(e) | decorator;
295 }
296 }
297 }
298}
299
300/// @brief Apply the `decorator` to the selection.
301/// This decorate only the lines modulo `modulo` with a shift of `shift`.
302/// @param decorator The decorator to apply.
303/// @param modulo The modulo of the lines to decorate.
304/// @param shift The shift of the lines to decorate.
305/// @ingroup dom
306// NOLINTNEXTLINE
308 int modulo,
309 int shift) {
310 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
311 for (int x = x_min_; x <= x_max_; ++x) {
312 if (y % 2 == 1 && (y / 2) % modulo == shift) {
313 Element& e = table_->elements_[y][x];
314 e = std::move(e) | decorator;
315 }
316 }
317 }
318}
319
320/// @brief Apply the `decorator` to the selection.
321/// This decorate only the corners modulo `modulo` with a shift of `shift`.
322/// @param decorator The decorator to apply.
323/// @param modulo The modulo of the corners to decorate.
324/// @param shift The shift of the corners to decorate.
325/// @ingroup dom
326// NOLINTNEXTLINE
328 int modulo,
329 int shift) {
330 for (int y = y_min_; y <= y_max_; ++y) {
331 for (int x = x_min_; x <= x_max_; ++x) {
332 if (y % 2 == 1 && x % 2 == 1 && ((x / 2) % modulo == shift)) {
333 Element& e = table_->elements_[y][x];
334 e = std::move(e) | decorator;
335 }
336 }
337 }
338}
339
340/// @brief Apply the `decorator` to the selection.
341/// This decorate only the corners modulo `modulo` with a shift of `shift`.
342/// @param decorator The decorator to apply.
343/// @param modulo The modulo of the corners to decorate.
344/// @param shift The shift of the corners to decorate.
345/// @ingroup dom
346// NOLINTNEXTLINE
348 int modulo,
349 int shift) {
350 for (int y = y_min_; y <= y_max_; ++y) {
351 for (int x = x_min_; x <= x_max_; ++x) {
352 if (y % 2 == 1 && x % 2 == 1 && ((y / 2) % modulo == shift)) {
353 Element& e = table_->elements_[y][x];
354 e = std::move(e) | decorator;
355 }
356 }
357 }
358}
359
360/// @brief Apply a `border` around the selection.
361/// @param border The border style to apply.
362/// @ingroup dom
368
369 // NOLINTNEXTLINE
370 table_->elements_[y_min_][x_min_] = text(charset[border][0]) | automerge;
371 // NOLINTNEXTLINE
372 table_->elements_[y_min_][x_max_] = text(charset[border][1]) | automerge;
373 // NOLINTNEXTLINE
374 table_->elements_[y_max_][x_min_] = text(charset[border][2]) | automerge;
375 // NOLINTNEXTLINE
376 table_->elements_[y_max_][x_max_] = text(charset[border][3]) | automerge;
377}
378
379/// @brief Draw some separator lines in the selection.
380/// @param border The border style to apply.
381/// @ingroup dom
383 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
384 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
385 if (y % 2 == 0 || x % 2 == 0) {
386 Element& e = table_->elements_[y][x];
387 e = (y % 2 == 1)
388 ? separatorCharacter(charset[border][5]) | automerge // NOLINT
389 : separatorCharacter(charset[border][4]) | automerge; // NOLINT
390 }
391 }
392 }
393}
394
395/// @brief Draw some vertical separator lines in the selection.
396/// @param border The border style to apply.
397/// @ingroup dom
399 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
400 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
401 if (x % 2 == 0) {
402 table_->elements_[y][x] =
404 }
405 }
406 }
407}
408
409/// @brief Draw some horizontal separator lines in the selection.
410/// @param border The border style to apply.
411/// @ingroup dom
413 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
414 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
415 if (y % 2 == 0) {
416 table_->elements_[y][x] =
418 }
419 }
420 }
421}
422
423/// @brief Draw some separator lines to the left side of the selection.
424/// @param border The border style to apply.
425/// @ingroup dom
427 for (int y = y_min_; y <= y_max_; y++) {
428 table_->elements_[y][x_min_] =
430 }
431}
432
433/// @brief Draw some separator lines to the right side of the selection.
434/// @param border The border style to apply.
435/// @ingroup dom
437 for (int y = y_min_; y <= y_max_; y++) {
438 table_->elements_[y][x_max_] =
440 }
441}
442
443/// @brief Draw some separator lines to the top side of the selection.
444/// @param border The border style to apply.
445/// @ingroup dom
447 for (int x = x_min_; x <= x_max_; x++) {
448 table_->elements_[y_min_][x] =
450 }
451}
452
453/// @brief Draw some separator lines to the bottom side of the selection.
454/// @param border The border style to apply.
455/// @ingroup dom
457 for (int x = x_min_; x <= x_max_; x++) {
458 table_->elements_[y_max_][x] =
460 }
461}
462
463} // namespace ftxui
void DecorateAlternateColumn(Decorator, int modulo=2, int shift=0)
Apply the decorator to the selection. This decorate only the lines modulo modulo with a shift of shif...
Definition table.cpp:287
void SeparatorVertical(BorderStyle border=LIGHT)
Draw some vertical separator lines in the selection.
Definition table.cpp:398
void DecorateCells(Decorator)
Apply the decorator to the selection.
Definition table.cpp:269
void BorderLeft(BorderStyle border=LIGHT)
Draw some separator lines to the left side of the selection.
Definition table.cpp:426
void DecorateCellsAlternateColumn(Decorator, int modulo=2, int shift=0)
Apply the decorator to the selection. This decorate only the corners modulo modulo with a shift of sh...
Definition table.cpp:327
void Decorate(Decorator)
Apply the decorator to the selection. This decorate both the cells, the lines and the corners.
Definition table.cpp:255
void DecorateAlternateRow(Decorator, int modulo=2, int shift=0)
Apply the decorator to the selection. This decorate only the lines modulo modulo with a shift of shif...
Definition table.cpp:307
void BorderTop(BorderStyle border=LIGHT)
Draw some separator lines to the top side of the selection.
Definition table.cpp:446
void Separator(BorderStyle border=LIGHT)
Draw some separator lines in the selection.
Definition table.cpp:382
void BorderBottom(BorderStyle border=LIGHT)
Draw some separator lines to the bottom side of the selection.
Definition table.cpp:456
void DecorateCellsAlternateRow(Decorator, int modulo=2, int shift=0)
Apply the decorator to the selection. This decorate only the corners modulo modulo with a shift of sh...
Definition table.cpp:347
void BorderRight(BorderStyle border=LIGHT)
Draw some separator lines to the right side of the selection.
Definition table.cpp:436
void Border(BorderStyle border=LIGHT)
Apply a border around the selection.
Definition table.cpp:363
void SeparatorHorizontal(BorderStyle border=LIGHT)
Draw some horizontal separator lines in the selection.
Definition table.cpp:412
Element Render()
Render the table.
Definition table.cpp:224
Table()
Create an empty table.
Definition table.cpp:48
TableSelection SelectCell(int column, int row)
Select a cell of the table.
Definition table.cpp:178
TableSelection SelectColumn(int column_index)
Select a column of the table.
Definition table.cpp:160
TableSelection SelectRow(int row_index)
Select a row of the table.
Definition table.cpp:143
TableSelection SelectColumns(int column_min, int column_max)
Select a range of columns of the table.
Definition table.cpp:169
TableSelection SelectRows(int row_min, int row_max)
Select a range of rows of the table.
Definition table.cpp:152
TableSelection SelectAll()
Select all the table.
Definition table.cpp:211
TableSelection SelectRectangle(int column_min, int column_max, int row_min, int row_max)
Select a rectangle of the table.
Definition table.cpp:189
std::function< Element(Element)> Decorator
Definition elements.hpp:24
Decorator size(WidthOrHeight, Constraint, int value)
Apply a constraint on the size of an element.
Definition size.cpp:89
Element flex(Element)
Make a child element to expand proportionally to the space left in a container.
Definition flex.cpp:123
std::shared_ptr< Node > Element
Definition elements.hpp:22
std::shared_ptr< T > Make(Args &&... args)
Definition component.hpp:26
Element emptyElement()
Definition util.cpp:140
Element flex_shrink(Element)
Minimize if needed.
Definition flex.cpp:159
Element text(std::wstring text)
Display a piece of unicode text.
Definition text.cpp:160
Element separatorCharacter(std::string)
Draw a vertical or horizontal separation in between two other elements.
Element gridbox(std::vector< Elements > lines)
A container displaying a grid of elements.
Definition gridbox.cpp:173
Element automerge(Element child)
Enable character to be automatically merged with others nearby.
Definition automerge.cpp:17
Element border(Element)
Draw a border around the element.
Definition border.cpp:227
BorderStyle
Definition elements.hpp:27