FTXUI  5.0.0
C++ functional terminal UI.
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 __alloc_traits<>::value_type
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/screen.hpp" // for Pixel, Screen
18 
19 namespace ftxui {
20 
21 namespace {
22 using Charset = std::array<std::string, 6>; // NOLINT
23 using Charsets = std::array<Charset, 6>; // NOLINT
24 // NOLINTNEXTLINE
25 static 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:
35 class 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]),
42  foreground_color_(foreground_color) {} // NOLINT
43 
44  const Charset& charset_; // NOLINT
45  std::optional<Color> foreground_color_;
46 
47  void ComputeRequirement() override {
49  requirement_ = children_[0]->requirement();
50  requirement_.min_x += 2;
51  requirement_.min_y += 2;
52  if (children_.size() == 2) {
53  requirement_.min_x =
54  std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
55  }
56  requirement_.selected_box.x_min++;
57  requirement_.selected_box.x_max++;
58  requirement_.selected_box.y_min++;
59  requirement_.selected_box.y_max++;
60  }
61 
62  void SetBox(Box box) override {
63  Node::SetBox(box);
64  if (children_.size() == 2) {
65  Box title_box;
66  title_box.x_min = box.x_min + 1;
67  title_box.x_max = box.x_max - 1;
68  title_box.y_min = box.y_min;
69  title_box.y_max = box.y_min;
70  children_[1]->SetBox(title_box);
71  }
72  box.x_min++;
73  box.x_max--;
74  box.y_min++;
75  box.y_max--;
76  children_[0]->SetBox(box);
77  }
78 
79  void Render(Screen& screen) override {
80  // Draw content.
81  children_[0]->Render(screen);
82 
83  // Draw the border.
84  if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) {
85  return;
86  }
87 
88  screen.at(box_.x_min, box_.y_min) = charset_[0]; // NOLINT
89  screen.at(box_.x_max, box_.y_min) = charset_[1]; // NOLINT
90  screen.at(box_.x_min, box_.y_max) = charset_[2]; // NOLINT
91  screen.at(box_.x_max, box_.y_max) = charset_[3]; // NOLINT
92 
93  for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
94  Pixel& p1 = screen.PixelAt(x, box_.y_min);
95  Pixel& p2 = screen.PixelAt(x, box_.y_max);
96  p1.character = charset_[4]; // NOLINT
97  p2.character = charset_[4]; // NOLINT
98  p1.automerge = true;
99  p2.automerge = true;
100  }
101  for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
102  Pixel& p3 = screen.PixelAt(box_.x_min, y);
103  Pixel& p4 = screen.PixelAt(box_.x_max, y);
104  p3.character = charset_[5]; // NOLINT
105  p4.character = charset_[5]; // NOLINT
106  p3.automerge = true;
107  p4.automerge = true;
108  }
109 
110  // Draw title.
111  if (children_.size() == 2) {
112  children_[1]->Render(screen);
113  }
114 
115  // Draw the border color.
116  if (foreground_color_) {
117  for (int x = box_.x_min; x <= box_.x_max; ++x) {
118  screen.PixelAt(x, box_.y_min).foreground_color = *foreground_color_;
119  screen.PixelAt(x, box_.y_max).foreground_color = *foreground_color_;
120  }
121  for (int y = box_.y_min; y <= box_.y_max; ++y) {
122  screen.PixelAt(box_.x_min, y).foreground_color = *foreground_color_;
123  screen.PixelAt(box_.x_max, y).foreground_color = *foreground_color_;
124  }
125  }
126  }
127 };
128 
129 // For reference, here is the charset for normal border:
130 class BorderPixel : public Node {
131  public:
132  BorderPixel(Elements children, Pixel pixel)
133  : Node(std::move(children)), pixel_(std::move(pixel)) {}
134 
135  private:
136  Pixel pixel_;
137 
138  void ComputeRequirement() override {
140  requirement_ = children_[0]->requirement();
141  requirement_.min_x += 2;
142  requirement_.min_y += 2;
143  if (children_.size() == 2) {
144  requirement_.min_x =
145  std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
146  }
147  requirement_.selected_box.x_min++;
148  requirement_.selected_box.x_max++;
149  requirement_.selected_box.y_min++;
150  requirement_.selected_box.y_max++;
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
234 Decorator borderWith(const Pixel& pixel) {
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
252 Decorator 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
262 Decorator 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 light 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 dashed 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 /// @ingroup dom
483 /// @see border
484 ///
485 /// ### Example
486 ///
487 /// ```cpp
488 /// Element document = window(text("Title"),
489 /// text("content")
490 /// );
491 /// ```
492 ///
493 /// ### Output
494 ///
495 /// ```bash
496 /// ┌Title──┐
497 /// │content│
498 /// └───────┘
499 /// ```
500 Element window(Element title, Element content) {
501  return std::make_shared<Border>(unpack(std::move(content), std::move(title)),
502  ROUNDED);
503 }
504 } // namespace ftxui
A class representing terminal colors.
Definition: color.hpp:21
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 borderDouble(Element)
Draw a double border around the element.
Definition: border.cpp:405
std::function< Element(Element)> Decorator
Definition: elements.hpp:25
Element borderDashed(Element)
Draw a light border around the element.
Definition: border.cpp:300
std::shared_ptr< Node > Element
Definition: elements.hpp:23
Element borderRounded(Element)
Draw a rounded border around the element.
Definition: border.cpp:440
Element window(Element title, Element content)
Draw window with a title and a border around the element.
Definition: border.cpp:500
Element borderHeavy(Element)
Draw a heavy border around the element.
Definition: border.cpp:370
std::vector< Element > Elements
Definition: elements.hpp:24
Element borderLight(Element)
Draw a dashed border around the element.
Definition: border.cpp:335
Decorator borderWith(const Pixel &)
Same as border but with a constant Pixel around the element.
Definition: border.cpp:234
Decorator borderStyled(BorderStyle)
Same as border but with different styles.
Definition: border.cpp:243
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:227
Element borderEmpty(Element)
Draw an empty border around the element.
Definition: border.cpp:475
BorderStyle
Definition: elements.hpp:28
@ EMPTY
Definition: elements.hpp:34
@ DOUBLE
Definition: elements.hpp:32
@ HEAVY
Definition: elements.hpp:31
@ ROUNDED
Definition: elements.hpp:33
@ DASHED
Definition: elements.hpp:30
@ LIGHT
Definition: elements.hpp:29
A unicode character and its associated style.
Definition: screen.hpp:20