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 
12 namespace ftxui {
13 namespace {
14 
15 bool IsCell(int x, int y) {
16  return x % 2 == 1 && y % 2 == 1;
17 }
18 
19 // NOLINTNEXTLINE
20 static std::string charset[6][6] = {
21  {"┌", "┐", "└", "┘", "─", "│"}, // LIGHT
22  {"┏", "┓", "┗", "┛", "╍", "╏"}, // DASHED
23  {"┏", "┓", "┗", "┛", "━", "┃"}, // HEAVY
24  {"╔", "╗", "╚", "╝", "═", "║"}, // DOUBLE
25  {"╭", "╮", "╰", "╯", "─", "│"}, // ROUNDED
26  {" ", " ", " ", " ", " ", " "}, // EMPTY
27 };
28 
29 int Wrap(int input, int modulo) {
30  input %= modulo;
31  input += modulo;
32  input %= modulo;
33  return input;
34 }
35 
36 void 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
53 Table::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
70 Table::Table(std::vector<std::vector<Element>> input) {
71  Initialize(std::move(input));
72 }
73 
74 // private
75 void Table::Initialize(std::vector<std::vector<Element>> input) {
76  input_dim_y_ = static_cast<int>(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
134 TableSelection 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
151 TableSelection 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
160 TableSelection 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
348  BorderTop(border);
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:147
@ WIDTH
Definition: elements.hpp:147
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
Element emptyElement()
Definition: util.cpp:133
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:119
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:148
Element border(Element)
Draw a border around the element.
Definition: border.cpp:227
BorderStyle
Definition: elements.hpp:27