FTXUI  5.0.0
C++ functional terminal UI.
separator.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 <array> // for array, array<>::value_type
5 #include <memory> // for make_shared, allocator
6 #include <string> // for basic_string, string
7 #include <utility> // for move
8 
9 #include "ftxui/dom/elements.hpp" // for Element, BorderStyle, LIGHT, separator, DOUBLE, EMPTY, HEAVY, separatorCharacter, separatorDouble, separatorEmpty, separatorHSelector, separatorHeavy, separatorLight, separatorStyled, separatorVSelector
10 #include "ftxui/dom/node.hpp" // for Node
11 #include "ftxui/dom/requirement.hpp" // for Requirement
12 #include "ftxui/screen/box.hpp" // for Box
13 #include "ftxui/screen/color.hpp" // for Color
14 #include "ftxui/screen/pixel.hpp" // for Pixel
15 #include "ftxui/screen/screen.hpp" // for Pixel, Screen
16 
17 namespace ftxui {
18 
19 namespace {
20 using Charset = std::array<std::string, 2>; // NOLINT
21 using Charsets = std::array<Charset, 6>; // NOLINT
22 // NOLINTNEXTLINE
23 const Charsets charsets = {
24  Charset{"│", "─"}, // LIGHT
25  Charset{"╏", "╍"}, // DASHED
26  Charset{"┃", "━"}, // HEAVY
27  Charset{"║", "═"}, // DOUBLE
28  Charset{"│", "─"}, // ROUNDED
29  Charset{" ", " "}, // EMPTY
30 };
31 
32 class Separator : public Node {
33  public:
34  explicit Separator(std::string value) : value_(std::move(value)) {}
35 
36  void ComputeRequirement() override {
37  requirement_.min_x = 1;
38  requirement_.min_y = 1;
39  }
40 
41  void Render(Screen& screen) override {
42  for (int y = box_.y_min; y <= box_.y_max; ++y) {
43  for (int x = box_.x_min; x <= box_.x_max; ++x) {
44  Pixel& pixel = screen.PixelAt(x, y);
45  pixel.character = value_;
46  pixel.automerge = true;
47  }
48  }
49  }
50 
51  std::string value_;
52 };
53 
54 class SeparatorAuto : public Node {
55  public:
56  explicit SeparatorAuto(BorderStyle style) : style_(style) {}
57 
58  void ComputeRequirement() override {
59  requirement_.min_x = 1;
60  requirement_.min_y = 1;
61  }
62 
63  void Render(Screen& screen) override {
64  const bool is_column = (box_.x_max == box_.x_min);
65  const bool is_line = (box_.y_min == box_.y_max);
66 
67  const std::string c =
68  charsets[style_][int(is_line && !is_column)]; // NOLINT
69 
70  for (int y = box_.y_min; y <= box_.y_max; ++y) {
71  for (int x = box_.x_min; x <= box_.x_max; ++x) {
72  Pixel& pixel = screen.PixelAt(x, y);
73  pixel.character = c;
74  pixel.automerge = true;
75  }
76  }
77  }
78 
79  BorderStyle style_;
80 };
81 
82 class SeparatorWithPixel : public SeparatorAuto {
83  public:
84  explicit SeparatorWithPixel(Pixel pixel)
85  : SeparatorAuto(LIGHT), pixel_(std::move(pixel)) {
86  pixel_.automerge = true;
87  }
88  void Render(Screen& screen) override {
89  for (int y = box_.y_min; y <= box_.y_max; ++y) {
90  for (int x = box_.x_min; x <= box_.x_max; ++x) {
91  screen.PixelAt(x, y) = pixel_;
92  }
93  }
94  }
95 
96  private:
97  Pixel pixel_;
98 };
99 } // namespace
100 
101 /// @brief Draw a vertical or horizontal separation in between two other
102 /// elements.
103 /// @ingroup dom
104 /// @see separator
105 /// @see separatorLight
106 /// @see separatorDashed
107 /// @see separatorDouble
108 /// @see separatorHeavy
109 /// @see separatorEmpty
110 /// @see separatorRounded
111 /// @see separatorStyled
112 /// @see separatorCharacter
113 ///
114 /// Add a visual separation in between two elements.
115 ///
116 /// ### Example
117 ///
118 /// ```cpp
119 /// // Use 'border' as a function...
120 /// Element document = vbox({
121 /// text("up"),
122 /// separator(),
123 /// text("down"),
124 /// });
125 /// ```
126 ///
127 /// ### Output
128 ///
129 /// ```bash
130 /// up
131 /// ────
132 /// down
133 /// ```
135  return std::make_shared<SeparatorAuto>(LIGHT);
136 }
137 
138 /// @brief Draw a vertical or horizontal separation in between two other
139 /// elements.
140 /// @param style the style of the separator.
141 /// @ingroup dom
142 /// @see separator
143 /// @see separatorLight
144 /// @see separatorDashed
145 /// @see separatorDouble
146 /// @see separatorHeavy
147 /// @see separatorEmpty
148 /// @see separatorRounded
149 /// @see separatorStyled
150 /// @see separatorCharacter
151 ///
152 /// Add a visual separation in between two elements.
153 ///
154 /// ### Example
155 ///
156 /// ```cpp
157 /// // Use 'border' as a function...
158 /// Element document = vbox({
159 /// text("up"),
160 /// separatorStyled(DOUBLE),
161 /// text("down"),
162 /// });
163 /// ```
164 ///
165 /// ### Output
166 ///
167 /// ```bash
168 /// up
169 /// ════
170 /// down
171 /// ```
173  return std::make_shared<SeparatorAuto>(style);
174 }
175 
176 /// @brief Draw a vertical or horizontal separation in between two other
177 /// elements, using the LIGHT style.
178 /// @ingroup dom
179 /// @see separator
180 /// @see separatorLight
181 /// @see separatorDashed
182 /// @see separatorDouble
183 /// @see separatorHeavy
184 /// @see separatorEmpty
185 /// @see separatorRounded
186 /// @see separatorStyled
187 /// @see separatorCharacter
188 ///
189 /// Add a visual separation in between two elements.
190 ///
191 /// ### Example
192 ///
193 /// ```cpp
194 /// // Use 'border' as a function...
195 /// Element document = vbox({
196 /// text("up"),
197 /// separatorLight(),
198 /// text("down"),
199 /// });
200 /// ```
201 ///
202 /// ### Output
203 ///
204 /// ```bash
205 /// up
206 /// ────
207 /// down
208 /// ```
210  return std::make_shared<SeparatorAuto>(LIGHT);
211 }
212 
213 /// @brief Draw a vertical or horizontal separation in between two other
214 /// elements, using the DASHED style.
215 /// @ingroup dom
216 /// @see separator
217 /// @see separatorLight
218 /// @see separatorDashed
219 /// @see separatorDouble
220 /// @see separatorHeavy
221 /// @see separatorEmpty
222 /// @see separatorRounded
223 /// @see separatorStyled
224 /// @see separatorCharacter
225 ///
226 /// Add a visual separation in between two elements.
227 ///
228 /// ### Example
229 ///
230 /// ```cpp
231 /// // Use 'border' as a function...
232 /// Element document = vbox({
233 /// text("up"),
234 /// separatorLight(),
235 /// text("down"),
236 /// });
237 /// ```
238 ///
239 /// ### Output
240 ///
241 /// ```bash
242 /// up
243 /// ╍╍╍╍
244 /// down
245 /// ```
247  return std::make_shared<SeparatorAuto>(DASHED);
248 }
249 
250 /// @brief Draw a vertical or horizontal separation in between two other
251 /// elements, using the HEAVY style.
252 /// @ingroup dom
253 /// @see separator
254 /// @see separatorLight
255 /// @see separatorDashed
256 /// @see separatorDouble
257 /// @see separatorHeavy
258 /// @see separatorEmpty
259 /// @see separatorRounded
260 /// @see separatorStyled
261 /// @see separatorCharacter
262 ///
263 /// Add a visual separation in between two elements.
264 ///
265 /// ### Example
266 ///
267 /// ```cpp
268 /// // Use 'border' as a function...
269 /// Element document = vbox({
270 /// text("up"),
271 /// separatorHeavy(),
272 /// text("down"),
273 /// });
274 /// ```
275 ///
276 /// ### Output
277 ///
278 /// ```bash
279 /// up
280 /// ━━━━
281 /// down
282 /// ```
284  return std::make_shared<SeparatorAuto>(HEAVY);
285 }
286 
287 /// @brief Draw a vertical or horizontal separation in between two other
288 /// elements, using the DOUBLE style.
289 /// @ingroup dom
290 /// @see separator
291 /// @see separatorLight
292 /// @see separatorDashed
293 /// @see separatorDouble
294 /// @see separatorHeavy
295 /// @see separatorEmpty
296 /// @see separatorRounded
297 /// @see separatorStyled
298 /// @see separatorCharacter
299 ///
300 /// Add a visual separation in between two elements.
301 ///
302 /// ### Example
303 ///
304 /// ```cpp
305 /// // Use 'border' as a function...
306 /// Element document = vbox({
307 /// text("up"),
308 /// separatorDouble(),
309 /// text("down"),
310 /// });
311 /// ```
312 ///
313 /// ### Output
314 ///
315 /// ```bash
316 /// up
317 /// ════
318 /// down
319 /// ```
321  return std::make_shared<SeparatorAuto>(DOUBLE);
322 }
323 
324 /// @brief Draw a vertical or horizontal separation in between two other
325 /// elements, using the EMPTY style.
326 /// @ingroup dom
327 /// @see separator
328 /// @see separatorLight
329 /// @see separatorDashed
330 /// @see separatorDouble
331 /// @see separatorHeavy
332 /// @see separatorEmpty
333 /// @see separatorRounded
334 /// @see separatorStyled
335 /// @see separatorCharacter
336 ///
337 /// Add a visual separation in between two elements.
338 ///
339 /// ### Example
340 ///
341 /// ```cpp
342 /// // Use 'border' as a function...
343 /// Element document = vbox({
344 /// text("up"),
345 /// separator(),
346 /// text("down"),
347 /// });
348 /// ```
349 ///
350 /// ### Output
351 ///
352 /// ```bash
353 /// up
354 ///
355 /// down
356 /// ```
358  return std::make_shared<SeparatorAuto>(EMPTY);
359 }
360 
361 /// @brief Draw a vertical or horizontal separation in between two other
362 /// elements.
363 /// @param value the character to fill the separator area.
364 /// @ingroup dom
365 /// @see separator
366 /// @see separatorLight
367 /// @see separatorDashed
368 /// @see separatorDouble
369 /// @see separatorHeavy
370 /// @see separatorEmpty
371 /// @see separatorRounded
372 /// @see separatorStyled
373 /// @see separatorCharacter
374 ///
375 /// Add a visual separation in between two elements.
376 ///
377 /// ### Example
378 ///
379 /// ```cpp
380 /// // Use 'border' as a function...
381 /// Element document = vbox({
382 /// text("up"),
383 /// separator(),
384 /// text("down"),
385 /// });
386 /// ```
387 ///
388 /// ### Output
389 ///
390 /// ```bash
391 /// up
392 /// ────
393 /// down
394 /// ```
395 Element separatorCharacter(std::string value) {
396  return std::make_shared<Separator>(std::move(value));
397 }
398 
399 /// @brief Draw a separator in between two element filled with a given pixel.
400 /// @ingroup dom
401 /// @see separator
402 /// @see separatorLight
403 /// @see separatorDashed
404 /// @see separatorHeavy
405 /// @see separatorDouble
406 /// @see separatorStyled
407 ///
408 /// ### Example
409 ///
410 /// ```cpp
411 /// Pixel empty;
412 /// Element document = vbox({
413 /// text("Up"),
414 /// separator(empty),
415 /// text("Down"),
416 /// })
417 /// ```
418 ///
419 /// ### Output
420 ///
421 /// ```bash
422 /// Up
423 ///
424 /// Down
425 /// ```
427  return std::make_shared<SeparatorWithPixel>(std::move(pixel));
428 }
429 
430 /// @brief Draw an horizontal bar, with the area in between left/right colored
431 /// differently.
432 /// @param left the left limit of the active area.
433 /// @param right the right limit of the active area.
434 /// @param selected_color the color of the selected area.
435 /// @param unselected_color the color of the unselected area.
436 ///
437 /// ### Example
438 ///
439 /// ```cpp
440 /// Element document = separatorHSelector(2,5, Color::White, Color::Blue);
441 /// ```
443  float right,
444  Color unselected_color,
445  Color selected_color) {
446  class Impl : public Node {
447  public:
448  Impl(float left, float right, Color selected_color, Color unselected_color)
449  : left_(left),
450  right_(right),
451  unselected_color_(unselected_color),
452  selected_color_(selected_color) {}
453  void ComputeRequirement() override {
454  requirement_.min_x = 1;
455  requirement_.min_y = 1;
456  }
457 
458  void Render(Screen& screen) override {
459  if (box_.y_max < box_.y_min) {
460  return;
461  }
462 
463  // This are the two location with an empty demi-cell.
464  int demi_cell_left = int(left_ * 2.F - 1.F); // NOLINT
465  int demi_cell_right = int(right_ * 2.F + 2.F); // NOLINT
466 
467  const int y = box_.y_min;
468  for (int x = box_.x_min; x <= box_.x_max; ++x) {
469  Pixel& pixel = screen.PixelAt(x, y);
470 
471  const int a = (x - box_.x_min) * 2;
472  const int b = a + 1;
473  const bool a_empty = demi_cell_left == a || demi_cell_right == a;
474  const bool b_empty = demi_cell_left == b || demi_cell_right == b;
475 
476  if (!a_empty && !b_empty) {
477  pixel.character = "─";
478  pixel.automerge = true;
479  } else {
480  pixel.character = a_empty ? "╶" : "╴"; // NOLINT
481  pixel.automerge = false;
482  }
483 
484  if (demi_cell_left <= a && b <= demi_cell_right) {
485  pixel.foreground_color = selected_color_;
486  } else {
487  pixel.foreground_color = unselected_color_;
488  }
489  }
490  }
491 
492  float left_;
493  float right_;
494  Color unselected_color_;
495  Color selected_color_;
496  };
497  return std::make_shared<Impl>(left, right, unselected_color, selected_color);
498 }
499 
500 /// @brief Draw an vertical bar, with the area in between up/downcolored
501 /// differently.
502 /// @param up the left limit of the active area.
503 /// @param down the right limit of the active area.
504 /// @param selected_color the color of the selected area.
505 /// @param unselected_color the color of the unselected area.
506 ///
507 /// ### Example
508 ///
509 /// ```cpp
510 /// Element document = separatorHSelector(2,5, Color::White, Color::Blue);
511 /// ```
513  float down,
514  Color unselected_color,
515  Color selected_color) {
516  class Impl : public Node {
517  public:
518  Impl(float up, float down, Color unselected_color, Color selected_color)
519  : up_(up),
520  down_(down),
521  unselected_color_(unselected_color),
522  selected_color_(selected_color) {}
523  void ComputeRequirement() override {
524  requirement_.min_x = 1;
525  requirement_.min_y = 1;
526  }
527 
528  void Render(Screen& screen) override {
529  if (box_.x_max < box_.x_min) {
530  return;
531  }
532 
533  // This are the two location with an empty demi-cell.
534  const int demi_cell_up = int(up_ * 2 - 1);
535  const int demi_cell_down = int(down_ * 2 + 2);
536 
537  const int x = box_.x_min;
538  for (int y = box_.y_min; y <= box_.y_max; ++y) {
539  Pixel& pixel = screen.PixelAt(x, y);
540 
541  const int a = (y - box_.y_min) * 2;
542  const int b = a + 1;
543  const bool a_empty = demi_cell_up == a || demi_cell_down == a;
544  const bool b_empty = demi_cell_up == b || demi_cell_down == b;
545 
546  if (!a_empty && !b_empty) {
547  pixel.character = "│";
548  pixel.automerge = true;
549  } else {
550  pixel.character = a_empty ? "╷" : "╵"; // NOLINT
551  pixel.automerge = false;
552  }
553 
554  if (demi_cell_up <= a && b <= demi_cell_down) {
555  pixel.foreground_color = selected_color_;
556  } else {
557  pixel.foreground_color = unselected_color_;
558  }
559  }
560  }
561 
562  float up_;
563  float down_;
564  Color unselected_color_;
565  Color selected_color_;
566  };
567  return std::make_shared<Impl>(up, down, unselected_color, selected_color);
568 }
569 
570 } // namespace ftxui
A class representing terminal colors.
Definition: color.hpp:20
Pixel & PixelAt(int x, int y)
Access a cell (Pixel) at a given position.
Definition: image.cpp:43
A rectangular grid of Pixel.
Definition: screen.hpp:25
Element separatorStyled(BorderStyle)
Draw a vertical or horizontal separation in between two other elements.
Definition: separator.cpp:172
Element separatorEmpty()
Draw a vertical or horizontal separation in between two other elements, using the EMPTY style.
Definition: separator.cpp:357
Element separatorVSelector(float up, float down, Color unselected_color, Color selected_color)
Draw an vertical bar, with the area in between up/downcolored differently.
Definition: separator.cpp:512
std::shared_ptr< Node > Element
Definition: elements.hpp:22
Element separatorLight()
Draw a vertical or horizontal separation in between two other elements, using the LIGHT style.
Definition: separator.cpp:209
Element separatorHSelector(float left, float right, Color unselected_color, Color selected_color)
Draw an horizontal bar, with the area in between left/right colored differently.
Definition: separator.cpp:442
Element separatorDashed()
Draw a vertical or horizontal separation in between two other elements, using the DASHED style.
Definition: separator.cpp:246
Element separatorCharacter(std::string)
Draw a vertical or horizontal separation in between two other elements.
Definition: separator.cpp:395
Element separator()
Draw a vertical or horizontal separation in between two other elements.
Definition: separator.cpp:134
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition: node.cpp:47
Element separatorDouble()
Draw a vertical or horizontal separation in between two other elements, using the DOUBLE style.
Definition: separator.cpp:320
Element separatorHeavy()
Draw a vertical or horizontal separation in between two other elements, using the HEAVY style.
Definition: separator.cpp:283
BorderStyle
Definition: elements.hpp:27
@ EMPTY
Definition: elements.hpp:33
@ DOUBLE
Definition: elements.hpp:31
@ HEAVY
Definition: elements.hpp:30
@ DASHED
Definition: elements.hpp:29
@ LIGHT
Definition: elements.hpp:28
A Unicode character and its associated style.
Definition: pixel.hpp:15
Color foreground_color
Definition: pixel.hpp:47
std::string character
Definition: pixel.hpp:43
bool automerge
Definition: pixel.hpp:34