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