FTXUI 7.1.0
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
31// Resolve negative indices as counting from the end.
32int Wrap(int input, int modulo) {
33 return input < 0 ? input + modulo : input;
34}
35
36bool InRange(int input, int modulo) {
37 return input >= 0 && input < modulo;
38}
39
40void Order(int& a, int& b) {
41 if (a >= b) {
42 std::swap(a, b);
43 }
44}
45
46} // namespace
47
48/// @brief Create an empty table.
49Table::Table() {
50 Initialize({});
51}
52
53/// @brief Create a table from a vector of vector of string.
54/// @param input The input data.
55Table::Table(const std::vector<std::vector<std::string>>& input) {
56 std::vector<std::vector<Element>> output;
57 output.reserve(input.size());
58 for (const auto& row : input) {
59 output.emplace_back();
60 auto& output_row = output.back();
61 output_row.reserve(row.size());
62 for (const 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.
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.
77Table::Table(std::initializer_list<std::vector<std::string>> init) {
78 std::vector<std::vector<Element>> input;
79 for (const auto& row : init) {
80 std::vector<Element> output_row;
81 output_row.reserve(row.size());
82 for (const auto& cell : row) {
83 output_row.push_back(text(cell));
84 }
85 input.push_back(std::move(output_row));
86 }
87 Initialize(std::move(input));
88}
89
90// private
91void Table::Initialize(std::vector<std::vector<Element>> input) {
92 input_dim_y_ = static_cast<int>(input.size());
93 input_dim_x_ = 0;
94 for (auto& row : input) {
95 input_dim_x_ = std::max(input_dim_x_, int(row.size()));
96 }
97
98 dim_y_ = 2 * input_dim_y_ + 1;
99 dim_x_ = 2 * input_dim_x_ + 1;
100
101 // Reserve space.
102 elements_.resize(dim_y_);
103 for (int y = 0; y < dim_y_; ++y) {
104 elements_[y].resize(dim_x_);
105 }
106
107 // Transfer elements_ from |input| toward |elements_|.
108 {
109 int y = 1;
110 for (auto& row : input) {
111 int x = 1;
112 for (auto& cell : row) {
113 elements_[y][x] = std::move(cell);
114 x += 2;
115 }
116 y += 2;
117 }
118 }
119
120 // Add empty element for the border.
121 for (int y = 0; y < dim_y_; ++y) {
122 for (int x = 0; x < dim_x_; ++x) {
123 auto& element = elements_[y][x];
124
125 if (IsCell(x, y)) {
126 if (!element) {
127 element = emptyElement();
128 }
129 continue;
130 }
131
132 element = emptyElement();
133 }
134 }
135}
136
137/// @brief Select a row of the table.
138/// @param index The index of the row to select.
139/// @note You can use negative index to select from the end.
140TableSelection Table::SelectRow(int index) {
141 return SelectRectangle(0, -1, index, index);
142}
143
144/// @brief Select a range of rows of the table.
145/// @param row_min The first row to select.
146/// @param row_max The last row to select.
147/// @note You can use negative index to select from the end.
148TableSelection Table::SelectRows(int row_min, int row_max) {
149 return SelectRectangle(0, -1, row_min, row_max);
150}
151
152/// @brief Select a column of the table.
153/// @param index The index of the column to select.
154/// @note You can use negative index to select from the end.
155TableSelection Table::SelectColumn(int index) {
156 return SelectRectangle(index, index, 0, -1);
157}
158
159/// @brief Select a range of columns of the table.
160/// @param column_min The first column to select.
161/// @param column_max The last column to select.
162/// @note You can use negative index to select from the end.
163TableSelection Table::SelectColumns(int column_min, int column_max) {
164 return SelectRectangle(column_min, column_max, 0, -1);
165}
166
167/// @brief Select a cell of the table.
168/// @param column The column of the cell to select.
169/// @param row The row of the cell to select.
170/// @note You can use negative index to select from the end.
171TableSelection Table::SelectCell(int column, int row) {
172 return SelectRectangle(column, column, row, row);
173}
174
175/// @brief Select a rectangle of the table.
176/// @param column_min The first column to select.
177/// @param column_max The last column to select.
178/// @param row_min The first row to select.
179/// @param row_max The last row to select.
180/// @note You can use negative index to select from the end.
181TableSelection Table::SelectRectangle(int column_min,
182 int column_max,
183 int row_min,
184 int row_max) {
185 TableSelection output; // NOLINT
186 output.table_ = this;
187
188 column_min = Wrap(column_min, input_dim_x_);
189 column_max = Wrap(column_max, input_dim_x_);
190 row_min = Wrap(row_min, input_dim_y_);
191 row_max = Wrap(row_max, input_dim_y_);
192
193 // Out of range indices select nothing.
194 if (!InRange(column_min, input_dim_x_) ||
195 !InRange(column_max, input_dim_x_) || !InRange(row_min, input_dim_y_) ||
196 !InRange(row_max, input_dim_y_)) {
197 output.x_min_ = 0;
198 output.x_max_ = -1;
199 output.y_min_ = 0;
200 output.y_max_ = -1;
201 return output;
202 }
203
204 Order(column_min, column_max);
205 Order(row_min, row_max);
206
207 output.x_min_ = 2 * column_min;
208 output.x_max_ = 2 * column_max + 2;
209 output.y_min_ = 2 * row_min;
210 output.y_max_ = 2 * row_max + 2;
211 return output;
212}
213
214/// @brief Select all the table.
215TableSelection Table::SelectAll() {
216 TableSelection output; // NOLINT
217 output.table_ = this;
218 output.x_min_ = 0;
219 output.x_max_ = dim_x_ - 1;
220 output.y_min_ = 0;
221 output.y_max_ = dim_y_ - 1;
222 return output;
223}
224
225/// @brief Render the table.
226/// @return The rendered table. This is an element you can draw.
227Element Table::Render() {
228 for (int y = 0; y < dim_y_; ++y) {
229 for (int x = 0; x < dim_x_; ++x) {
230 auto& it = elements_[y][x];
231
232 // Line
233 if ((x + y) % 2 == 1) {
234 it = std::move(it) | flex;
235 continue;
236 }
237
238 // Cells
239 if ((x % 2) == 1 && (y % 2) == 1) {
240 it = std::move(it) | flex_shrink;
241 continue;
242 }
243
244 // Corners
245 it = std::move(it) | size(WIDTH, EQUAL, 0) | size(HEIGHT, EQUAL, 0);
246 }
247 }
248 dim_x_ = 0;
249 dim_y_ = 0;
250 return gridbox(std::move(elements_));
251}
252
253/// @brief Apply the `decorator` to the selection.
254/// This decorate both the cells, the lines and the corners.
255/// @param decorator The decorator to apply.
256// NOLINTNEXTLINE
257void TableSelection::Decorate(const Decorator& decorator) {
258 for (int y = y_min_; y <= y_max_; ++y) {
259 for (int x = x_min_; x <= x_max_; ++x) {
260 Element& e = table_->elements_[y][x];
261 e = std::move(e) | decorator;
262 }
263 }
264}
265
266/// @brief Apply the `decorator` to the selection.
267/// @param decorator The decorator to apply.
268/// This decorate only the cells.
269// NOLINTNEXTLINE
270void TableSelection::DecorateCells(const Decorator& decorator) {
271 for (int y = y_min_; y <= y_max_; ++y) {
272 for (int x = x_min_; x <= x_max_; ++x) {
273 if (y % 2 == 1 && x % 2 == 1) {
274 Element& e = table_->elements_[y][x];
275 e = std::move(e) | decorator;
276 }
277 }
278 }
279}
280
281/// @brief Apply the `decorator` to the selection.
282/// This decorate only the lines modulo `modulo` with a shift of `shift`.
283/// @param decorator The decorator to apply.
284/// @param modulo The modulo of the lines to decorate.
285/// @param shift The shift of the lines to decorate.
286// NOLINTNEXTLINE
287void TableSelection::DecorateAlternateColumn(const Decorator& decorator,
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// NOLINTNEXTLINE
306void TableSelection::DecorateAlternateRow(const Decorator& decorator,
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// NOLINTNEXTLINE
325void TableSelection::DecorateCellsAlternateColumn(const Decorator& decorator,
326 int modulo,
327 int shift) {
328 for (int y = y_min_; y <= y_max_; ++y) {
329 for (int x = x_min_; x <= x_max_; ++x) {
330 if (y % 2 == 1 && x % 2 == 1 && ((x / 2) % modulo == shift)) {
331 Element& e = table_->elements_[y][x];
332 e = std::move(e) | decorator;
333 }
334 }
335 }
336}
337
338/// @brief Apply the `decorator` to the selection.
339/// This decorate only the corners modulo `modulo` with a shift of `shift`.
340/// @param decorator The decorator to apply.
341/// @param modulo The modulo of the corners to decorate.
342/// @param shift The shift of the corners to decorate.
343// NOLINTNEXTLINE
344void TableSelection::DecorateCellsAlternateRow(const Decorator& decorator,
345 int modulo,
346 int shift) {
347 for (int y = y_min_; y <= y_max_; ++y) {
348 for (int x = x_min_; x <= x_max_; ++x) {
349 if (y % 2 == 1 && x % 2 == 1 && ((y / 2) % modulo == shift)) {
350 Element& e = table_->elements_[y][x];
351 e = std::move(e) | decorator;
352 }
353 }
354 }
355}
356
357/// @brief Apply the `decorator` to the border of the selection.
358/// @param decorator The decorator to apply.
359void TableSelection::DecorateBorder(const Decorator& decorator) {
360 for (int x = x_min_; x <= x_max_; ++x) {
361 table_->elements_[y_min_][x] =
362 std::move(table_->elements_[y_min_][x]) | decorator;
363 table_->elements_[y_max_][x] =
364 std::move(table_->elements_[y_max_][x]) | decorator;
365 }
366 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
367 table_->elements_[y][x_min_] =
368 std::move(table_->elements_[y][x_min_]) | decorator;
369 table_->elements_[y][x_max_] =
370 std::move(table_->elements_[y][x_max_]) | decorator;
371 }
372}
373
374/// @brief Apply the `decorator` to the left border of the selection.
375/// @param decorator The decorator to apply.
376void TableSelection::DecorateBorderLeft(const Decorator& decorator) {
377 for (int y = y_min_; y <= y_max_; y++) {
378 table_->elements_[y][x_min_] =
379 std::move(table_->elements_[y][x_min_]) | decorator;
380 }
381}
382
383/// @brief Apply the `decorator` to the right border of the selection.
384/// @param decorator The decorator to apply.
385void TableSelection::DecorateBorderRight(const Decorator& decorator) {
386 for (int y = y_min_; y <= y_max_; y++) {
387 table_->elements_[y][x_max_] =
388 std::move(table_->elements_[y][x_max_]) | decorator;
389 }
390}
391
392/// @brief Apply the `decorator` to the top border of the selection.
393/// @param decorator The decorator to apply.
394void TableSelection::DecorateBorderTop(const Decorator& decorator) {
395 for (int x = x_min_; x <= x_max_; x++) {
396 table_->elements_[y_min_][x] =
397 std::move(table_->elements_[y_min_][x]) | decorator;
398 }
399}
400
401/// @brief Apply the `decorator` to the bottom border of the selection.
402/// @param decorator The decorator to apply.
403void TableSelection::DecorateBorderBottom(const Decorator& decorator) {
404 for (int x = x_min_; x <= x_max_; x++) {
405 table_->elements_[y_max_][x] =
406 std::move(table_->elements_[y_max_][x]) | decorator;
407 }
408}
409
410/// @brief Apply the `decorator` to the separators of the selection.
411/// @param decorator The decorator to apply.
412void TableSelection::DecorateSeparator(const Decorator& decorator) {
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 || x % 2 == 0) {
416 table_->elements_[y][x] =
417 std::move(table_->elements_[y][x]) | decorator;
418 }
419 }
420 }
421}
422
423/// @brief Apply the `decorator` to the vertical separators of the selection.
424/// @param decorator The decorator to apply.
425void TableSelection::DecorateSeparatorVertical(const Decorator& decorator) {
426 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
427 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
428 if (x % 2 == 0) {
429 table_->elements_[y][x] =
430 std::move(table_->elements_[y][x]) | decorator;
431 }
432 }
433 }
434}
435
436/// @brief Apply the `decorator` to the horizontal separators of the selection.
437/// @param decorator The decorator to apply.
438void TableSelection::DecorateSeparatorHorizontal(const Decorator& decorator) {
439 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
440 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
441 if (y % 2 == 0) {
442 table_->elements_[y][x] =
443 std::move(table_->elements_[y][x]) | decorator;
444 }
445 }
446 }
447}
448
449/// @brief Apply a `border` around the selection.
450/// @param border The border style to apply.
451void TableSelection::Border(BorderStyle border) {
452 if (x_min_ > x_max_ || y_min_ > y_max_) {
453 return;
454 }
455
456 BorderLeft(border);
457 BorderRight(border);
458 BorderTop(border);
459 BorderBottom(border);
460
461 // NOLINTNEXTLINE
462 table_->elements_[y_min_][x_min_] =
463 text(table_charset[border][0]) | automerge;
464 // NOLINTNEXTLINE
465 table_->elements_[y_min_][x_max_] =
466 text(table_charset[border][1]) | automerge;
467 // NOLINTNEXTLINE
468 table_->elements_[y_max_][x_min_] =
469 text(table_charset[border][2]) | automerge;
470 // NOLINTNEXTLINE
471 table_->elements_[y_max_][x_max_] =
472 text(table_charset[border][3]) | automerge;
473}
474
475/// @brief Apply a `border` around the selection.
476/// @param border The border style to apply.
477/// @param decorator The decorator to apply.
478void TableSelection::Border(BorderStyle border, const Decorator& decorator) {
479 Border(border);
480 DecorateBorder(decorator);
481}
482
483/// @brief Draw some separator lines in the selection.
484/// @param border The border style to apply.
485void TableSelection::Separator(BorderStyle border) {
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 (y % 2 == 0 || x % 2 == 0) {
489 Element& e = table_->elements_[y][x];
490 e = (y % 2 == 1) ? separatorCharacter(table_charset[border][5]) |
491 automerge // NOLINT
492 : separatorCharacter(table_charset[border][4]) |
493 automerge; // NOLINT
494 }
495 }
496 }
497}
498
499/// @brief Draw some separator lines in the selection.
500/// @param border The border style to apply.
501/// @param decorator The decorator to apply.
502void TableSelection::Separator(BorderStyle border, const Decorator& decorator) {
503 Separator(border);
504 DecorateSeparator(decorator);
505}
506
507/// @brief Draw some vertical separator lines in the selection.
508/// @param border The border style to apply.
509void TableSelection::SeparatorVertical(BorderStyle border) {
510 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
511 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
512 if (x % 2 == 0) {
513 table_->elements_[y][x] =
514 separatorCharacter(table_charset[border][5]) | automerge; // NOLINT
515 }
516 }
517 }
518}
519
520/// @brief Draw some vertical separator lines in the selection.
521/// @param border The border style to apply.
522/// @param decorator The decorator to apply.
523void TableSelection::SeparatorVertical(BorderStyle border,
524 const Decorator& decorator) {
525 SeparatorVertical(border);
526 DecorateSeparatorVertical(decorator);
527}
528
529/// @brief Draw some horizontal separator lines in the selection.
530/// @param border The border style to apply.
531void TableSelection::SeparatorHorizontal(BorderStyle border) {
532 for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
533 for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
534 if (y % 2 == 0) {
535 table_->elements_[y][x] =
536 separatorCharacter(table_charset[border][4]) | automerge; // NOLINT
537 }
538 }
539 }
540}
541
542/// @brief Draw some horizontal separator lines in the selection.
543/// @param border The border style to apply.
544/// @param decorator The decorator to apply.
545void TableSelection::SeparatorHorizontal(BorderStyle border,
546 const Decorator& decorator) {
547 SeparatorHorizontal(border);
548 DecorateSeparatorHorizontal(decorator);
549}
550
551/// @brief Draw some separator lines to the left side of the selection.
552/// @param border The border style to apply.
553void TableSelection::BorderLeft(BorderStyle border) {
554 for (int y = y_min_; y <= y_max_; y++) {
555 table_->elements_[y][x_min_] =
556 separatorCharacter(table_charset[border][5]) | automerge; // NOLINT
557 }
558}
559
560/// @brief Draw some separator lines to the left side of the selection.
561/// @param border The border style to apply.
562/// @param decorator The decorator to apply.
563void TableSelection::BorderLeft(BorderStyle border,
564 const Decorator& decorator) {
565 BorderLeft(border);
566 DecorateBorderLeft(decorator);
567}
568
569/// @brief Draw some separator lines to the right side of the selection.
570/// @param border The border style to apply.
571void TableSelection::BorderRight(BorderStyle border) {
572 for (int y = y_min_; y <= y_max_; y++) {
573 table_->elements_[y][x_max_] =
574 separatorCharacter(table_charset[border][5]) | automerge; // NOLINT
575 }
576}
577
578/// @brief Draw some separator lines to the right side of the selection.
579/// @param border The border style to apply.
580/// @param decorator The decorator to apply.
581void TableSelection::BorderRight(BorderStyle border,
582 const Decorator& decorator) {
583 BorderRight(border);
584 DecorateBorderRight(decorator);
585}
586
587/// @brief Draw some separator lines to the top side of the selection.
588/// @param border The border style to apply.
589void TableSelection::BorderTop(BorderStyle border) {
590 for (int x = x_min_; x <= x_max_; x++) {
591 table_->elements_[y_min_][x] =
592 separatorCharacter(table_charset[border][4]) | automerge; // NOLINT
593 }
594}
595
596/// @brief Draw some separator lines to the top side of the selection.
597/// @param border The border style to apply.
598/// @param decorator The decorator to apply.
599void TableSelection::BorderTop(BorderStyle border, const Decorator& decorator) {
600 BorderTop(border);
601 DecorateBorderTop(decorator);
602}
603
604/// @brief Draw some separator lines to the bottom side of the selection.
605/// @param border The border style to apply.
606void TableSelection::BorderBottom(BorderStyle border) {
607 for (int x = x_min_; x <= x_max_; x++) {
608 table_->elements_[y_max_][x] =
609 separatorCharacter(table_charset[border][4]) | automerge; // NOLINT
610 }
611}
612
613/// @brief Draw some separator lines to the bottom side of the selection.
614/// @param border The border style to apply.
615/// @param decorator The decorator to apply.
616void TableSelection::BorderBottom(BorderStyle border,
617 const Decorator& decorator) {
618 BorderBottom(border);
619 DecorateBorderBottom(decorator);
620}
621
622} // 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