FTXUI 6.1.9
C++ functional terminal UI.
Loading...
Searching...
No Matches
src/ftxui/dom/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.
48 Initialize({});
49}
50
51/// @brief Create a table from a vector of vector of string.
52/// @param input The input data.
53Table::Table(const std::vector<std::vector<std::string>>& input) {
54 std::vector<std::vector<Element>> output;
55 output.reserve(input.size());
56 for (const auto& row : input) {
57 output.emplace_back();
58 auto& output_row = output.back();
59 output_row.reserve(row.size());
60 for (const auto& cell : row) {
61 output_row.push_back(text(std::move(cell)));
62 }
63 }
64 Initialize(std::move(output));
65}
66
67/// @brief Create a table from a vector of vector of Element
68/// @param input The input elements.
69Table::Table(std::vector<std::vector<Element>> input) {
70 Initialize(std::move(input));
71}
72
73/// @brief Create a table from a list of list of string.
74/// @param init The input data.
75Table::Table(std::initializer_list<std::vector<std::string>> init) {
76 std::vector<std::vector<Element>> input;
77 for (const auto& row : init) {
78 std::vector<Element> output_row;
79 output_row.reserve(row.size());
80 for (const auto& cell : row) {
81 output_row.push_back(text(cell));
82 }
83 input.push_back(std::move(output_row));
84 }
85 Initialize(std::move(input));
86}
87
88// private
89void Table::Initialize(std::vector<std::vector<Element>> input) {
90 input_dim_y_ = static_cast<int>(input.size());
91 input_dim_x_ = 0;
92 for (auto& row : input) {
93 input_dim_x_ = std::max(input_dim_x_, int(row.size()));
94 }
95
96 dim_y_ = 2 * input_dim_y_ + 1;
97 dim_x_ = 2 * input_dim_x_ + 1;
98
99 // Reserve space.
100 elements_.resize(dim_y_);
101 for (int y = 0; y < dim_y_; ++y) {
102 elements_[y].resize(dim_x_);
103 }
104
105 // Transfer elements_ from |input| toward |elements_|.
106 {
107 int y = 1;
108 for (auto& row : input) {
109 int x = 1;
110 for (auto& cell : row) {
111 elements_[y][x] = std::move(cell);
112 x += 2;
113 }
114 y += 2;
115 }
116 }
117
118 // Add empty element for the border.
119 for (int y = 0; y < dim_y_; ++y) {
120 for (int x = 0; x < dim_x_; ++x) {
121 auto& element = elements_[y][x];
122
123 if (IsCell(x, y)) {
124 if (!element) {
125 element = emptyElement();
126 }
127 continue;
128 }
129
130 element = emptyElement();
131 }
132 }
133}
134
135/// @brief Select a row of the table.
136/// @param index The index of the row to select.
137/// @note You can use negative index to select from the end.
139 return SelectRectangle(0, -1, index, index);
140}
141
142/// @brief Select a range of rows of the table.
143/// @param row_min The first row to select.
144/// @param row_max The last row to select.
145/// @note You can use negative index to select from the end.
146TableSelection Table::SelectRows(int row_min, int row_max) {
147 return SelectRectangle(0, -1, row_min, row_max);
148}
149
150/// @brief Select a column of the table.
151/// @param index The index of the column to select.
152/// @note You can use negative index to select from the end.
154 return SelectRectangle(index, index, 0, -1);
155}
156
157/// @brief Select a range of columns of the table.
158/// @param column_min The first column to select.
159/// @param column_max The last column to select.
160/// @note You can use negative index to select from the end.
161TableSelection Table::SelectColumns(int column_min, int column_max) {
162 return SelectRectangle(column_min, column_max, 0, -1);
163}
164
165/// @brief Select a cell of the table.
166/// @param column The column of the cell to select.
167/// @param row The row of the cell to select.
168/// @note You can use negative index to select from the end.
169TableSelection Table::SelectCell(int column, int row) {
170 return SelectRectangle(column, column, row, row);
171}
172
173/// @brief Select a rectangle of the table.
174/// @param column_min The first column to select.
175/// @param column_max The last column to select.
176/// @param row_min The first row to select.
177/// @param row_max The last row to select.
178/// @note You can use negative index to select from the end.
180 int column_max,
181 int row_min,
182 int row_max) {
183 column_min = Wrap(column_min, input_dim_x_);
184 column_max = Wrap(column_max, input_dim_x_);
185 Order(column_min, column_max);
186 row_min = Wrap(row_min, input_dim_y_);
187 row_max = Wrap(row_max, input_dim_y_);
188 Order(row_min, row_max);
189
190 TableSelection output; // NOLINT
191 output.table_ = this;
192 output.x_min_ = 2 * column_min;
193 output.x_max_ = 2 * column_max + 2;
194 output.y_min_ = 2 * row_min;
195 output.y_max_ = 2 * row_max + 2;
196 return output;
197}
198
199/// @brief Select all the table.
201 TableSelection output; // NOLINT
202 output.table_ = this;
203 output.x_min_ = 0;
204 output.x_max_ = dim_x_ - 1;
205 output.y_min_ = 0;
206 output.y_max_ = dim_y_ - 1;
207 return output;
208}
209
210/// @brief Render the table.
211/// @return The rendered table. This is an element you can draw.
213 for (int y = 0; y < dim_y_; ++y) {
214 for (int x = 0; x < dim_x_; ++x) {
215 auto& it = elements_[y][x];
216
217 // Line
218 if ((x + y) % 2 == 1) {
219 it = std::move(it) | flex;
220 continue;
221 }
222
223 // Cells
224 if ((x % 2) == 1 && (y % 2) == 1) {
225 it = std::move(it) | flex_shrink;
226 continue;
227 }
228
229 // Corners
230 it = std::move(it) | size(WIDTH, EQUAL, 0) | size(HEIGHT, EQUAL, 0);
231 }
232 }
233 dim_x_ = 0;
234 dim_y_ = 0;
235 return gridbox(std::move(elements_));
236}
237
238/// @brief Apply the `decorator` to the selection.
239/// This decorate both the cells, the lines and the corners.
240/// @param decorator The decorator to apply.
241// NOLINTNEXTLINE
242void TableSelection::Decorate(const Decorator& decorator) {
243 for (int y = y_min_; y <= y_max_; ++y) {
244 for (int x = x_min_; x <= x_max_; ++x) {
245 Element& e = table_->elements_[y][x];
246 e = std::move(e) | decorator;
247 }
248 }
249}
250
251/// @brief Apply the `decorator` to the selection.
252/// @param decorator The decorator to apply.
253/// This decorate only the cells.
254// NOLINTNEXTLINE
256 for (int y = y_min_; y <= y_max_; ++y) {
257 for (int x = x_min_; x <= x_max_; ++x) {
258 if (y % 2 == 1 && x % 2 == 1) {
259 Element& e = table_->elements_[y][x];
260 e = std::move(e) | decorator;
261 }
262 }
263 }
264}
265
266/// @brief Apply the `decorator` to the selection.
267/// This decorate only the lines modulo `modulo` with a shift of `shift`.
268/// @param decorator The decorator to apply.
269/// @param modulo The modulo of the lines to decorate.
270/// @param shift The shift of the lines to decorate.
271// NOLINTNEXTLINE
273 int modulo,
274 int shift) {
275 for (int y = y_min_; y <= y_max_; ++y) {
276 for (int x = x_min_; x <= x_max_; ++x) {
277 if (y % 2 == 1 && (x / 2) % modulo == shift) {
278 Element& e = table_->elements_[y][x];
279 e = std::move(e) | decorator;
280 }
281 }
282 }
283}
284
285/// @brief Apply the `decorator` to the selection.
286/// This decorate only the lines modulo `modulo` with a shift of `shift`.
287/// @param decorator The decorator to apply.
288/// @param modulo The modulo of the lines to decorate.
289/// @param shift The shift of the lines to decorate.
290// NOLINTNEXTLINE
292 int modulo,
293 int shift) {
294 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
295 for (int x = x_min_; x <= x_max_; ++x) {
296 if (y % 2 == 1 && (y / 2) % modulo == shift) {
297 Element& e = table_->elements_[y][x];
298 e = std::move(e) | decorator;
299 }
300 }
301 }
302}
303
304/// @brief Apply the `decorator` to the selection.
305/// This decorate only the corners modulo `modulo` with a shift of `shift`.
306/// @param decorator The decorator to apply.
307/// @param modulo The modulo of the corners to decorate.
308/// @param shift The shift of the corners to decorate.
309// NOLINTNEXTLINE
311 int modulo,
312 int shift) {
313 for (int y = y_min_; y <= y_max_; ++y) {
314 for (int x = x_min_; x <= x_max_; ++x) {
315 if (y % 2 == 1 && x % 2 == 1 && ((x / 2) % modulo == shift)) {
316 Element& e = table_->elements_[y][x];
317 e = std::move(e) | decorator;
318 }
319 }
320 }
321}
322
323/// @brief Apply the `decorator` to the selection.
324/// This decorate only the corners modulo `modulo` with a shift of `shift`.
325/// @param decorator The decorator to apply.
326/// @param modulo The modulo of the corners to decorate.
327/// @param shift The shift of the corners to decorate.
328// NOLINTNEXTLINE
330 int modulo,
331 int shift) {
332 for (int y = y_min_; y <= y_max_; ++y) {
333 for (int x = x_min_; x <= x_max_; ++x) {
334 if (y % 2 == 1 && x % 2 == 1 && ((y / 2) % modulo == shift)) {
335 Element& e = table_->elements_[y][x];
336 e = std::move(e) | decorator;
337 }
338 }
339 }
340}
341
342/// @brief Apply the `decorator` to the border of the selection.
343/// @param decorator The decorator to apply.
345 for (int x = x_min_; x <= x_max_; ++x) {
346 table_->elements_[y_min_][x] =
347 std::move(table_->elements_[y_min_][x]) | decorator;
348 table_->elements_[y_max_][x] =
349 std::move(table_->elements_[y_max_][x]) | decorator;
350 }
351 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
352 table_->elements_[y][x_min_] =
353 std::move(table_->elements_[y][x_min_]) | decorator;
354 table_->elements_[y][x_max_] =
355 std::move(table_->elements_[y][x_max_]) | decorator;
356 }
357}
358
359/// @brief Apply the `decorator` to the left border of the selection.
360/// @param decorator The decorator to apply.
362 for (int y = y_min_; y <= y_max_; y++) {
363 table_->elements_[y][x_min_] =
364 std::move(table_->elements_[y][x_min_]) | decorator;
365 }
366}
367
368/// @brief Apply the `decorator` to the right border of the selection.
369/// @param decorator The decorator to apply.
371 for (int y = y_min_; y <= y_max_; y++) {
372 table_->elements_[y][x_max_] =
373 std::move(table_->elements_[y][x_max_]) | decorator;
374 }
375}
376
377/// @brief Apply the `decorator` to the top border of the selection.
378/// @param decorator The decorator to apply.
380 for (int x = x_min_; x <= x_max_; x++) {
381 table_->elements_[y_min_][x] =
382 std::move(table_->elements_[y_min_][x]) | decorator;
383 }
384}
385
386/// @brief Apply the `decorator` to the bottom border of the selection.
387/// @param decorator The decorator to apply.
389 for (int x = x_min_; x <= x_max_; x++) {
390 table_->elements_[y_max_][x] =
391 std::move(table_->elements_[y_max_][x]) | decorator;
392 }
393}
394
395/// @brief Apply the `decorator` to the separators of the selection.
396/// @param decorator The decorator to apply.
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 (y % 2 == 0 || x % 2 == 0) {
401 table_->elements_[y][x] =
402 std::move(table_->elements_[y][x]) | decorator;
403 }
404 }
405 }
406}
407
408/// @brief Apply the `decorator` to the vertical separators of the selection.
409/// @param decorator The decorator to apply.
411 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
412 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
413 if (x % 2 == 0) {
414 table_->elements_[y][x] =
415 std::move(table_->elements_[y][x]) | decorator;
416 }
417 }
418 }
419}
420
421/// @brief Apply the `decorator` to the horizontal separators of the selection.
422/// @param decorator The decorator to apply.
424 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
425 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
426 if (y % 2 == 0) {
427 table_->elements_[y][x] =
428 std::move(table_->elements_[y][x]) | decorator;
429 }
430 }
431 }
432}
433
434/// @brief Apply a `border` around the selection.
435/// @param border The border style to apply.
437 BorderLeft(border);
438 BorderRight(border);
439 BorderTop(border);
440 BorderBottom(border);
441
442 // NOLINTNEXTLINE
443 table_->elements_[y_min_][x_min_] = text(charset[border][0]) | automerge;
444 // NOLINTNEXTLINE
445 table_->elements_[y_min_][x_max_] = text(charset[border][1]) | automerge;
446 // NOLINTNEXTLINE
447 table_->elements_[y_max_][x_min_] = text(charset[border][2]) | automerge;
448 // NOLINTNEXTLINE
449 table_->elements_[y_max_][x_max_] = text(charset[border][3]) | automerge;
450}
451
452/// @brief Apply a `border` around the selection.
453/// @param border The border style to apply.
454/// @param decorator The decorator to apply.
455void TableSelection::Border(BorderStyle border, const Decorator& decorator) {
456 Border(border);
457 DecorateBorder(decorator);
458}
459
460/// @brief Draw some separator lines in the selection.
461/// @param border The border style to apply.
463 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
464 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
465 if (y % 2 == 0 || x % 2 == 0) {
466 Element& e = table_->elements_[y][x];
467 e = (y % 2 == 1)
468 ? separatorCharacter(charset[border][5]) | automerge // NOLINT
469 : separatorCharacter(charset[border][4]) | automerge; // NOLINT
470 }
471 }
472 }
473}
474
475/// @brief Draw some separator lines in the selection.
476/// @param border The border style to apply.
477/// @param decorator The decorator to apply.
478void TableSelection::Separator(BorderStyle border, const Decorator& decorator) {
479 Separator(border);
480 DecorateSeparator(decorator);
481}
482
483/// @brief Draw some vertical separator lines in the selection.
484/// @param border The border style to apply.
486 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
487 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
488 if (x % 2 == 0) {
489 table_->elements_[y][x] =
490 separatorCharacter(charset[border][5]) | automerge; // NOLINT
491 }
492 }
493 }
494}
495
496/// @brief Draw some vertical separator lines in the selection.
497/// @param border The border style to apply.
498/// @param decorator The decorator to apply.
500 const Decorator& decorator) {
501 SeparatorVertical(border);
502 DecorateSeparatorVertical(decorator);
503}
504
505/// @brief Draw some horizontal separator lines in the selection.
506/// @param border The border style to apply.
508 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
509 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
510 if (y % 2 == 0) {
511 table_->elements_[y][x] =
512 separatorCharacter(charset[border][4]) | automerge; // NOLINT
513 }
514 }
515 }
516}
517
518/// @brief Draw some horizontal separator lines in the selection.
519/// @param border The border style to apply.
520/// @param decorator The decorator to apply.
522 const Decorator& decorator) {
523 SeparatorHorizontal(border);
525}
526
527/// @brief Draw some separator lines to the left side of the selection.
528/// @param border The border style to apply.
530 for (int y = y_min_; y <= y_max_; y++) {
531 table_->elements_[y][x_min_] =
532 separatorCharacter(charset[border][5]) | automerge; // NOLINT
533 }
534}
535
536/// @brief Draw some separator lines to the left side of the selection.
537/// @param border The border style to apply.
538/// @param decorator The decorator to apply.
540 const Decorator& decorator) {
541 BorderLeft(border);
542 DecorateBorderLeft(decorator);
543}
544
545/// @brief Draw some separator lines to the right side of the selection.
546/// @param border The border style to apply.
548 for (int y = y_min_; y <= y_max_; y++) {
549 table_->elements_[y][x_max_] =
550 separatorCharacter(charset[border][5]) | automerge; // NOLINT
551 }
552}
553
554/// @brief Draw some separator lines to the right side of the selection.
555/// @param border The border style to apply.
556/// @param decorator The decorator to apply.
558 const Decorator& decorator) {
559 BorderRight(border);
560 DecorateBorderRight(decorator);
561}
562
563/// @brief Draw some separator lines to the top side of the selection.
564/// @param border The border style to apply.
566 for (int x = x_min_; x <= x_max_; x++) {
567 table_->elements_[y_min_][x] =
568 separatorCharacter(charset[border][4]) | automerge; // NOLINT
569 }
570}
571
572/// @brief Draw some separator lines to the top side of the selection.
573/// @param border The border style to apply.
574/// @param decorator The decorator to apply.
575void TableSelection::BorderTop(BorderStyle border, const Decorator& decorator) {
576 BorderTop(border);
577 DecorateBorderTop(decorator);
578}
579
580/// @brief Draw some separator lines to the bottom side of the selection.
581/// @param border The border style to apply.
583 for (int x = x_min_; x <= x_max_; x++) {
584 table_->elements_[y_max_][x] =
585 separatorCharacter(charset[border][4]) | automerge; // NOLINT
586 }
587}
588
589/// @brief Draw some separator lines to the bottom side of the selection.
590/// @param border The border style to apply.
591/// @param decorator The decorator to apply.
593 const Decorator& decorator) {
594 BorderBottom(border);
595 DecorateBorderBottom(decorator);
596}
597
598} // namespace ftxui
void DecorateBorderRight(const Decorator &)
Apply the decorator to the right border of the selection.
void DecorateBorder(const Decorator &)
Apply the decorator to the border of the selection.
void DecorateAlternateRow(const 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...
void DecorateSeparatorHorizontal(const Decorator &)
Apply the decorator to the horizontal separators of the selection.
void DecorateCellsAlternateRow(const 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...
void DecorateCellsAlternateColumn(const 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...
void SeparatorVertical(BorderStyle border=LIGHT)
Draw some vertical separator lines in the selection.
void Decorate(const Decorator &)
Apply the decorator to the selection. This decorate both the cells, the lines and the corners.
void DecorateBorderBottom(const Decorator &)
Apply the decorator to the bottom border of the selection.
void BorderLeft(BorderStyle border=LIGHT)
Draw some separator lines to the left side of the selection.
void DecorateCells(const Decorator &)
Apply the decorator to the selection.
void DecorateAlternateColumn(const 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...
void DecorateSeparatorVertical(const Decorator &)
Apply the decorator to the vertical separators of the selection.
void DecorateBorderLeft(const Decorator &)
Apply the decorator to the left border of the selection.
void BorderTop(BorderStyle border=LIGHT)
Draw some separator lines to the top side of the selection.
void Separator(BorderStyle border=LIGHT)
Draw some separator lines in the selection.
void BorderBottom(BorderStyle border=LIGHT)
Draw some separator lines to the bottom side of the selection.
void DecorateSeparator(const Decorator &)
Apply the decorator to the separators of the selection.
void DecorateBorderTop(const Decorator &)
Apply the decorator to the top border of the selection.
void BorderRight(BorderStyle border=LIGHT)
Draw some separator lines to the right side of the selection.
void Border(BorderStyle border=LIGHT)
Apply a border around the selection.
void SeparatorHorizontal(BorderStyle border=LIGHT)
Draw some horizontal separator lines in the selection.
Component Wrap(std::string name, Component component)
Definition gallery.cpp:18
Element Render()
Render the table.
Table()
Create an empty table.
TableSelection SelectCell(int column, int row)
Select a cell of the table.
TableSelection SelectColumn(int column_index)
Select a column of the table.
TableSelection SelectRow(int row_index)
Select a row of the table.
TableSelection SelectColumns(int column_min, int column_max)
Select a range of columns of the table.
TableSelection SelectRows(int row_min, int row_max)
Select a range of rows of the table.
TableSelection SelectAll()
Select all the table.
TableSelection SelectRectangle(int column_min, int column_max, int row_min, int row_max)
Select a rectangle of the table.
Decorator size(WidthOrHeight, Constraint, int value)
Apply a constraint on the size of an element.
Element text(std::wstring_view text)
Display a piece of unicode text.
Element flex(Element)
Make a child element to expand proportionally to the space left in a container.
Definition flex.cpp:123
Element emptyElement()
Definition dom/util.cpp:140
Element flex_shrink(Element)
Minimize if needed.
Definition flex.cpp:159
Element automerge(Element child)
Enable character to be automatically merged with others nearby.
Definition automerge.cpp:17
Element separatorCharacter(std::string_view)
Draw a vertical or horizontal separation in between two other elements.
BorderStyle
BorderStyle is an enumeration that represents the different styles of borders that can be applied to ...
Definition elements.hpp:36
The FTXUI ftxui:: namespace.
Definition animation.hpp:10
std::function< Element(Element)> Decorator
Definition elements.hpp:25
std::shared_ptr< Node > Element
Definition elements.hpp:23
Element gridbox(std::vector< Elements > lines)
A container displaying a grid of elements.