FTXUI  5.0.0
C++ functional terminal UI.
gauge.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 <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
5 #include <memory> // for allocator, make_shared
6 #include <string> // for string
7 
8 #include "ftxui/dom/elements.hpp" // for Element, gauge, gaugeDirection, gaugeDown, gaugeLeft, gaugeRight, gaugeUp
9 #include "ftxui/dom/node.hpp" // for Node
10 #include "ftxui/dom/requirement.hpp" // for Requirement
11 #include "ftxui/screen/box.hpp" // for Box
12 #include "ftxui/screen/screen.hpp" // for Screen, Pixel
13 
14 namespace ftxui {
15 
16 namespace {
17 // NOLINTNEXTLINE
18 static const std::string charset_horizontal[11] = {
19 #if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
20  // Microsoft's terminals often use fonts not handling the 8 unicode
21  // characters for representing the whole gauge. Fallback with less.
22  " ", " ", " ", " ", "▌", "▌", "▌", "█", "█", "█",
23 #else
24  " ", " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█",
25 #endif
26  // An extra character in case when the fuzzer manage to have:
27  // int(9 * (limit - limit_int) = 9
28  "█"};
29 
30 // NOLINTNEXTLINE
31 static const std::string charset_vertical[10] = {
32  "█",
33  "▇",
34  "▆",
35  "▅",
36  "▄",
37  "▃",
38  "▂",
39  "▁",
40  " ",
41  // An extra character in case when the fuzzer manage to have:
42  // int(8 * (limit - limit_int) = 8
43  " ",
44 };
45 
46 class Gauge : public Node {
47  public:
48  Gauge(float progress, Direction direction)
49  : progress_(progress), direction_(direction) {
50  // This handle NAN correctly:
51  if (!(progress_ > 0.F)) {
52  progress_ = 0.F;
53  }
54  if (!(progress_ < 1.F)) {
55  progress_ = 1.F;
56  }
57  }
58 
59  void ComputeRequirement() override {
60  switch (direction_) {
61  case Direction::Right:
62  case Direction::Left:
63  requirement_.flex_grow_x = 1;
64  requirement_.flex_grow_y = 0;
65  requirement_.flex_shrink_x = 1;
66  requirement_.flex_shrink_y = 0;
67  break;
68  case Direction::Up:
69  case Direction::Down:
70  requirement_.flex_grow_x = 0;
71  requirement_.flex_grow_y = 1;
72  requirement_.flex_shrink_x = 0;
73  requirement_.flex_shrink_y = 1;
74  break;
75  }
76  requirement_.min_x = 1;
77  requirement_.min_y = 1;
78  }
79 
80  void Render(Screen& screen) override {
81  switch (direction_) {
82  case Direction::Right:
83  RenderHorizontal(screen, /*invert=*/false);
84  break;
85  case Direction::Up:
86  RenderVertical(screen, /*invert=*/false);
87  break;
88  case Direction::Left:
89  RenderHorizontal(screen, /*invert=*/true);
90  break;
91  case Direction::Down:
92  RenderVertical(screen, /*invert=*/true);
93  break;
94  }
95  }
96 
97  void RenderHorizontal(Screen& screen, bool invert) {
98  const int y = box_.y_min;
99  if (y > box_.y_max) {
100  return;
101  }
102 
103  // Draw the progress bar horizontally.
104  {
105  const float progress = invert ? 1.F - progress_ : progress_;
106  const auto limit =
107  float(box_.x_min) + progress * float(box_.x_max - box_.x_min + 1);
108  const int limit_int = static_cast<int>(limit);
109  int x = box_.x_min;
110  while (x < limit_int) {
111  screen.at(x++, y) = charset_horizontal[9]; // NOLINT
112  }
113  // NOLINTNEXTLINE
114  screen.at(x++, y) = charset_horizontal[int(9 * (limit - limit_int))];
115  while (x <= box_.x_max) {
116  screen.at(x++, y) = charset_horizontal[0];
117  }
118  }
119 
120  if (invert) {
121  for (int x = box_.x_min; x <= box_.x_max; x++) {
122  screen.PixelAt(x, y).inverted ^= true;
123  }
124  }
125  }
126 
127  void RenderVertical(Screen& screen, bool invert) {
128  const int x = box_.x_min;
129  if (x > box_.x_max) {
130  return;
131  }
132 
133  // Draw the progress bar vertically:
134  {
135  const float progress = invert ? progress_ : 1.F - progress_;
136  const float limit =
137  float(box_.y_min) + progress * float(box_.y_max - box_.y_min + 1);
138  const int limit_int = static_cast<int>(limit);
139  int y = box_.y_min;
140  while (y < limit_int) {
141  screen.at(x, y++) = charset_vertical[8]; // NOLINT
142  }
143  // NOLINTNEXTLINE
144  screen.at(x, y++) = charset_vertical[int(8 * (limit - limit_int))];
145  while (y <= box_.y_max) {
146  screen.at(x, y++) = charset_vertical[0];
147  }
148  }
149 
150  if (invert) {
151  for (int y = box_.y_min; y <= box_.y_max; y++) {
152  screen.PixelAt(x, y).inverted ^= true;
153  }
154  }
155  }
156 
157  private:
158  float progress_;
159  Direction direction_;
160 };
161 
162 } // namespace
163 
164 /// @brief Draw a high definition progress bar progressing in specified
165 /// direction.
166 /// @param progress The proportion of the area to be filled. Belong to [0,1].
167 // @param direction Direction of progress bars progression.
168 /// @ingroup dom
169 Element gaugeDirection(float progress, Direction direction) {
170  return std::make_shared<Gauge>(progress, direction);
171 }
172 
173 /// @brief Draw a high definition progress bar progressing from left to right.
174 /// @param progress The proportion of the area to be filled. Belong to [0,1].
175 /// @ingroup dom
176 ///
177 /// ### Example
178 ///
179 /// A gauge. It can be used to represent a progress bar.
180 /// ~~~cpp
181 /// border(gaugeRight(0.5))
182 /// ~~~
183 ///
184 /// #### Output
185 ///
186 /// ~~~bash
187 /// ┌──────────────────────────────────────────────────────────────────────────┐
188 /// │█████████████████████████████████████ │
189 /// └──────────────────────────────────────────────────────────────────────────┘
190 /// ~~~
191 Element gaugeRight(float progress) {
192  return gaugeDirection(progress, Direction::Right);
193 }
194 
195 /// @brief Draw a high definition progress bar progressing from right to left.
196 /// @param progress The proportion of the area to be filled. Belong to [0,1].
197 /// @ingroup dom
198 ///
199 /// ### Example
200 ///
201 /// A gauge. It can be used to represent a progress bar.
202 /// ~~~cpp
203 /// border(gaugeLeft(0.5))
204 /// ~~~
205 ///
206 /// #### Output
207 ///
208 /// ~~~bash
209 /// ┌──────────────────────────────────────────────────────────────────────────┐
210 /// │ █████████████████████████████████████│
211 /// └──────────────────────────────────────────────────────────────────────────┘
212 /// ~~~
213 Element gaugeLeft(float progress) {
214  return gaugeDirection(progress, Direction::Left);
215 }
216 
217 /// @brief Draw a high definition progress bar progressing from bottom to top.
218 /// @param progress The proportion of the area to be filled. Belong to [0,1].
219 /// @ingroup dom
220 ///
221 /// ### Example
222 ///
223 /// A gauge. It can be used to represent a progress bar.
224 /// ~~~cpp
225 /// border(gaugeUp(0.5))
226 /// ~~~
227 ///
228 /// #### Output
229 ///
230 /// ~~~bash
231 /// ┌─┐
232 /// │ │
233 /// │ │
234 /// │ │
235 /// │ │
236 /// │█│
237 /// │█│
238 /// │█│
239 /// │█│
240 /// └─┘
241 /// ~~~
242 Element gaugeUp(float progress) {
243  return gaugeDirection(progress, Direction::Up);
244 }
245 
246 /// @brief Draw a high definition progress bar progressing from top to bottom.
247 /// @param progress The proportion of the area to be filled. Belong to [0,1].
248 /// @ingroup dom
249 ///
250 /// ### Example
251 ///
252 /// A gauge. It can be used to represent a progress bar.
253 /// ~~~cpp
254 /// border(gaugeDown(0.5))
255 /// ~~~
256 ///
257 /// #### Output
258 ///
259 /// ~~~bash
260 /// ┌─┐
261 /// │█│
262 /// │█│
263 /// │█│
264 /// │█│
265 /// │ │
266 /// │ │
267 /// │ │
268 /// │ │
269 /// └─┘
270 /// ~~~
271 Element gaugeDown(float progress) {
272  return gaugeDirection(progress, Direction::Down);
273 }
274 
275 /// @brief Draw a high definition progress bar.
276 /// @param progress The proportion of the area to be filled. Belong to [0,1].
277 /// @ingroup dom
278 ///
279 /// ### Example
280 ///
281 /// A gauge. It can be used to represent a progress bar.
282 /// ~~~cpp
283 /// border(gauge(0.5))
284 /// ~~~
285 ///
286 /// #### Output
287 ///
288 /// ~~~bash
289 /// ┌──────────────────────────────────────────────────────────────────────────┐
290 /// │█████████████████████████████████████ │
291 /// └──────────────────────────────────────────────────────────────────────────┘
292 /// ~~~
293 Element gauge(float progress) {
294  return gaugeRight(progress);
295 }
296 
297 } // namespace ftxui
Element gaugeDirection(float progress, Direction direction)
Draw a high definition progress bar progressing in specified direction.
Definition: gauge.cpp:169
Direction
Definition: direction.hpp:8
std::shared_ptr< Node > Element
Definition: elements.hpp:22
Element gaugeRight(float progress)
Draw a high definition progress bar progressing from left to right.
Definition: gauge.cpp:191
Element gaugeUp(float progress)
Draw a high definition progress bar progressing from bottom to top.
Definition: gauge.cpp:242
Element gaugeLeft(float progress)
Draw a high definition progress bar progressing from right to left.
Definition: gauge.cpp:213
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition: node.cpp:47
Element gauge(float progress)
Draw a high definition progress bar.
Definition: gauge.cpp:293
Element gaugeDown(float progress)
Draw a high definition progress bar progressing from top to bottom.
Definition: gauge.cpp:271