FTXUI 7.1.0
C++ functional terminal UI.
Loading...
Searching...
No Matches
src/ftxui/dom/border.cpp
Go to the documentation of this file.
1// Copyright 2020 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 <algorithm> // for max
5#include <array> // for array
6#include <ftxui/screen/color.hpp> // for Color
7#include <memory> // for allocator, make_shared, __shared_ptr_access
8#include <optional> // for optional, nullopt
9#include <string> // for basic_string, string
10#include <utility> // for move
11#include <vector> // for vector
12
13#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, borderStyled, Elements, DASHED, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDashed, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderWith, window
14#include "ftxui/dom/node.hpp" // for Node, Elements
15#include "ftxui/dom/requirement.hpp" // for Requirement
16#include "ftxui/screen/box.hpp" // for Box
17#include "ftxui/screen/cell.hpp" // for Cell
18#include "ftxui/screen/screen.hpp" // for Cell, Screen
19
20namespace ftxui {
21
22namespace {
23using Charset = std::array<std::string, 6>; // NOLINT
24using Charsets = std::array<Charset, 6>; // NOLINT
25// NOLINTNEXTLINE
26static Charsets simple_border_charset = {
27 Charset{"┌", "┐", "└", "┘", "─", "│"}, // LIGHT
28 Charset{"┏", "┓", "┗", "┛", "╍", "╏"}, // DASHED
29 Charset{"┏", "┓", "┗", "┛", "━", "┃"}, // HEAVY
30 Charset{"╔", "╗", "╚", "╝", "═", "║"}, // DOUBLE
31 Charset{"╭", "╮", "╰", "╯", "─", "│"}, // ROUNDED
32 Charset{" ", " ", " ", " ", " ", " "}, // EMPTY
33};
34
35// For reference, here is the charset for normal border:
36class Border : public Node {
37 public:
38 Border(Elements children,
39 BorderStyle style,
40 std::optional<Color> foreground_color = std::nullopt)
41 : Node(std::move(children)),
42 charset_(simple_border_charset[style]) // NOLINT
43 ,
44 foreground_color_(foreground_color) {} // NOLINT
45
46 const Charset& charset_; // NOLINT
47 std::optional<Color> foreground_color_;
48
49 void ComputeRequirement() override {
50 Node::ComputeRequirement();
51 requirement_ = children_[0]->requirement();
52 requirement_.min_x += 2;
53 requirement_.min_y += 2;
54 if (children_.size() == 2) {
55 requirement_.min_x =
56 std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
57 }
58 requirement_.focused.box.x_min++;
59 requirement_.focused.box.x_max++;
60 requirement_.focused.box.y_min++;
61 requirement_.focused.box.y_max++;
62 }
63
64 void SetBox(Box box) override {
65 Node::SetBox(box);
66 if (children_.size() == 2) {
67 Box title_box;
68 title_box.x_min = box.x_min + 1;
69 title_box.x_max = box.x_max - 1;
70 title_box.y_min = box.y_min;
71 title_box.y_max = box.y_min;
72 children_[1]->SetBox(title_box);
73 }
74 box.x_min++;
75 box.x_max--;
76 box.y_min++;
77 box.y_max--;
78 children_[0]->SetBox(box);
79 }
80
81 void Render(Screen& screen) override {
82 // Draw content.
83 children_[0]->Render(screen);
84
85 // Draw the border.
86 if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) {
87 return;
88 }
89
90 screen.at(box_.x_min, box_.y_min) = charset_[0]; // NOLINT
91 screen.at(box_.x_max, box_.y_min) = charset_[1]; // NOLINT
92 screen.at(box_.x_min, box_.y_max) = charset_[2]; // NOLINT
93 screen.at(box_.x_max, box_.y_max) = charset_[3]; // NOLINT
94
95 for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
96 Cell& p1 = screen.CellAt(x, box_.y_min);
97 Cell& p2 = screen.CellAt(x, box_.y_max);
98 p1.character = charset_[4]; // NOLINT
99 p2.character = charset_[4]; // NOLINT
100 p1.automerge = true;
101 p2.automerge = true;
102 }
103 for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
104 Cell& p3 = screen.CellAt(box_.x_min, y);
105 Cell& p4 = screen.CellAt(box_.x_max, y);
106 p3.character = charset_[5]; // NOLINT
107 p4.character = charset_[5]; // NOLINT
108 p3.automerge = true;
109 p4.automerge = true;
110 }
111
112 // Draw title.
113 if (children_.size() == 2) {
114 RenderTitle(screen);
115 }
116
117 // Draw the border color.
118 if (foreground_color_) {
119 for (int x = box_.x_min; x <= box_.x_max; ++x) {
120 screen.CellAt(x, box_.y_min).foreground_color = *foreground_color_;
121 screen.CellAt(x, box_.y_max).foreground_color = *foreground_color_;
122 }
123 for (int y = box_.y_min; y <= box_.y_max; ++y) {
124 screen.CellAt(box_.x_min, y).foreground_color = *foreground_color_;
125 screen.CellAt(box_.x_max, y).foreground_color = *foreground_color_;
126 }
127 }
128 }
129
130 // The title spans the whole top border, so that it can be aligned. Restore
131 // the border cells it didn't draw on, so that its decorators (e.g. color)
132 // only apply to its content.
133 void RenderTitle(Screen& screen) {
134 std::vector<Cell> top_border;
135 for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
136 top_border.push_back(screen.CellAt(x, box_.y_min));
137 }
138 children_[1]->Render(screen);
139 for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
140 Cell& cell = screen.CellAt(x, box_.y_min);
141 const Cell& saved = top_border[x - box_.x_min - 1];
142 if (cell.character == saved.character) {
143 cell = saved;
144 }
145 }
146 }
147};
148
149// For reference, here is the charset for normal border:
150class BorderCell : public Node {
151 public:
152 BorderCell(Elements children, Cell pixel)
153 : Node(std::move(children)), pixel_(std::move(pixel)) {}
154
155 private:
156 Cell pixel_;
157
158 void ComputeRequirement() override {
159 Node::ComputeRequirement();
160 requirement_ = children_[0]->requirement();
161 requirement_.min_x += 2;
162 requirement_.min_y += 2;
163 if (children_.size() == 2) {
164 requirement_.min_x =
165 std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
166 }
167
168 requirement_.focused.box.Shift(1, 1);
169 }
170
171 void SetBox(Box box) override {
172 Node::SetBox(box);
173 if (children_.size() == 2) {
174 Box title_box;
175 title_box.x_min = box.x_min + 1;
176 title_box.x_max = box.x_max - 1;
177 title_box.y_min = box.y_min;
178 title_box.y_max = box.y_min;
179 children_[1]->SetBox(title_box);
180 }
181 box.x_min++;
182 box.x_max--;
183 box.y_min++;
184 box.y_max--;
185 children_[0]->SetBox(box);
186 }
187
188 void Render(Screen& screen) override {
189 // Draw content.
190 children_[0]->Render(screen);
191
192 // Draw the border.
193 if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) {
194 return;
195 }
196
197 screen.CellAt(box_.x_min, box_.y_min) = pixel_;
198 screen.CellAt(box_.x_max, box_.y_min) = pixel_;
199 screen.CellAt(box_.x_min, box_.y_max) = pixel_;
200 screen.CellAt(box_.x_max, box_.y_max) = pixel_;
201
202 for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
203 screen.CellAt(x, box_.y_min) = pixel_;
204 screen.CellAt(x, box_.y_max) = pixel_;
205 }
206 for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
207 screen.CellAt(box_.x_min, y) = pixel_;
208 screen.CellAt(box_.x_max, y) = pixel_;
209 }
210 }
211};
212} // namespace
213
214/// @brief Draw a border around the element.
215/// @ingroup dom
216/// @see border
217/// @see borderLight
218/// @see borderDashed
219/// @see borderDouble
220/// @see borderHeavy
221/// @see borderEmpty
222/// @see borderRounded
223/// @see borderStyled
224/// @see borderWith
225///
226/// Add a border around an element
227///
228/// ### Example
229///
230/// ```cpp
231/// // Use 'border' as a function...
232/// Element document = border(text("The element"));
233///
234/// // ...Or as a 'pipe'.
235/// Element document = text("The element") | border;
236/// ```
237///
238/// ### Output
239///
240/// ```bash
241/// ┌───────────┐
242/// │The element│
243/// └───────────┘
244/// ```
246 return std::make_shared<Border>(unpack(std::move(child)), ROUNDED);
247}
248
249/// @brief Same as border but with a constant Cell around the element.
250/// @ingroup dom
251/// @see border
252Decorator borderWith(const Cell& pixel) {
253 return [pixel](Element child) {
254 return std::make_shared<BorderCell>(unpack(std::move(child)), pixel);
255 };
256}
257
258/// @brief Same as border but with different styles.
259/// @ingroup dom
260/// @see border
262 return [style](Element child) {
263 return std::make_shared<Border>(unpack(std::move(child)), style);
264 };
265}
266
267/// @brief Same as border but with a foreground color.
268/// @ingroup dom
269/// @see border
270Decorator borderStyled(Color foreground_color) {
271 return [foreground_color](Element child) {
272 return std::make_shared<Border>(unpack(std::move(child)), ROUNDED,
273 foreground_color);
274 };
275}
276
277/// @brief Same as border but with a foreground color and a different style
278/// @ingroup dom
279/// @see border
280Decorator borderStyled(BorderStyle style, Color foreground_color) {
281 return [style, foreground_color](Element child) {
282 return std::make_shared<Border>(unpack(std::move(child)), style,
283 foreground_color);
284 };
285}
286
287/// @brief Draw a dashed border around the element.
288/// @ingroup dom
289/// @see border
290/// @see borderLight
291/// @see borderDashed
292/// @see borderDouble
293/// @see borderHeavy
294/// @see borderRounded
295/// @see borderEmpty
296/// @see borderStyled
297/// @see borderWith
298///
299/// Add a border around an element
300///
301/// ### Example
302///
303/// ```cpp
304/// // Use 'borderDash' as a function...
305/// Element document = borderDash(text("The element"));
306///
307/// // ...Or as a 'pipe'.
308/// Element document = text("The element") | borderDAsh;
309/// ```
310///
311/// ### Output
312///
313/// ```bash
314/// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
315/// ╏The element ╏
316/// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
317/// ```
319 return std::make_shared<Border>(unpack(std::move(child)), DASHED);
320}
321
322/// @brief Draw a light border around the element.
323/// @ingroup dom
324/// @see border
325/// @see borderLight
326/// @see borderDashed
327/// @see borderDouble
328/// @see borderHeavy
329/// @see borderRounded
330/// @see borderEmpty
331/// @see borderStyled
332/// @see borderWith
333///
334/// Add a border around an element
335///
336/// ### Example
337///
338/// ```cpp
339/// // Use 'borderLight' as a function...
340/// Element document = borderLight(text("The element"));
341///
342/// // ...Or as a 'pipe'.
343/// Element document = text("The element") | borderLight;
344/// ```
345///
346/// ### Output
347///
348/// ```bash
349/// ┌──────────────┐
350/// │The element │
351/// └──────────────┘
352/// ```
354 return std::make_shared<Border>(unpack(std::move(child)), LIGHT);
355}
356
357/// @brief Draw a heavy border around the element.
358/// @ingroup dom
359/// @see border
360/// @see borderLight
361/// @see borderDashed
362/// @see borderDouble
363/// @see borderHeavy
364/// @see borderRounded
365/// @see borderEmpty
366/// @see borderStyled
367/// @see borderWith
368///
369/// Add a border around an element
370///
371/// ### Example
372///
373/// ```cpp
374/// // Use 'borderHeavy' as a function...
375/// Element document = borderHeavy(text("The element"));
376///
377/// // ...Or as a 'pipe'.
378/// Element document = text("The element") | borderHeavy;
379/// ```
380///
381/// ### Output
382///
383/// ```bash
384/// ┏━━━━━━━━━━━━━━┓
385/// ┃The element ┃
386/// ┗━━━━━━━━━━━━━━┛
387/// ```
389 return std::make_shared<Border>(unpack(std::move(child)), HEAVY);
390}
391
392/// @brief Draw a double border around the element.
393/// @ingroup dom
394/// @see border
395/// @see borderLight
396/// @see borderDashed
397/// @see borderDouble
398/// @see borderHeavy
399/// @see borderRounded
400/// @see borderEmpty
401/// @see borderStyled
402/// @see borderWith
403///
404/// Add a border around an element
405///
406/// ### Example
407///
408/// ```cpp
409/// // Use 'borderDouble' as a function...
410/// Element document = borderDouble(text("The element"));
411///
412/// // ...Or as a 'pipe'.
413/// Element document = text("The element") | borderDouble;
414/// ```
415///
416/// ### Output
417///
418/// ```bash
419/// ╔══════════════╗
420/// ║The element ║
421/// ╚══════════════╝
422/// ```
424 return std::make_shared<Border>(unpack(std::move(child)), DOUBLE);
425}
426
427/// @brief Draw a rounded border around the element.
428/// @ingroup dom
429/// @see border
430/// @see borderLight
431/// @see borderDashed
432/// @see borderDouble
433/// @see borderHeavy
434/// @see borderRounded
435/// @see borderEmpty
436/// @see borderStyled
437/// @see borderWith
438///
439/// Add a border around an element
440///
441/// ### Example
442///
443/// ```cpp
444/// // Use 'borderRounded' as a function...
445/// Element document = borderRounded(text("The element"));
446///
447/// // ...Or as a 'pipe'.
448/// Element document = text("The element") | borderRounded;
449/// ```
450///
451/// ### Output
452///
453/// ```bash
454/// ╭──────────────╮
455/// │The element │
456/// ╰──────────────╯
457/// ```
459 return std::make_shared<Border>(unpack(std::move(child)), ROUNDED);
460}
461
462/// @brief Draw an empty border around the element.
463/// @ingroup dom
464/// @see border
465/// @see borderLight
466/// @see borderDashed
467/// @see borderDouble
468/// @see borderHeavy
469/// @see borderRounded
470/// @see borderEmpty
471/// @see borderStyled
472/// @see borderWith
473///
474/// Add a border around an element
475///
476/// ### Example
477///
478/// ```cpp
479/// // Use 'borderRounded' as a function...
480/// Element document = borderRounded(text("The element"));
481///
482/// // ...Or as a 'pipe'.
483/// Element document = text("The element") | borderRounded;
484/// ```
485///
486/// ### Output
487///
488/// ```bash
489///
490/// The element
491///
492/// ```
494 return std::make_shared<Border>(unpack(std::move(child)), EMPTY);
495}
496
497/// @brief Draw window with a title and a border around the element.
498/// @param title The title of the window.
499/// @param content The element to be wrapped.
500/// @param border The style of the border. Default is ROUNDED.
501/// @ingroup dom
502/// @see border
503///
504/// ### Example
505///
506/// ```cpp
507/// Element document = window(text("Title"),
508/// text("content")
509/// );
510///
511/// // With specifying border
512/// Element document = window(text("Title"),
513/// text("content"),
514/// ROUNDED
515/// );
516/// ```
517///
518/// ### Output
519///
520/// ```bash
521/// ┌Title──┐
522/// │content│
523/// └───────┘
524/// ```
525Element window(Element title, Element content, BorderStyle border) {
526 return std::make_shared<Border>(unpack(std::move(content), std::move(title)),
527 border);
528}
529} // namespace ftxui
Element window(Element title, Element content, BorderStyle border=ROUNDED)
Draw window with a title and a border around the element.
Element borderDouble(Element child)
Draw a double border around the element.
Element borderDashed(Element child)
Draw a dashed border around the element.
Element borderRounded(Element child)
Draw a rounded border around the element.
BorderStyle
BorderStyle is an enumeration that represents the different styles of borders that can be applied to ...
Definition elements.hpp:38
Element borderHeavy(Element child)
Draw a heavy border around the element.
Decorator borderWith(const Cell &pixel)
Same as border but with a constant Cell around the element.
Element borderLight(Element child)
Draw a light border around the element.
Decorator borderStyled(BorderStyle style)
Same as border but with different styles.
Element border(Element child)
Draw a border around the element.
Element borderEmpty(Element child)
Draw an empty border around the element.
@ EMPTY
Definition elements.hpp:44
@ DOUBLE
Definition elements.hpp:42
@ HEAVY
Definition elements.hpp:41
@ ROUNDED
Definition elements.hpp:43
@ DASHED
Definition elements.hpp:40
@ LIGHT
Definition elements.hpp:39
The FTXUI ftxui:: namespace.
Definition animation.hpp:11
std::shared_ptr< Node > Element
Definition app.hpp:25
std::vector< Element > Elements
Definition elements.hpp:26
std::function< Element(Element)> Decorator
Definition elements.hpp:27
int y
Definition elements.hpp:134
void Render(Screen &screen, Node *node, Selection &selection)
Definition node.cpp:105
const Charset & charset_
std::optional< Color > foreground_color_