FTXUI 7.0.3
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 table_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.
47Table::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.
138TableSelection Table::SelectRow(int index) {
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.
153TableSelection Table::SelectColumn(int index) {
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.
179TableSelection Table::SelectRectangle(int column_min,
180 int column_max,
181 int row_min,
182 int row_max) {
183 TableSelection output; // NOLINT
184 output.table_ = this;
185 if (input_dim_x_ == 0 || input_dim_y_ == 0) {
186 output.x_min_ = 0;
187 output.x_max_ = -1;
188 output.y_min_ = 0;
189 output.y_max_ = -1;
190 return output;
191 }
192
193 column_min = Wrap(column_min, input_dim_x_);
194 column_max = Wrap(column_max, input_dim_x_);
195 Order(column_min, column_max);
196 row_min = Wrap(row_min, input_dim_y_);
197 row_max = Wrap(row_max, input_dim_y_);
198 Order(row_min, row_max);
199
200 output.x_min_ = 2 * column_min;
201 output.x_max_ = 2 * column_max + 2;
202 output.y_min_ = 2 * row_min;
203 output.y_max_ = 2 * row_max + 2;
204 return output;
205}
206
207/// @brief Select all the table.
208TableSelection Table::SelectAll() {
209 TableSelection output; // NOLINT
210 output.table_ = this;
211 output.x_min_ = 0;
212 output.x_max_ = dim_x_ - 1;
213 output.y_min_ = 0;
214 output.y_max_ = dim_y_ - 1;
215 return output;
216}
217
218/// @brief Render the table.
219/// @return The rendered table. This is an element you can draw.
220Element Table::Render() {
221 for (int y = 0; y < dim_y_; ++y) {
222 for (int x = 0; x < dim_x_; ++x) {
223 auto& it = elements_[y][x];
224
225 // Line
226 if ((x + y) % 2 == 1) {
227 it = std::move(it) | flex;
228 continue;
229 }
230
231 // Cells
232 if ((x % 2) == 1 && (y % 2) == 1) {
233 it = std::move(it) | flex_shrink;
234 continue;
235 }
236
237 // Corners
238 it = std::move(it) | size(WIDTH, EQUAL, 0) | size(HEIGHT, EQUAL, 0);
239 }
240 }
241 dim_x_ = 0;
242 dim_y_ = 0;
243 return gridbox(std::move(elements_));
244}
245
246/// @brief Apply the `decorator` to the selection.
247/// This decorate both the cells, the lines and the corners.
248/// @param decorator The decorator to apply.
249// NOLINTNEXTLINE
250void TableSelection::Decorate(const Decorator& decorator) {
251 for (int y = y_min_; y <= y_max_; ++y) {
252 for (int x = x_min_; x <= x_max_; ++x) {
253 Element& e = table_->elements_[y][x];
254 e = std::move(e) | decorator;
255 }
256 }
257}
258
259/// @brief Apply the `decorator` to the selection.
260/// @param decorator The decorator to apply.
261/// This decorate only the cells.
262// NOLINTNEXTLINE
263void TableSelection::DecorateCells(const Decorator& decorator) {
264 for (int y = y_min_; y <= y_max_; ++y) {
265 for (int x = x_min_; x <= x_max_; ++x) {
266 if (y % 2 == 1 && x % 2 == 1) {
267 Element& e = table_->elements_[y][x];
268 e = std::move(e) | decorator;
269 }
270 }
271 }
272}
273
274/// @brief Apply the `decorator` to the selection.
275/// This decorate only the lines modulo `modulo` with a shift of `shift`.
276/// @param decorator The decorator to apply.
277/// @param modulo The modulo of the lines to decorate.
278/// @param shift The shift of the lines to decorate.
279// NOLINTNEXTLINE
280void TableSelection::DecorateAlternateColumn(const Decorator& decorator,
281 int modulo,
282 int shift) {
283 for (int y = y_min_; y <= y_max_; ++y) {
284 for (int x = x_min_; x <= x_max_; ++x) {
285 if (y % 2 == 1 && (x / 2) % modulo == shift) {
286 Element& e = table_->elements_[y][x];
287 e = std::move(e) | decorator;
288 }
289 }
290 }
291}
292
293/// @brief Apply the `decorator` to the selection.
294/// This decorate only the lines modulo `modulo` with a shift of `shift`.
295/// @param decorator The decorator to apply.
296/// @param modulo The modulo of the lines to decorate.
297/// @param shift The shift of the lines to decorate.
298// NOLINTNEXTLINE
299void TableSelection::DecorateAlternateRow(const Decorator& decorator,
300 int modulo,
301 int shift) {
302 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
303 for (int x = x_min_; x <= x_max_; ++x) {
304 if (y % 2 == 1 && (y / 2) % modulo == shift) {
305 Element& e = table_->elements_[y][x];
306 e = std::move(e) | decorator;
307 }
308 }
309 }
310}
311
312/// @brief Apply the `decorator` to the selection.
313/// This decorate only the corners modulo `modulo` with a shift of `shift`.
314/// @param decorator The decorator to apply.
315/// @param modulo The modulo of the corners to decorate.
316/// @param shift The shift of the corners to decorate.
317// NOLINTNEXTLINE
318void TableSelection::DecorateCellsAlternateColumn(const Decorator& decorator,
319 int modulo,
320 int shift) {
321 for (int y = y_min_; y <= y_max_; ++y) {
322 for (int x = x_min_; x <= x_max_; ++x) {
323 if (y % 2 == 1 && x % 2 == 1 && ((x / 2) % modulo == shift)) {
324 Element& e = table_->elements_[y][x];
325 e = std::move(e) | decorator;
326 }
327 }
328 }
329}
330
331/// @brief Apply the `decorator` to the selection.
332/// This decorate only the corners modulo `modulo` with a shift of `shift`.
333/// @param decorator The decorator to apply.
334/// @param modulo The modulo of the corners to decorate.
335/// @param shift The shift of the corners to decorate.
336// NOLINTNEXTLINE
337void TableSelection::DecorateCellsAlternateRow(const Decorator& decorator,
338 int modulo,
339 int shift) {
340 for (int y = y_min_; y <= y_max_; ++y) {
341 for (int x = x_min_; x <= x_max_; ++x) {
342 if (y % 2 == 1 && x % 2 == 1 && ((y / 2) % modulo == shift)) {
343 Element& e = table_->elements_[y][x];
344 e = std::move(e) | decorator;
345 }
346 }
347 }
348}
349
350/// @brief Apply the `decorator` to the border of the selection.
351/// @param decorator The decorator to apply.
352void TableSelection::DecorateBorder(const Decorator& decorator) {
353 for (int x = x_min_; x <= x_max_; ++x) {
354 table_->elements_[y_min_][x] =
355 std::move(table_->elements_[y_min_][x]) | decorator;
356 table_->elements_[y_max_][x] =
357 std::move(table_->elements_[y_max_][x]) | decorator;
358 }
359 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
360 table_->elements_[y][x_min_] =
361 std::move(table_->elements_[y][x_min_]) | decorator;
362 table_->elements_[y][x_max_] =
363 std::move(table_->elements_[y][x_max_]) | decorator;
364 }
365}
366
367/// @brief Apply the `decorator` to the left border of the selection.
368/// @param decorator The decorator to apply.
369void TableSelection::DecorateBorderLeft(const Decorator& decorator) {
370 for (int y = y_min_; y <= y_max_; y++) {
371 table_->elements_[y][x_min_] =
372 std::move(table_->elements_[y][x_min_]) | decorator;
373 }
374}
375
376/// @brief Apply the `decorator` to the right border of the selection.
377/// @param decorator The decorator to apply.
378void TableSelection::DecorateBorderRight(const Decorator& decorator) {
379 for (int y = y_min_; y <= y_max_; y++) {
380 table_->elements_[y][x_max_] =
381 std::move(table_->elements_[y][x_max_]) | decorator;
382 }
383}
384
385/// @brief Apply the `decorator` to the top border of the selection.
386/// @param decorator The decorator to apply.
387void TableSelection::DecorateBorderTop(const Decorator& decorator) {
388 for (int x = x_min_; x <= x_max_; x++) {
389 table_->elements_[y_min_][x] =
390 std::move(table_->elements_[y_min_][x]) | decorator;
391 }
392}
393
394/// @brief Apply the `decorator` to the bottom border of the selection.
395/// @param decorator The decorator to apply.
396void TableSelection::DecorateBorderBottom(const Decorator& decorator) {
397 for (int x = x_min_; x <= x_max_; x++) {
398 table_->elements_[y_max_][x] =
399 std::move(table_->elements_[y_max_][x]) | decorator;
400 }
401}
402
403/// @brief Apply the `decorator` to the separators of the selection.
404/// @param decorator The decorator to apply.
405void TableSelection::DecorateSeparator(const Decorator& decorator) {
406 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
407 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
408 if (y % 2 == 0 || x % 2 == 0) {
409 table_->elements_[y][x] =
410 std::move(table_->elements_[y][x]) | decorator;
411 }
412 }
413 }
414}
415
416/// @brief Apply the `decorator` to the vertical separators of the selection.
417/// @param decorator The decorator to apply.
418void TableSelection::DecorateSeparatorVertical(const Decorator& decorator) {
419 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
420 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
421 if (x % 2 == 0) {
422 table_->elements_[y][x] =
423 std::move(table_->elements_[y][x]) | decorator;
424 }
425 }
426 }
427}
428
429/// @brief Apply the `decorator` to the horizontal separators of the selection.
430/// @param decorator The decorator to apply.
431void TableSelection::DecorateSeparatorHorizontal(const Decorator& decorator) {
432 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
433 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
434 if (y % 2 == 0) {
435 table_->elements_[y][x] =
436 std::move(table_->elements_[y][x]) | decorator;
437 }
438 }
439 }
440}
441
442/// @brief Apply a `border` around the selection.
443/// @param border The border style to apply.
444void TableSelection::Border(BorderStyle border) {
445 if (x_min_ > x_max_ || y_min_ > y_max_) {
446 return;
447 }
448
449 BorderLeft(border);
450 BorderRight(border);
451 BorderTop(border);
452 BorderBottom(border);
453
454 // NOLINTNEXTLINE
455 table_->elements_[y_min_][x_min_] =
456 text(table_charset[border][0]) | automerge;
457 // NOLINTNEXTLINE
458 table_->elements_[y_min_][x_max_] =
459 text(table_charset[border][1]) | automerge;
460 // NOLINTNEXTLINE
461 table_->elements_[y_max_][x_min_] =
462 text(table_charset[border][2]) | automerge;
463 // NOLINTNEXTLINE
464 table_->elements_[y_max_][x_max_] =
465 text(table_charset[border][3]) | automerge;
466}
467
468/// @brief Apply a `border` around the selection.
469/// @param border The border style to apply.
470/// @param decorator The decorator to apply.
471void TableSelection::Border(BorderStyle border, const Decorator& decorator) {
472 Border(border);
473 DecorateBorder(decorator);
474}
475
476/// @brief Draw some separator lines in the selection.
477/// @param border The border style to apply.
478void TableSelection::Separator(BorderStyle border) {
479 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
480 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
481 if (y % 2 == 0 || x % 2 == 0) {
482 Element& e = table_->elements_[y][x];
483 e = (y % 2 == 1) ? separatorCharacter(table_charset[border][5]) |
484 automerge // NOLINT
485 : separatorCharacter(table_charset[border][4]) |
486 automerge; // NOLINT
487 }
488 }
489 }
490}
491
492/// @brief Draw some separator lines in the selection.
493/// @param border The border style to apply.
494/// @param decorator The decorator to apply.
495void TableSelection::Separator(BorderStyle border, const Decorator& decorator) {
496 Separator(border);
497 DecorateSeparator(decorator);
498}
499
500/// @brief Draw some vertical separator lines in the selection.
501/// @param border The border style to apply.
502void TableSelection::SeparatorVertical(BorderStyle border) {
503 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
504 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
505 if (x % 2 == 0) {
506 table_->elements_[y][x] =
507 separatorCharacter(table_charset[border][5]) | automerge; // NOLINT
508 }
509 }
510 }
511}
512
513/// @brief Draw some vertical separator lines in the selection.
514/// @param border The border style to apply.
515/// @param decorator The decorator to apply.
516void TableSelection::SeparatorVertical(BorderStyle border,
517 const Decorator& decorator) {
518 SeparatorVertical(border);
519 DecorateSeparatorVertical(decorator);
520}
521
522/// @brief Draw some horizontal separator lines in the selection.
523/// @param border The border style to apply.
524void TableSelection::SeparatorHorizontal(BorderStyle border) {
525 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
526 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
527 if (y % 2 == 0) {
528 table_->elements_[y][x] =
529 separatorCharacter(table_charset[border][4]) | automerge; // NOLINT
530 }
531 }
532 }
533}
534
535/// @brief Draw some horizontal separator lines in the selection.
536/// @param border The border style to apply.
537/// @param decorator The decorator to apply.
538void TableSelection::SeparatorHorizontal(BorderStyle border,
539 const Decorator& decorator) {
540 SeparatorHorizontal(border);
541 DecorateSeparatorHorizontal(decorator);
542}
543
544/// @brief Draw some separator lines to the left side of the selection.
545/// @param border The border style to apply.
546void TableSelection::BorderLeft(BorderStyle border) {
547 for (int y = y_min_; y <= y_max_; y++) {
548 table_->elements_[y][x_min_] =
549 separatorCharacter(table_charset[border][5]) | automerge; // NOLINT
550 }
551}
552
553/// @brief Draw some separator lines to the left side of the selection.
554/// @param border The border style to apply.
555/// @param decorator The decorator to apply.
556void TableSelection::BorderLeft(BorderStyle border,
557 const Decorator& decorator) {
558 BorderLeft(border);
559 DecorateBorderLeft(decorator);
560}
561
562/// @brief Draw some separator lines to the right side of the selection.
563/// @param border The border style to apply.
564void TableSelection::BorderRight(BorderStyle border) {
565 for (int y = y_min_; y <= y_max_; y++) {
566 table_->elements_[y][x_max_] =
567 separatorCharacter(table_charset[border][5]) | automerge; // NOLINT
568 }
569}
570
571/// @brief Draw some separator lines to the right side of the selection.
572/// @param border The border style to apply.
573/// @param decorator The decorator to apply.
574void TableSelection::BorderRight(BorderStyle border,
575 const Decorator& decorator) {
576 BorderRight(border);
577 DecorateBorderRight(decorator);
578}
579
580/// @brief Draw some separator lines to the top side of the selection.
581/// @param border The border style to apply.
582void TableSelection::BorderTop(BorderStyle border) {
583 for (int x = x_min_; x <= x_max_; x++) {
584 table_->elements_[y_min_][x] =
585 separatorCharacter(table_charset[border][4]) | automerge; // NOLINT
586 }
587}
588
589/// @brief Draw some separator lines to the top side of the selection.
590/// @param border The border style to apply.
591/// @param decorator The decorator to apply.
592void TableSelection::BorderTop(BorderStyle border, const Decorator& decorator) {
593 BorderTop(border);
594 DecorateBorderTop(decorator);
595}
596
597/// @brief Draw some separator lines to the bottom side of the selection.
598/// @param border The border style to apply.
599void TableSelection::BorderBottom(BorderStyle border) {
600 for (int x = x_min_; x <= x_max_; x++) {
601 table_->elements_[y_max_][x] =
602 separatorCharacter(table_charset[border][4]) | automerge; // NOLINT
603 }
604}
605
606/// @brief Draw some separator lines to the bottom side of the selection.
607/// @param border The border style to apply.
608/// @param decorator The decorator to apply.
609void TableSelection::BorderBottom(BorderStyle border,
610 const Decorator& decorator) {
611 BorderBottom(border);
612 DecorateBorderBottom(decorator);
613}
614
615} // namespace ftxui
Component Wrap(std::string name, Component component)
Definition gallery.cpp:18
BorderStyle
BorderStyle is an enumeration that represents the different styles of borders that can be applied to ...
Definition elements.hpp:38
The FTXUI ftxui:: namespace.
Definition animation.hpp:11
std::shared_ptr< Node > Element
Definition elements.hpp:25
std::function< Element(Element)> Decorator
Definition elements.hpp:27
int y
Definition elements.hpp:134
const Element & element
Definition node.hpp:95