FTXUI 6.1.9
C++ functional terminal UI.
载入中...
搜索中...
未找到
src/ftxui/dom/canvas.cpp
浏览该文件的文档.
1// 版权所有 2021 Arthur Sonzogni. 保留所有权利。
2// 此源代码的使用受 MIT 许可证的约束,MIT 许可证可在以下位置找到:
3// LICENSE 文件。
5
6#include <algorithm> // for max, min
7#include <cmath> // for abs
8#include <cstdint> // for uint8_t
9#include <cstdlib> // for abs
10#include <ftxui/screen/color.hpp> // for Color
11#include <functional> // for function
12#include <map> // for map
13#include <memory> // for make_shared
14#include <utility> // for move, pair
15#include <vector> // for vector
16
17#include "ftxui/dom/elements.hpp" // for Element, canvas
18#include "ftxui/dom/node.hpp" // for Node
19#include "ftxui/dom/requirement.hpp" // for Requirement
20#include "ftxui/screen/box.hpp" // for Box
21#include "ftxui/screen/image.hpp" // for Image
22#include "ftxui/screen/pixel.hpp" // for Pixel
23#include "ftxui/screen/screen.hpp" // for Pixel, Screen
24#include "ftxui/screen/string.hpp" // for Utf8ToGlyphs
25#include "ftxui/util/ref.hpp" // for ConstRef
26
27namespace ftxui {
28
29namespace {
30
31// Base UTF8 pattern:
32// 11100010 10100000 10000000 // 空
33
34// 单个点的模式:
35// ┌──────┬───────┐
36// │点1 │ 点4 │
37// ├──────┼───────┤
38// │点2 │ 点5 │
39// ├──────┼───────┤
40// │点3 │ 点6 │
41// ├──────┼───────┤
42// │点0-1 │ 点0-2 │
43// └──────┴───────┘
44// 点1
45// 点2
46// 点3
47// 点0-1
48// 点4
49// 点5
50// 点6
51// 点0-2
52
53// NOLINTNEXTLINE
54uint8_t g_map_braille[2][4][2] = {
55 {
56 {0b00000000, 0b00000001}, // NOLINT | dot1
57 {0b00000000, 0b00000010}, // NOLINT | dot2
58 {0b00000000, 0b00000100}, // NOLINT | dot3
59 {0b00000001, 0b00000000}, // NOLINT | dot0-1
60 },
61 {
62 {0b00000000, 0b00001000}, // NOLINT | dot4
63 {0b00000000, 0b00010000}, // NOLINT | dot5
64 {0b00000000, 0b00100000}, // NOLINT | dot6
65 {0b00000010, 0b00000000}, // NOLINT | dot0-2
66 },
67};
68
69// NOLINTNEXTLINE
70std::vector<std::string> g_map_block = {
71 " ", "▘", "▖", "▌", "▝", "▀", "▞", "▛",
72 "▗", "▚", "▄", "▙", "▐", "▜", "▟", "█",
73};
74
75// NOLINTNEXTLINE
76const std::map<std::string, uint8_t> g_map_block_inversed = {
77 {" ", 0b0000}, {"▘", 0b0001}, {"▖", 0b0010}, {"▌", 0b0011},
78 {"▝", 0b0100}, {"▀", 0b0101}, {"▞", 0b0110}, {"▛", 0b0111},
79 {"▗", 0b1000}, {"▚", 0b1001}, {"▄", 0b1010}, {"▙", 0b1011},
80 {"▐", 0b1100}, {"▜", 0b1101}, {"▟", 0b1110}, {"█", 0b1111},
81};
82
83constexpr auto nostyle = [](Pixel& /*pixel*/) {};
84
85} // namespace
86
87/// @brief 构造函数。
88/// @param width 画布的宽度。一个单元格是2x4盲文点。
89/// @param height 画布的高度。一个单元格是2x4盲文点。
90Canvas::Canvas(int width, int height)
91 : width_(width),
92 height_(height),
93 storage_(width_ * height_ / 8 /* NOLINT */) {}
94
95/// @brief 获取单元格的内容。
96/// @param x 单元格的x坐标。
97/// @param y 单元格的y坐标。
98Pixel Canvas::GetPixel(int x, int y) const {
99 auto it = storage_.find(XY{x, y});
100 return (it == storage_.end()) ? Pixel() : it->second.content;
101}
102
103/// @brief 绘制一个盲文点。
104/// @param x 盲文点的x坐标。
105/// @param y 盲文点的y坐标。
106/// @param value 盲文点是否填充。
107void Canvas::DrawPoint(int x, int y, bool value) {
108 DrawPoint(x, y, value, [](Pixel& /*pixel*/) {});
109}
110
111/// @brief 绘制一个盲文点。
112/// @param x 盲文点的x坐标。
113/// @param y 盲文点的y坐标。
114/// @param value 盲文点是否填充。
115/// @param color 盲文点的颜色。
116void Canvas::DrawPoint(int x, int y, bool value, const Color& color) {
117 DrawPoint(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
118}
119
120/// @brief 绘制一个盲文点。
121/// @param x 盲文点的x坐标。
122/// @param y 盲文点的y坐标。
123/// @param value 盲文点是否填充。
124/// @param style 单元格的样式。
125void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) {
126 Style(x, y, style);
127 if (value) {
128 DrawPointOn(x, y);
129 } else {
130 DrawPointOff(x, y);
131 }
132}
133
134/// @brief 绘制一个盲文点。
135/// @param x 盲文点的x坐标。
136/// @param y 盲文点的y坐标。
137void Canvas::DrawPointOn(int x, int y) {
138 if (!IsIn(x, y)) {
139 return;
140 }
141 Cell& cell = storage_[XY{x / 2, y / 4}];
142 if (cell.type != CellType::kBraille) {
143 cell.content.character = "⠀"; // 3 bytes.
144 cell.type = CellType::kBraille;
145 }
146
147 cell.content.character[1] |= g_map_braille[x % 2][y % 4][0]; // NOLINT
148 cell.content.character[2] |= g_map_braille[x % 2][y % 4][1]; // NOLINT
149}
150
151/// @brief 擦除一个盲文点。
152/// @param x 盲文点的x坐标。
153/// @param y 盲文点的y坐标。
154void Canvas::DrawPointOff(int x, int y) {
155 if (!IsIn(x, y)) {
156 return;
157 }
158 Cell& cell = storage_[XY{x / 2, y / 4}];
159 if (cell.type != CellType::kBraille) {
160 cell.content.character = "⠀"; // 3 byt
161 cell.type = CellType::kBraille;
162 }
163
164 cell.content.character[1] &= ~(g_map_braille[x % 2][y % 4][0]); // NOLINT
165 cell.content.character[2] &= ~(g_map_braille[x % 2][y % 4][1]); // NOLINT
166}
167
168/// @brief 切换盲文点。已填充的将被擦除,另一个将被绘制。
169/// @param x 盲文点的x坐标。
170/// @param y 盲文点的y坐标。
171void Canvas::DrawPointToggle(int x, int y) {
172 if (!IsIn(x, y)) {
173 return;
174 }
175 Cell& cell = storage_[XY{x / 2, y / 4}];
176 if (cell.type != CellType::kBraille) {
177 cell.content.character = "⠀"; // 3 byt
178 cell.type = CellType::kBraille;
179 }
180
181 cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0]; // NOLINT
182 cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1]; // NOLINT
183}
184
185/// @brief 绘制由盲文点组成的线条。
186/// @param x1 第一个盲文点的x坐标。
187/// @param y1 第一个盲文点的y坐标。
188/// @param x2 第二个盲文点的x坐标。
189/// @param y2 第二个盲文点的y坐标。
190void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) {
191 DrawPointLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
192}
193
194/// @brief 绘制由盲文点组成的线条。
195/// @param x1 第一个盲文点的x坐标。
196/// @param y1 第一个盲文点的y坐标。
197/// @param x2 第二个盲文点的x坐标。
198/// @param y2 第二个盲文点的y坐标。
199/// @param color 线条的颜色。
200void Canvas::DrawPointLine(int x1, int y1, int x2, int y2, const Color& color) {
201 DrawPointLine(x1, y1, x2, y2,
202 [color](Pixel& p) { p.foreground_color = color; });
203}
204
205/// @brief 绘制由盲文点组成的线条。
206/// @param x1 第一个盲文点的x坐标。
207/// @param y1 第一个盲文点的y坐标。
208/// @param x2 第二个盲文点的x坐标。
209/// @param y2 第二个盲文点的y坐标。
210/// @param style 线条的样式。
212 int y1,
213 int x2,
214 int y2,
215 const Stylizer& style) {
216 const int dx = std::abs(x2 - x1);
217 const int dy = std::abs(y2 - y1);
218 const int sx = x1 < x2 ? 1 : -1;
219 const int sy = y1 < y2 ? 1 : -1;
220 const int length = std::max(dx, dy);
221
222 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
223 return;
224 }
225 if (dx + dx > width_ * height_) {
226 return;
227 }
228
229 int error = dx - dy;
230 for (int i = 0; i < length; ++i) {
231 DrawPoint(x1, y1, true, style);
232 if (2 * error >= -dy) {
233 error -= dy;
234 x1 += sx;
235 }
236 if (2 * error <= dx) {
237 error += dx;
238 y1 += sy;
239 }
240 }
241 DrawPoint(x2, y2, true, style);
242}
243
244/// @brief 绘制由盲文点组成的圆。
245/// @param x 圆心的x坐标。
246/// @param y 圆心的y坐标。
247/// @param radius 圆的半径。
248void Canvas::DrawPointCircle(int x, int y, int radius) {
249 DrawPointCircle(x, y, radius, [](Pixel& /*pixel*/) {});
250}
251
252/// @brief 绘制由盲文点组成的圆。
253/// @param x 圆心的x坐标。
254/// @param y 圆心的y坐标。
255/// @param radius 圆的半径。
256/// @param color 圆的颜色。
257void Canvas::DrawPointCircle(int x, int y, int radius, const Color& color) {
258 DrawPointCircle(x, y, radius,
259 [color](Pixel& p) { p.foreground_color = color; });
260}
261
262/// @brief 绘制由盲文点组成的圆。
263/// @param x 圆心的x坐标。
264/// @param y 圆心的y坐标。
265/// @param radius 圆的半径。
266/// @param style 圆的样式。
267void Canvas::DrawPointCircle(int x, int y, int radius, const Stylizer& style) {
268 DrawPointEllipse(x, y, radius, radius, style);
269}
270
271/// @brief 绘制由盲文点组成的实心圆。
272/// @param x 圆心的x坐标。
273/// @param y 圆心的y坐标。
274/// @param radius 圆的半径。
275void Canvas::DrawPointCircleFilled(int x, int y, int radius) {
276 DrawPointCircleFilled(x, y, radius, [](Pixel& /*pixel*/) {});
277}
278
279/// @brief 绘制由盲文点组成的实心圆。
280/// @param x 圆心的x坐标。
281/// @param y 圆心的y坐标。
282/// @param radius 圆的半径。
283/// @param color 圆的颜色。
285 int y,
286 int radius,
287 const Color& color) {
288 DrawPointCircleFilled(x, y, radius,
289 [color](Pixel& p) { p.foreground_color = color; });
290}
291
292/// @brief 绘制由盲文点组成的实心圆。
293/// @param x 圆心的x坐标。
294/// @param y 圆心的y坐标。
295/// @param radius 圆的半径。
296/// @param style 圆的样式。
298 int y,
299 int radius,
300 const Stylizer& style) {
301 DrawPointEllipseFilled(x, y, radius, radius, style);
302}
303
304/// @brief 绘制由盲文点组成的椭圆。
305/// @param x 椭圆中心的x坐标。
306/// @param y 椭圆中心的y坐标。
307/// @param r1 椭圆沿x轴的半径。
308/// @param r2 椭圆沿y轴的半径。
309void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) {
310 DrawPointEllipse(x, y, r1, r2, [](Pixel& /*pixel*/) {});
311}
312
313/// @brief 绘制由盲文点组成的椭圆。
314/// @param x 椭圆中心的x坐标。
315/// @param y 椭圆中心的y坐标。
316/// @param r1 椭圆沿x轴的半径。
317/// @param r2 椭圆沿y轴的半径。
318/// @param color 椭圆的颜色。
320 int y,
321 int r1,
322 int r2,
323 const Color& color) {
324 DrawPointEllipse(x, y, r1, r2,
325 [color](Pixel& p) { p.foreground_color = color; });
326}
327
328/// @brief 绘制由盲文点组成的椭圆。
329/// @param x1 椭圆中心的x坐标。
330/// @param y1 椭圆中心的y坐标。
331/// @param r1 椭圆沿x轴的半径。
332/// @param r2 椭圆沿y轴的半径。
333/// @param s 椭圆的样式。
335 int y1,
336 int r1,
337 int r2,
338 const Stylizer& s) {
339 int x = -r1;
340 int y = 0;
341 int e2 = r2;
342 int dx = (1 + 2 * x) * e2 * e2;
343 int dy = x * x;
344 int err = dx + dy;
345
346 do { // NOLINT
347 DrawPoint(x1 - x, y1 + y, true, s);
348 DrawPoint(x1 + x, y1 + y, true, s);
349 DrawPoint(x1 + x, y1 - y, true, s);
350 DrawPoint(x1 - x, y1 - y, true, s);
351 e2 = 2 * err;
352 if (e2 >= dx) {
353 x++;
354 err += dx += 2 * r2 * r2;
355 }
356 if (e2 <= dy) {
357 y++;
358 err += dy += 2 * r1 * r1;
359 }
360 } while (x <= 0);
361
362 while (y++ < r2) {
363 DrawPoint(x1, y1 + y, true, s);
364 DrawPoint(x1, y1 - y, true, s);
365 }
366}
367
368/// @brief 绘制由盲文点组成的实心椭圆。
369/// @param x1 椭圆中心的x坐标。
370/// @param y1 椭圆中心的y坐标。
371/// @param r1 椭圆沿x轴的半径。
372/// @param r2 椭圆沿y轴的半径。
373void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) {
374 DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel& /*pixel*/) {});
375}
376
377/// @brief 绘制由盲文点组成的实心椭圆。
378/// @param x1 椭圆中心的x坐标。
379/// @param y1 椭圆中心的y坐标。
380/// @param r1 椭圆沿x轴的半径。
381/// @param r2 椭圆沿y轴的半径。
382/// @param color 椭圆的颜色。
384 int y1,
385 int r1,
386 int r2,
387 const Color& color) {
388 DrawPointEllipseFilled(x1, y1, r1, r2,
389 [color](Pixel& p) { p.foreground_color = color; });
390}
391
392/// @brief 绘制由盲文点组成的实心椭圆。
393/// @param x1 椭圆中心的x坐标。
394/// @param y1 椭圆中心的y坐标。
395/// @param r1 椭圆沿x轴的半径。
396/// @param r2 椭圆沿y轴的半径。
397/// @param s 椭圆的样式。
399 int y1,
400 int r1,
401 int r2,
402 const Stylizer& s) {
403 int x = -r1;
404 int y = 0;
405 int e2 = r2;
406 int dx = (1 + 2 * x) * e2 * e2;
407 int dy = x * x;
408 int err = dx + dy;
409
410 do { // NOLINT
411 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
412 DrawPoint(xx, y1 + y, true, s);
413 DrawPoint(xx, y1 - y, true, s);
414 }
415 e2 = 2 * err;
416 if (e2 >= dx) {
417 x++;
418 err += dx += 2 * r2 * r2;
419 }
420 if (e2 <= dy) {
421 y++;
422 err += dy += 2 * r1 * r1;
423 }
424 } while (x <= 0);
425
426 while (y++ < r2) {
427 for (int yy = y1 - y; yy <= y1 + y; ++yy) {
428 DrawPoint(x1, yy, true, s);
429 }
430 }
431}
432
433/// @brief 绘制一个块。
434/// @param x 块的x坐标。
435/// @param y 块的y坐标。
436/// @param value 块是否填充。
437void Canvas::DrawBlock(int x, int y, bool value) {
438 DrawBlock(x, y, value, [](Pixel& /*pixel*/) {});
439}
440
441/// @brief 绘制一个块。
442/// @param x 块的x坐标。
443/// @param y 块的y坐标。
444/// @param value 块是否填充。
445/// @param color 块的颜色。
446void Canvas::DrawBlock(int x, int y, bool value, const Color& color) {
447 DrawBlock(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
448}
449
450/// @brief 绘制一个块。
451/// @param x 块的x坐标。
452/// @param y 块的y坐标。
453/// @param value 块是否填充。
454/// @param style 块的样式。
455void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) {
456 Style(x, y, style);
457 if (value) {
458 DrawBlockOn(x, y);
459 } else {
460 DrawBlockOff(x, y);
461 }
462}
463
464/// @brief 绘制一个块。
465/// @param x 块的x坐标。
466/// @param y 块的y坐标。
467void Canvas::DrawBlockOn(int x, int y) {
468 if (!IsIn(x, y)) {
469 return;
470 }
471 y /= 2;
472 Cell& cell = storage_[XY{x / 2, y / 2}];
473 if (cell.type != CellType::kBlock) {
474 cell.content.character = " ";
475 cell.type = CellType::kBlock;
476 }
477
478 const uint8_t bit = (x % 2) * 2 + y % 2;
479 uint8_t value = g_map_block_inversed.at(cell.content.character);
480 value |= 1U << bit;
481 cell.content.character = g_map_block[value];
482}
483
484/// @brief 擦除一个块。
485/// @param x 块的x坐标。
486/// @param y 块的y坐标。
487void Canvas::DrawBlockOff(int x, int y) {
488 if (!IsIn(x, y)) {
489 return;
490 }
491 Cell& cell = storage_[XY{x / 2, y / 4}];
492 if (cell.type != CellType::kBlock) {
493 cell.content.character = " ";
494 cell.type = CellType::kBlock;
495 }
496 y /= 2;
497
498 const uint8_t bit = (y % 2) * 2 + x % 2;
499 uint8_t value = g_map_block_inversed.at(cell.content.character);
500 value &= ~(1U << bit);
501 cell.content.character = g_map_block[value];
502}
503
504/// @brief 切换一个块。如果已填充,则擦除。如果为空,
505/// 将被填充。
506/// @param x 块的x坐标。
507/// @param y 块的y坐标。
508void Canvas::DrawBlockToggle(int x, int y) {
509 if (!IsIn(x, y)) {
510 return;
511 }
512 Cell& cell = storage_[XY{x / 2, y / 4}];
513 if (cell.type != CellType::kBlock) {
514 cell.content.character = " ";
515 cell.type = CellType::kBlock;
516 }
517 y /= 2;
518
519 const uint8_t bit = (y % 2) * 2 + x % 2;
520 uint8_t value = g_map_block_inversed.at(cell.content.character);
521 value ^= 1U << bit;
522 cell.content.character = g_map_block[value];
523}
524
525/// @brief 绘制由块字符组成的线条。
526/// @param x1 线条第一个点的x坐标。
527/// @param y1 线条第一个点的y坐标。
528/// @param x2 线条第二个点的x坐标。
529/// @param y2 线条第二个点的y坐标。
530void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) {
531 DrawBlockLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
532}
533
534/// @brief 绘制由块字符组成的线条。
535/// @param x1 线条第一个点的x坐标。
536/// @param y1 线条第一个点的y坐标。
537/// @param x2 线条第二个点的x坐标。
538/// @param y2 线条第二个点的y坐标。
539/// @param color 线条的颜色。
540void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color) {
541 DrawBlockLine(x1, y1, x2, y2,
542 [color](Pixel& p) { p.foreground_color = color; });
543}
544
545/// @brief 绘制由块字符组成的线条。
546/// @param x1 线条第一个点的x坐标。
547/// @param y1 线条第一个点的y坐标。
548/// @param x2 线条第二个点的x坐标。
549/// @param y2 线条第二个点的y坐标。
550/// @param style 线条的样式。
552 int y1,
553 int x2,
554 int y2,
555 const Stylizer& style) {
556 y1 /= 2;
557 y2 /= 2;
558
559 const int dx = std::abs(x2 - x1);
560 const int dy = std::abs(y2 - y1);
561 const int sx = x1 < x2 ? 1 : -1;
562 const int sy = y1 < y2 ? 1 : -1;
563 const int length = std::max(dx, dy);
564
565 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
566 return;
567 }
568 if (dx + dx > width_ * height_) {
569 return;
570 }
571
572 int error = dx - dy;
573 for (int i = 0; i < length; ++i) {
574 DrawBlock(x1, y1 * 2, true, style);
575 if (2 * error >= -dy) {
576 error -= dy;
577 x1 += sx;
578 }
579 if (2 * error <= dx) {
580 error += dx;
581 y1 += sy;
582 }
583 }
584 DrawBlock(x2, y2 * 2, true, style);
585}
586
587/// @brief 绘制由块字符组成的圆。
588/// @param x 圆心的x坐标。
589/// @param y 圆心的y坐标。
590/// @param radius 圆的半径。
591void Canvas::DrawBlockCircle(int x, int y, int radius) {
592 DrawBlockCircle(x, y, radius, nostyle);
593}
594
595/// @brief 绘制由块字符组成的圆。
596/// @param x 圆心的x坐标。
597/// @param y 圆心的y坐标。
598/// @param radius 圆的半径。
599/// @param color 圆的颜色。
600void Canvas::DrawBlockCircle(int x, int y, int radius, const Color& color) {
601 DrawBlockCircle(x, y, radius,
602 [color](Pixel& p) { p.foreground_color = color; });
603}
604
605/// @brief 绘制由块字符组成的圆。
606/// @param x 圆心的x坐标。
607/// @param y 圆心的y坐标。
608/// @param radius 圆的半径。
609/// @param style 圆的样式。
610void Canvas::DrawBlockCircle(int x, int y, int radius, const Stylizer& style) {
611 DrawBlockEllipse(x, y, radius, radius, style);
612}
613
614/// @brief 绘制由块字符组成的实心圆。
615/// @param x 圆心的x坐标。
616/// @param y 圆心的y坐标。
617/// @param radius 圆的半径。
618void Canvas::DrawBlockCircleFilled(int x, int y, int radius) {
619 DrawBlockCircleFilled(x, y, radius, nostyle);
620}
621
622/// @brief 绘制由块字符组成的实心圆。
623/// @param x 圆心的x坐标。
624/// @param y 圆心的y坐标。
625/// @param radius 圆的半径。
626/// @param color 圆的颜色。
628 int y,
629 int radius,
630 const Color& color) {
631 DrawBlockCircleFilled(x, y, radius,
632 [color](Pixel& p) { p.foreground_color = color; });
633}
634
635/// @brief 绘制由块字符组成的实心圆。
636/// @param x 圆心的x坐标。
637/// @param y 圆心的y坐标。
638/// @param radius 圆的半径。
639/// @param s 圆的样式。
641 int y,
642 int radius,
643 const Stylizer& s) {
644 DrawBlockEllipseFilled(x, y, radius, radius, s);
645}
646
647/// @brief 绘制由块字符组成的椭圆。
648/// @param x 椭圆中心的x坐标。
649/// @param y 椭圆中心的y坐标。
650/// @param r1 椭圆沿x轴的半径。
651/// @param r2 椭圆沿y轴的半径。
652void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) {
653 DrawBlockEllipse(x, y, r1, r2, nostyle);
654}
655
656/// @brief 绘制由块字符组成的椭圆。
657/// @param x 椭圆中心的x坐标。
658/// @param y 椭圆中心的y坐标。
659/// @param r1 椭圆沿x轴的半径。
660/// @param r2 椭圆沿y轴的半径。
661/// @param color 椭圆的颜色。
663 int y,
664 int r1,
665 int r2,
666 const Color& color) {
667 DrawBlockEllipse(x, y, r1, r2,
668 [color](Pixel& p) { p.foreground_color = color; });
669}
670
671/// @brief 绘制由块字符组成的椭圆。
672/// @param x1 椭圆中心的x坐标。
673/// @param y1 椭圆中心的y坐标。
674/// @param r1 椭圆沿x轴的半径。
675/// @param r2 椭圆沿y轴的半径。
676/// @param s 椭圆的样式。
678 int y1,
679 int r1,
680 int r2,
681 const Stylizer& s) {
682 y1 /= 2;
683 r2 /= 2;
684 int x = -r1;
685 int y = 0;
686 int e2 = r2;
687 int dx = (1 + 2 * x) * e2 * e2;
688 int dy = x * x;
689 int err = dx + dy;
690
691 do { // NOLINT
692 DrawBlock(x1 - x, 2 * (y1 + y), true, s);
693 DrawBlock(x1 + x, 2 * (y1 + y), true, s);
694 DrawBlock(x1 + x, 2 * (y1 - y), true, s);
695 DrawBlock(x1 - x, 2 * (y1 - y), true, s);
696 e2 = 2 * err;
697 if (e2 >= dx) {
698 x++;
699 err += dx += 2 * r2 * r2;
700 }
701 if (e2 <= dy) {
702 y++;
703 err += dy += 2 * r1 * r1;
704 }
705 } while (x <= 0);
706
707 while (y++ < r2) {
708 DrawBlock(x1, 2 * (y1 + y), true, s);
709 DrawBlock(x1, 2 * (y1 - y), true, s);
710 }
711}
712
713/// @brief 绘制由块字符组成的实心椭圆。
714/// @param x 椭圆中心的x坐标。
715/// @param y 椭圆中心的y坐标。
716/// @param r1 椭圆沿x轴的半径。
717/// @param r2 椭圆沿y轴的半径。
718void Canvas::DrawBlockEllipseFilled(int x, int y, int r1, int r2) {
719 DrawBlockEllipseFilled(x, y, r1, r2, nostyle);
720}
721
722/// @brief 绘制由块字符组成的实心椭圆。
723/// @param x 椭圆中心的x坐标。
724/// @param y 椭圆中心的y坐标。
725/// @param r1 椭圆沿x轴的半径。
726/// @param r2 椭圆沿y轴的半径。
727/// @param color 椭圆的颜色。
729 int y,
730 int r1,
731 int r2,
732 const Color& color) {
733 DrawBlockEllipseFilled(x, y, r1, r2,
734 [color](Pixel& p) { p.foreground_color = color; });
735}
736
737/// @brief 绘制由块字符组成的实心椭圆。
738/// @param x1 椭圆中心的x坐标。
739/// @param y1 椭圆中心的y坐标。
740/// @param r1 椭圆沿x轴的半径。
741/// @param r2 椭圆沿y轴的半径。
742/// @param s 椭圆的样式。
744 int y1,
745 int r1,
746 int r2,
747 const Stylizer& s) {
748 y1 /= 2;
749 r2 /= 2;
750 int x = -r1;
751 int y = 0;
752 int e2 = r2;
753 int dx = (1 + 2 * x) * e2 * e2;
754 int dy = x * x;
755 int err = dx + dy;
756
757 do { // NOLINT
758 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
759 DrawBlock(xx, 2 * (y1 + y), true, s);
760 DrawBlock(xx, 2 * (y1 - y), true, s);
761 }
762 e2 = 2 * err;
763 if (e2 >= dx) {
764 x++;
765 err += dx += 2 * r2 * r2;
766 }
767 if (e2 <= dy) {
768 y++;
769 err += dy += 2 * r1 * r1;
770 }
771 } while (x <= 0);
772
773 while (y++ < r2) {
774 for (int yy = y1 + y; yy <= y1 - y; ++yy) {
775 DrawBlock(x1, 2 * yy, true, s);
776 }
777 }
778}
779
780/// @brief 绘制一段文本。
781/// @param x 文本的x坐标。
782/// @param y 文本的y坐标。
783/// @param value 要绘制的文本。
784void Canvas::DrawText(int x, int y, const std::string& value) {
785 DrawText(x, y, value, nostyle);
786}
787
788/// @brief 绘制一段文本。
789/// @param x 文本的x坐标。
790/// @param y 文本的y坐标。
791/// @param value 要绘制的文本。
792/// @param color 文本的颜色。
794 int y,
795 const std::string& value,
796 const Color& color) {
797 DrawText(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
798}
799
800/// @brief 绘制一段文本。
801/// @param x 文本的x坐标。
802/// @param y 文本的y坐标。
803/// @param value 要绘制的文本。
804/// @param style 文本的样式。
806 int y,
807 const std::string& value,
808 const Stylizer& style) {
809 for (const auto& it : Utf8ToGlyphs(value)) {
810 if (!IsIn(x, y)) {
811 x += 2;
812 continue;
813 }
814 Cell& cell = storage_[XY{x / 2, y / 4}];
815 cell.type = CellType::kCell;
816 cell.content.character = it;
817 style(cell.content);
818 x += 2;
819 }
820}
821
822/// @brief 在给定坐标处直接绘制预定义像素
823/// @param x 像素的x坐标。
824/// @param y 像素的y坐标。
825/// @param p 要绘制的像素。
826void Canvas::DrawPixel(int x, int y, const Pixel& p) {
827 Cell& cell = storage_[XY{x / 2, y / 4}];
828 cell.type = CellType::kCell;
829 cell.content = p;
830}
831
832/// @brief 在给定坐标处绘制预定义图像,左上角位于该坐标
833/// 您可以提供负坐标来随意对齐图像 -
834/// 只会绘制“可见”部分
835/// @param x 图像左上角对应的x坐标。
836/// @param y 图像左上角对应的y坐标。
837/// @param image 要绘制的图像。
838void Canvas::DrawImage(int x, int y, const Image& image) {
839 x /= 2;
840 y /= 4;
841 const int dx_begin = std::max(0, -x);
842 const int dy_begin = std::max(0, -y);
843 const int dx_end = std::min(image.dimx(), width_ - x);
844 const int dy_end = std::min(image.dimy(), height_ - y);
845
846 for (int dy = dy_begin; dy < dy_end; ++dy) {
847 for (int dx = dx_begin; dx < dx_end; ++dx) {
848 Cell& cell = storage_[XY{
849 x + dx,
850 y + dy,
851 }];
852 cell.type = CellType::kCell;
853 cell.content = image.PixelAt(dx, dy);
854 }
855 }
856}
857
858/// @brief 修改给定位置的像素。
859/// @param style 修改像素的函数。
860void Canvas::Style(int x, int y, const Stylizer& style) {
861 if (IsIn(x, y)) {
862 style(storage_[XY{x / 2, y / 4}].content);
863 }
864}
865
866namespace {
867
868class CanvasNodeBase : public Node {
869 public:
870 CanvasNodeBase() = default;
871
872 void Render(Screen& screen) override {
873 const Canvas& c = canvas();
874 const int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
875 const int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
876 for (int y = 0; y < y_max; ++y) {
877 for (int x = 0; x < x_max; ++x) {
878 screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.GetPixel(x, y);
879 }
880 }
881 }
882
883 virtual const Canvas& canvas() = 0;
884};
885
886} // namespace
887
888/// @brief 从 Canvas 或 Canvas 引用生成元素。
889// NOLINTNEXTLINE
891 class Impl : public CanvasNodeBase {
892 public:
893 explicit Impl(ConstRef<Canvas> canvas) : canvas_(std::move(canvas)) {
894 requirement_.min_x = (canvas_->width() + 1) / 2;
895 requirement_.min_y = (canvas_->height() + 3) / 4;
896 }
897 const Canvas& canvas() final { return *canvas_; }
898 ConstRef<Canvas> canvas_;
899 };
900 return std::make_shared<Impl>(canvas);
901}
902
903/// @brief 生成一个绘制指定大小画布的元素。
904/// @param width 画布的宽度。
905/// @param height 画布的高度。
906/// @param fn 绘制画布的函数。
907Element canvas(int width, int height, std::function<void(Canvas&)> fn) {
908 class Impl : public CanvasNodeBase {
909 public:
910 Impl(int width, int height, std::function<void(Canvas&)> fn)
911 : width_(width), height_(height), fn_(std::move(fn)) {}
912
913 void ComputeRequirement() final {
914 requirement_.min_x = (width_ + 1) / 2;
915 requirement_.min_y = (height_ + 3) / 4;
916 }
917
918 void Render(Screen& screen) final {
919 const int width = (box_.x_max - box_.x_min + 1) * 2;
920 const int height = (box_.y_max - box_.y_min + 1) * 4;
921 canvas_ = Canvas(width, height);
922 fn_(canvas_);
923 CanvasNodeBase::Render(screen);
924 }
925
926 const Canvas& canvas() final { return canvas_; }
927 Canvas canvas_;
928 int width_;
929 int height_;
930 std::function<void(Canvas&)> fn_;
931 };
932 return std::make_shared<Impl>(width, height, std::move(fn));
933}
934
935/// @brief 生成一个绘制画布的元素。
936/// @param fn 绘制画布的函数。
937Element canvas(std::function<void(Canvas&)> fn) {
938 const int default_dim = 12;
939 return canvas(default_dim, default_dim, std::move(fn));
940}
941
942} // namespace ftxui
一个适配器。拥有或引用一个不可变对象。
定义 ref.hpp:17
ButtonOption Style()
void DrawImage(int x, int y, const Image &)
在给定坐标处绘制预定义图像,左上角位于该坐标 您可以提供负坐标来随意对齐图像 - 只会绘制“可见”部分
void DrawBlockLine(int x1, int y1, int x2, int y2)
绘制由块字符组成的线条。
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
绘制由盲文点组成的实心椭圆。
void DrawPointLine(int x1, int y1, int x2, int y2)
绘制由盲文点组成的线条。
void DrawText(int x, int y, const std::string &value)
绘制一段文本。
Canvas()=default
std::function< void(Pixel &)> Stylizer
void DrawBlockOn(int x, int y)
绘制一个块。
void DrawPointCircleFilled(int x, int y, int radius)
绘制由盲文点组成的实心圆。
void DrawPointOn(int x, int y)
绘制一个盲文点。
void DrawPointOff(int x, int y)
擦除一个盲文点。
Pixel GetPixel(int x, int y) const
获取单元格的内容。
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
绘制由块字符组成的实心椭圆。
void DrawPointEllipse(int x, int y, int r1, int r2)
绘制由盲文点组成的椭圆。
void DrawPoint(int x, int y, bool value)
绘制一个盲文点。
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
绘制由块字符组成的椭圆。
void DrawBlockToggle(int x, int y)
切换一个块。如果已填充,则擦除。如果为空, 将被填充。
void DrawBlockCircle(int x1, int y1, int radius)
绘制由块字符组成的圆。
void DrawBlockCircleFilled(int x1, int y1, int radius)
绘制由块字符组成的实心圆。
void DrawPointCircle(int x, int y, int radius)
绘制由盲文点组成的圆。
int height() const
void DrawBlockOff(int x, int y)
擦除一个块。
int width() const
void DrawBlock(int x, int y, bool value)
绘制一个块。
void Style(int x, int y, const Stylizer &style)
修改给定位置的像素。
void DrawPointToggle(int x, int y)
切换盲文点。已填充的将被擦除,另一个将被绘制。
void DrawPixel(int x, int y, const Pixel &)
在给定坐标处直接绘制预定义像素
Node 是 DOM 树中所有元素的基类。
void Render(Screen &screen, const Element &element)
在 ftxui::Screen 上显示元素。
Decorator color(Color)
使用前景色进行装饰。
Canvas 是一个与绘图操作相关的可绘制缓冲区。
int dimy() const
Color foreground_color
std::string character
Pixel & PixelAt(int x, int y)
访问给定位置的单元格 (Pixel)。
int dimx() const
Color 是一个表示终端用户界面中颜色的类。
像素的矩形网格。
像素的矩形网格。
一个 Unicode 字符及其相关样式。
#include "ftxui/component/component_base.hpp" // 用于 ComponentBase
std::shared_ptr< Node > Element
std::vector< std::string > Utf8ToGlyphs(const std::string &input)
Element canvas(ConstRef< Canvas >)
从 Canvas 或 Canvas 引用生成元素。