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