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
31// 將負索引解析為從結尾開始計算。
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 創建一個空表格。
49Table::Table() {
50 Initialize({});
51}
52
53/// @brief 從一個字串向量的向量創建一個表格。
54/// @param input 輸入資料。
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 從一個 Element 向量的向量創建一個表格
70/// @param input 輸入元素。
71Table::Table(std::vector<std::vector<Element>> input) {
72 Initialize(std::move(input));
73}
74
75/// @brief 從字串串列的串列建立表格。
76/// @param init 輸入資料。
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 // 保留空間。
102 elements_.resize(dim_y_);
103 for (int y = 0; y < dim_y_; ++y) {
104 elements_[y].resize(dim_x_);
105 }
106
107 // 將 elements_ 從 |input| 移轉到 |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 // 為邊框加入空元素。
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 選取表格的某一行。
138/// @param index 要選取的行的索引。
139/// @note 您可以使用負數索引從末尾選取。
140TableSelection Table::SelectRow(int index) {
141 return SelectRectangle(0, -1, index, index);
142}
143
144/// @brief 選取表格的行範圍。
145/// @param row_min 要選取的第一行。
146/// @param row_max 要選取的最後一行。
147/// @note 您可以使用負數索引從末尾選取。
148TableSelection Table::SelectRows(int row_min, int row_max) {
149 return SelectRectangle(0, -1, row_min, row_max);
150}
151
152/// @brief 選取表格的某一列。
153/// @param index 要選取的列的索引。
154/// @note 您可以使用負數索引從末尾選取。
155TableSelection Table::SelectColumn(int index) {
156 return SelectRectangle(index, index, 0, -1);
157}
158
159/// @brief 選取表格的列範圍。
160/// @param column_min 要選取的第一列。
161/// @param column_max 要選取的最後一列。
162/// @note 您可以使用負數索引從末尾選取。
163TableSelection Table::SelectColumns(int column_min, int column_max) {
164 return SelectRectangle(column_min, column_max, 0, -1);
165}
166
167/// @brief 選取表格的一個單元格。
168/// @param column 要選取的單元格的列。
169/// @param row 要選取的單元格的行。
170/// @note 您可以使用負數索引從末尾選取。
171TableSelection Table::SelectCell(int column, int row) {
172 return SelectRectangle(column, column, row, row);
173}
174
175/// @brief 選取表格的一個矩形區域。
176/// @param column_min 要選取的第一列。
177/// @param column_max 要選取的最後一列。
178/// @param row_min 要選取的第一行。
179/// @param row_max 要選取的最後一行。
180/// @note 您可以使用負數索引從末尾選取。
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 // 超出範圍的索引不選取任何內容。
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 選取整個表格。
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 渲染表格。
226/// @return 渲染後的表格。這是一個你可以繪製的元素。
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 將 `decorator` 應用於選取範圍。
254/// 這會裝飾單元格、線條和角落。
255/// @param decorator 要應用的裝飾器。
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 將 `decorator` 應用於選取範圍。
267/// 這只會裝飾單元格。
268/// @param decorator 要應用的裝飾器。
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 將 `decorator` 應用於選取範圍。
282/// 這只會裝飾模數為 `modulo`,偏移量為 `shift` 的線條。
283/// @param decorator 要應用的裝飾器。
284/// @param modulo 要裝飾線條的模數。
285/// @param shift 要裝飾線條的偏移量。
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 將 `decorator` 應用於選取範圍。
301/// 這只會裝飾模數為 `modulo`,偏移量為 `shift` 的線條。
302/// @param decorator 要應用的裝飾器。
303/// @param modulo 要裝飾線條的模數。
304/// @param shift 要裝飾線條的偏移量。
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 將 `decorator` 應用於選取範圍。
320/// 這只會裝飾模數為 `modulo`,偏移量為 `shift` 的角落。
321/// @param decorator 要應用的裝飾器。
322/// @param modulo 要裝飾角落的模數。
323/// @param shift 要裝飾角落的偏移量。
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 將 `decorator` 應用於選取範圍。
339/// 這只會裝飾模數為 `modulo`,偏移量為 `shift` 的角落。
340/// @param decorator 要應用的裝飾器。
341/// @param modulo 要裝飾角落的模數。
342/// @param shift 要裝飾角落的偏移量。
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 將 `decorator` 套用到選取範圍的邊框上。
358/// @param decorator 要套用的裝飾器。
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 將 `decorator` 套用到選取範圍的左邊框上。
375/// @param decorator 要套用的裝飾器。
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 將 `decorator` 套用到選取範圍的右邊框上。
384/// @param decorator 要套用的裝飾器。
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 將 `decorator` 套用到選取範圍的上邊框上。
393/// @param decorator 要套用的裝飾器。
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 將 `decorator` 套用到選取範圍的下邊框上。
402/// @param decorator 要套用的裝飾器。
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 將 `decorator` 套用到選取範圍的分隔線上。
411/// @param decorator 要套用的裝飾器。
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 將 `decorator` 套用到選取範圍的垂直分隔線上。
424/// @param decorator 要套用的裝飾器。
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 將 `decorator` 套用到選取範圍的水平分隔線上。
437/// @param decorator 要套用的裝飾器。
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 在選取範圍周圍應用 `border`。
450/// @param border 要應用的邊框樣式。
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 在選取範圍周圍套用 `border`。
476/// @param border 要套用的邊框樣式。
477/// @param decorator 要套用的裝飾器。
478void TableSelection::Border(BorderStyle border, const Decorator& decorator) {
479 Border(border);
480 DecorateBorder(decorator);
481}
482
483/// @brief 在選取範圍內繪製一些分隔線。
484/// @param border 要應用的邊框樣式。
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 在選取範圍中繪製一些分隔線。
500/// @param border 要套用的邊框樣式。
501/// @param decorator 要套用的裝飾器。
502void TableSelection::Separator(BorderStyle border, const Decorator& decorator) {
503 Separator(border);
504 DecorateSeparator(decorator);
505}
506
507/// @brief 在選取範圍內繪製一些垂直分隔線。
508/// @param border 要應用的邊框樣式。
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 在選取範圍中繪製一些垂直分隔線。
521/// @param border 要套用的邊框樣式。
522/// @param decorator 要套用的裝飾器。
523void TableSelection::SeparatorVertical(BorderStyle border,
524 const Decorator& decorator) {
525 SeparatorVertical(border);
526 DecorateSeparatorVertical(decorator);
527}
528
529/// @brief 在選取範圍內繪製一些水平分隔線。
530/// @param border 要應用的邊框樣式。
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 在選取範圍中繪製一些水平分隔線。
543/// @param border 要套用的邊框樣式。
544/// @param decorator 要套用的裝飾器。
545void TableSelection::SeparatorHorizontal(BorderStyle border,
546 const Decorator& decorator) {
547 SeparatorHorizontal(border);
548 DecorateSeparatorHorizontal(decorator);
549}
550
551/// @brief 在選取範圍的左側繪製一些分隔線。
552/// @param border 要應用的邊框樣式。
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 在選取範圍的左側繪製一些分隔線。
561/// @param border 要套用的邊框樣式。
562/// @param decorator 要套用的裝飾器。
563void TableSelection::BorderLeft(BorderStyle border,
564 const Decorator& decorator) {
565 BorderLeft(border);
566 DecorateBorderLeft(decorator);
567}
568
569/// @brief 在選取範圍的右側繪製一些分隔線。
570/// @param border 要應用的邊框樣式。
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 在選取範圍的右側繪製一些分隔線。
579/// @param border 要套用的邊框樣式。
580/// @param decorator 要套用的裝飾器。
581void TableSelection::BorderRight(BorderStyle border,
582 const Decorator& decorator) {
583 BorderRight(border);
584 DecorateBorderRight(decorator);
585}
586
587/// @brief 在選取範圍的頂部繪製一些分隔線。
588/// @param border 要應用的邊框樣式。
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 在選取範圍的上側繪製一些分隔線。
597/// @param border 要套用的邊框樣式。
598/// @param decorator 要套用的裝飾器。
599void TableSelection::BorderTop(BorderStyle border, const Decorator& decorator) {
600 BorderTop(border);
601 DecorateBorderTop(decorator);
602}
603
604/// @brief 在選取範圍的底部繪製一些分隔線。
605/// @param border 要應用的邊框樣式。
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 在選取範圍的下側繪製一些分隔線。
614/// @param border 要套用的邊框樣式。
615/// @param decorator 要套用的裝飾器。
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 是一個列舉,表示可以應用於終端機 UI 元素的不同邊框樣式。
Definition elements.hpp:35
FTXUI ftxui:: 命名空間
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:131
const Element & element
Definition node.hpp:92