FTXUI 6.1.9
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
12#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
13#include "ftxui/dom/node.hpp" // for Node, Elements
14#include "ftxui/dom/requirement.hpp" // for Requirement
15#include "ftxui/screen/box.hpp" // for Box
16#include "ftxui/screen/pixel.hpp" // for Pixel
17#include "ftxui/screen/screen.hpp" // for Pixel, Screen
18
19namespace ftxui {
20
21namespace {
22using Charset = std::array<std::string, 6>; // NOLINT
23using Charsets = std::array<Charset, 6>; // NOLINT
24// NOLINTNEXTLINE
25static Charsets simple_border_charset = {
26 Charset{"┌", "┐", "└", "┘", "─", "│"}, // LIGHT
27 Charset{"┏", "┓", "┗", "┛", "╍", "╏"}, // DASHED
28 Charset{"┏", "┓", "┗", "┛", "━", "┃"}, // HEAVY
29 Charset{"╔", "╗", "╚", "╝", "═", "║"}, // DOUBLE
30 Charset{"╭", "╮", "╰", "╯", "─", "│"}, // ROUNDED
31 Charset{" ", " ", " ", " ", " ", " "}, // EMPTY
32};
33
34// For reference, here is the charset for normal border:
35class Border : public Node {
36 public:
37 Border(Elements children,
38 BorderStyle style,
39 std::optional<Color> foreground_color = std::nullopt)
40 : Node(std::move(children)),
41 charset_(simple_border_charset[style]) // NOLINT
42 ,
43 foreground_color_(foreground_color) {} // NOLINT
44
45 const Charset& charset_; // NOLINT
46 std::optional<Color> foreground_color_;
47
48 void ComputeRequirement() override {
50 requirement_ = children_[0]->requirement();
51 requirement_.min_x += 2;
52 requirement_.min_y += 2;
53 if (children_.size() == 2) {
54 requirement_.min_x =
55 std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
56 }
57 requirement_.focused.box.x_min++;
58 requirement_.focused.box.x_max++;
59 requirement_.focused.box.y_min++;
60 requirement_.focused.box.y_max++;
61 }
62
63 void SetBox(Box box) override {
64 Node::SetBox(box);
65 if (children_.size() == 2) {
66 Box title_box;
67 title_box.x_min = box.x_min + 1;
68 title_box.x_max = std::min(box.x_max - 1,
69 box.x_min + children_[1]->requirement().min_x);
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 Pixel& p1 = screen.PixelAt(x, box_.y_min);
97 Pixel& p2 = screen.PixelAt(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 Pixel& p3 = screen.PixelAt(box_.x_min, y);
105 Pixel& p4 = screen.PixelAt(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 children_[1]->Render(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.PixelAt(x, box_.y_min).foreground_color = *foreground_color_;
121 screen.PixelAt(x, box_.y_max).foreground_color = *foreground_color_;
122 }
123 for (int y = box_.y_min; y <= box_.y_max; ++y) {
124 screen.PixelAt(box_.x_min, y).foreground_color = *foreground_color_;
125 screen.PixelAt(box_.x_max, y).foreground_color = *foreground_color_;
126 }
127 }
128 }
129};
130
131// For reference, here is the charset for normal border:
132class BorderPixel : public Node {
133 public:
134 BorderPixel(Elements children, Pixel pixel)
135 : Node(std::move(children)), pixel_(std::move(pixel)) {}
136
137 private:
138 Pixel pixel_;
139
140 void ComputeRequirement() override {
142 requirement_ = children_[0]->requirement();
143 requirement_.min_x += 2;
144 requirement_.min_y += 2;
145 if (children_.size() == 2) {
146 requirement_.min_x =
147 std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
148 }
149
150 requirement_.focused.box.Shift(1, 1);
151 }
152
153 void SetBox(Box box) override {
154 Node::SetBox(box);
155 if (children_.size() == 2) {
156 Box title_box;
157 title_box.x_min = box.x_min + 1;
158 title_box.x_max = box.x_max - 1;
159 title_box.y_min = box.y_min;
160 title_box.y_max = box.y_min;
161 children_[1]->SetBox(title_box);
162 }
163 box.x_min++;
164 box.x_max--;
165 box.y_min++;
166 box.y_max--;
167 children_[0]->SetBox(box);
168 }
169
170 void Render(Screen& screen) override {
171 // Draw content.
172 children_[0]->Render(screen);
173
174 // Draw the border.
175 if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) {
176 return;
177 }
178
179 screen.PixelAt(box_.x_min, box_.y_min) = pixel_;
180 screen.PixelAt(box_.x_max, box_.y_min) = pixel_;
181 screen.PixelAt(box_.x_min, box_.y_max) = pixel_;
182 screen.PixelAt(box_.x_max, box_.y_max) = pixel_;
183
184 for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
185 screen.PixelAt(x, box_.y_min) = pixel_;
186 screen.PixelAt(x, box_.y_max) = pixel_;
187 }
188 for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
189 screen.PixelAt(box_.x_min, y) = pixel_;
190 screen.PixelAt(box_.x_max, y) = pixel_;
191 }
192 }
193};
194} // namespace
195
196/// @brief Draw a border around the element.
197/// @ingroup dom
198/// @see border
199/// @see borderLight
200/// @see borderDashed
201/// @see borderDouble
202/// @see borderHeavy
203/// @see borderEmpty
204/// @see borderRounded
205/// @see borderStyled
206/// @see borderWith
207///
208/// Add a border around an element
209///
210/// ### Example
211///
212/// ```cpp
213/// // Use 'border' as a function...
214/// Element document = border(text("The element"));
215///
216/// // ...Or as a 'pipe'.
217/// Element document = text("The element") | border;
218/// ```
219///
220/// ### Output
221///
222/// ```bash
223/// ┌───────────┐
224/// │The element│
225/// └───────────┘
226/// ```
228 return std::make_shared<Border>(unpack(std::move(child)), ROUNDED);
229}
230
231/// @brief Same as border but with a constant Pixel around the element.
232/// @ingroup dom
233/// @see border
235 return [pixel](Element child) {
236 return std::make_shared<BorderPixel>(unpack(std::move(child)), pixel);
237 };
238}
239
240/// @brief Same as border but with different styles.
241/// @ingroup dom
242/// @see border
244 return [style](Element child) {
245 return std::make_shared<Border>(unpack(std::move(child)), style);
246 };
247}
248
249/// @brief Same as border but with a foreground color.
250/// @ingroup dom
251/// @see border
252Decorator borderStyled(Color foreground_color) {
253 return [foreground_color](Element child) {
254 return std::make_shared<Border>(unpack(std::move(child)), ROUNDED,
255 foreground_color);
256 };
257}
258
259/// @brief Same as border but with a foreground color and a different style
260/// @ingroup dom
261/// @see border
262Decorator borderStyled(BorderStyle style, Color foreground_color) {
263 return [style, foreground_color](Element child) {
264 return std::make_shared<Border>(unpack(std::move(child)), style,
265 foreground_color);
266 };
267}
268
269/// @brief Draw a dashed border around the element.
270/// @ingroup dom
271/// @see border
272/// @see borderLight
273/// @see borderDashed
274/// @see borderDouble
275/// @see borderHeavy
276/// @see borderRounded
277/// @see borderEmpty
278/// @see borderStyled
279/// @see borderWith
280///
281/// Add a border around an element
282///
283/// ### Example
284///
285/// ```cpp
286/// // Use 'borderDash' as a function...
287/// Element document = borderDash(text("The element"));
288///
289/// // ...Or as a 'pipe'.
290/// Element document = text("The element") | borderDAsh;
291/// ```
292///
293/// ### Output
294///
295/// ```bash
296/// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
297/// ╏The element ╏
298/// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
299/// ```
301 return std::make_shared<Border>(unpack(std::move(child)), DASHED);
302}
303
304/// @brief Draw a light border around the element.
305/// @ingroup dom
306/// @see border
307/// @see borderLight
308/// @see borderDashed
309/// @see borderDouble
310/// @see borderHeavy
311/// @see borderRounded
312/// @see borderEmpty
313/// @see borderStyled
314/// @see borderWith
315///
316/// Add a border around an element
317///
318/// ### Example
319///
320/// ```cpp
321/// // Use 'borderLight' as a function...
322/// Element document = borderLight(text("The element"));
323///
324/// // ...Or as a 'pipe'.
325/// Element document = text("The element") | borderLight;
326/// ```
327///
328/// ### Output
329///
330/// ```bash
331/// ┌──────────────┐
332/// │The element │
333/// └──────────────┘
334/// ```
336 return std::make_shared<Border>(unpack(std::move(child)), LIGHT);
337}
338
339/// @brief Draw a heavy border around the element.
340/// @ingroup dom
341/// @see border
342/// @see borderLight
343/// @see borderDashed
344/// @see borderDouble
345/// @see borderHeavy
346/// @see borderRounded
347/// @see borderEmpty
348/// @see borderStyled
349/// @see borderWith
350///
351/// Add a border around an element
352///
353/// ### Example
354///
355/// ```cpp
356/// // Use 'borderHeavy' as a function...
357/// Element document = borderHeavy(text("The element"));
358///
359/// // ...Or as a 'pipe'.
360/// Element document = text("The element") | borderHeavy;
361/// ```
362///
363/// ### Output
364///
365/// ```bash
366/// ┏━━━━━━━━━━━━━━┓
367/// ┃The element ┃
368/// ┗━━━━━━━━━━━━━━┛
369/// ```
371 return std::make_shared<Border>(unpack(std::move(child)), HEAVY);
372}
373
374/// @brief Draw a double border around the element.
375/// @ingroup dom
376/// @see border
377/// @see borderLight
378/// @see borderDashed
379/// @see borderDouble
380/// @see borderHeavy
381/// @see borderRounded
382/// @see borderEmpty
383/// @see borderStyled
384/// @see borderWith
385///
386/// Add a border around an element
387///
388/// ### Example
389///
390/// ```cpp
391/// // Use 'borderDouble' as a function...
392/// Element document = borderDouble(text("The element"));
393///
394/// // ...Or as a 'pipe'.
395/// Element document = text("The element") | borderDouble;
396/// ```
397///
398/// ### Output
399///
400/// ```bash
401/// ╔══════════════╗
402/// ║The element ║
403/// ╚══════════════╝
404/// ```
406 return std::make_shared<Border>(unpack(std::move(child)), DOUBLE);
407}
408
409/// @brief Draw a rounded border around the element.
410/// @ingroup dom
411/// @see border
412/// @see borderLight
413/// @see borderDashed
414/// @see borderDouble
415/// @see borderHeavy
416/// @see borderRounded
417/// @see borderEmpty
418/// @see borderStyled
419/// @see borderWith
420///
421/// Add a border around an element
422///
423/// ### Example
424///
425/// ```cpp
426/// // Use 'borderRounded' as a function...
427/// Element document = borderRounded(text("The element"));
428///
429/// // ...Or as a 'pipe'.
430/// Element document = text("The element") | borderRounded;
431/// ```
432///
433/// ### Output
434///
435/// ```bash
436/// ╭──────────────╮
437/// │The element │
438/// ╰──────────────╯
439/// ```
441 return std::make_shared<Border>(unpack(std::move(child)), ROUNDED);
442}
443
444/// @brief Draw an empty border around the element.
445/// @ingroup dom
446/// @see border
447/// @see borderLight
448/// @see borderDashed
449/// @see borderDouble
450/// @see borderHeavy
451/// @see borderRounded
452/// @see borderEmpty
453/// @see borderStyled
454/// @see borderWith
455///
456/// Add a border around an element
457///
458/// ### Example
459///
460/// ```cpp
461/// // Use 'borderRounded' as a function...
462/// Element document = borderRounded(text("The element"));
463///
464/// // ...Or as a 'pipe'.
465/// Element document = text("The element") | borderRounded;
466/// ```
467///
468/// ### Output
469///
470/// ```bash
471///
472/// The element
473///
474/// ```
476 return std::make_shared<Border>(unpack(std::move(child)), EMPTY);
477}
478
479/// @brief Draw window with a title and a border around the element.
480/// @param title The title of the window.
481/// @param content The element to be wrapped.
482/// @param border The style of the border. Default is ROUNDED.
483/// @ingroup dom
484/// @see border
485///
486/// ### Example
487///
488/// ```cpp
489/// Element document = window(text("Title"),
490/// text("content")
491/// );
492///
493/// // With specifying border
494/// Element document = window(text("Title"),
495/// text("content"),
496/// ROUNDED
497/// );
498/// ```
499///
500/// ### Output
501///
502/// ```bash
503/// ┌Title──┐
504/// │content│
505/// └───────┘
506/// ```
507Element window(Element title, Element content, BorderStyle border) {
508 return std::make_shared<Border>(unpack(std::move(content), std::move(title)),
509 border);
510}
511} // namespace ftxui
virtual void SetBox(Box box)
Assign a position and a dimension to an element for drawing.
Definition node.cpp:41
virtual void ComputeRequirement()
Compute how much space an element needs.
Definition node.cpp:20
Element window(Element title, Element content, BorderStyle border=ROUNDED)
Draw window with a title and a border around the element.
Element borderDouble(Element)
Draw a double border around the element.
Element borderDashed(Element)
Draw a dashed border around the element.
Element borderRounded(Element)
Draw a rounded border around the element.
Element borderHeavy(Element)
Draw a heavy border around the element.
Element borderLight(Element)
Draw a light border around the element.
Decorator borderWith(const Pixel &)
Same as border but with a constant Pixel around the element.
Decorator borderStyled(BorderStyle)
Same as border but with different styles.
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition node.cpp:84
Element border(Element)
Draw a border around the element.
Element borderEmpty(Element)
Draw an empty border around the element.
BorderStyle
BorderStyle is an enumeration that represents the different styles of borders that can be applied to ...
Definition elements.hpp:35
@ EMPTY
Definition elements.hpp:41
@ DOUBLE
Definition elements.hpp:39
@ HEAVY
Definition elements.hpp:38
@ ROUNDED
Definition elements.hpp:40
@ DASHED
Definition elements.hpp:37
@ LIGHT
Definition elements.hpp:36
Color is a class that represents a color in the terminal user interface.
Definition color.hpp:22
A Unicode character and its associated style.
Definition pixel.hpp:15
The FTXUI ftxui:: namespace.
Definition animation.hpp:10
std::function< Element(Element)> Decorator
Definition elements.hpp:24
std::shared_ptr< Node > Element
Definition elements.hpp:22
std::vector< Element > Elements
Definition elements.hpp:23
const Charset & charset_
std::optional< Color > foreground_color_