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