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