FTXUI  5.0.0
C++ functional terminal UI.
slider.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, min
5#include <cstdint> // for uint8_t, uint16_t, uint32_t, uint64_t
6#include <ftxui/component/component_options.hpp> // for SliderOption
7#include <ftxui/dom/direction.hpp> // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
8#include <string> // for allocator
9#include <utility> // for move
10
11#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
12#include "ftxui/component/component.hpp" // for Make, Slider
13#include "ftxui/component/component_base.hpp" // for ComponentBase
14#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
15#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
16#include "ftxui/component/screen_interactive.hpp" // for Component
17#include "ftxui/dom/elements.hpp" // for operator|, text, Element, xflex, hbox, color, underlined, reflect, Decorator, dim, vcenter, focus, nothing, select, yflex, gaugeDirection
18#include "ftxui/screen/box.hpp" // for Box
19#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
20#include "ftxui/screen/util.hpp" // for clamp
21#include "ftxui/util/ref.hpp" // for ConstRef, Ref, ConstStringRef
22
23namespace ftxui {
24
25namespace {
26Decorator flexDirection(Direction direction) {
27 switch (direction) {
28 case Direction::Up:
29 case Direction::Down:
30 return yflex;
31 case Direction::Left:
33 return xflex;
34 }
35 return xflex; // NOT_REACHED()
36}
37
38template <class T>
39class SliderBase : public ComponentBase {
40 public:
41 explicit SliderBase(SliderOption<T> options)
42 : value_(options.value),
43 min_(options.min),
44 max_(options.max),
45 increment_(options.increment),
46 options_(options) {}
47
48 Element Render() override {
49 auto gauge_color = Focused() ? color(options_.color_active)
50 : color(options_.color_inactive);
51 const float percent = float(value_() - min_()) / float(max_() - min_());
52 return gaugeDirection(percent, options_.direction) |
53 flexDirection(options_.direction) | reflect(gauge_box_) |
54 gauge_color;
55 }
56
57 void OnLeft() {
58 switch (options_.direction) {
60 value_() -= increment_();
61 break;
62 case Direction::Left:
63 value_() += increment_();
64 break;
65 case Direction::Up:
66 case Direction::Down:
67 break;
68 }
69 }
70
71 void OnRight() {
72 switch (options_.direction) {
74 value_() += increment_();
75 break;
76 case Direction::Left:
77 value_() -= increment_();
78 break;
79 case Direction::Up:
80 case Direction::Down:
81 break;
82 }
83 }
84
85 void OnUp() {
86 switch (options_.direction) {
87 case Direction::Up:
88 value_() -= increment_();
89 break;
90 case Direction::Down:
91 value_() += increment_();
92 break;
93 case Direction::Left:
95 break;
96 }
97 }
98
99 void OnDown() {
100 switch (options_.direction) {
101 case Direction::Down:
102 value_() -= increment_();
103 break;
104 case Direction::Up:
105 value_() += increment_();
106 break;
107 case Direction::Left:
108 case Direction::Right:
109 break;
110 }
111 }
112
113 bool OnEvent(Event event) final {
114 if (event.is_mouse()) {
115 return OnMouseEvent(event);
116 }
117
118 T old_value = value_();
119 if (event == Event::ArrowLeft || event == Event::Character('h')) {
120 OnLeft();
121 }
122 if (event == Event::ArrowRight || event == Event::Character('l')) {
123 OnRight();
124 }
125 if (event == Event::ArrowUp || event == Event::Character('k')) {
126 OnDown();
127 }
128 if (event == Event::ArrowDown || event == Event::Character('j')) {
129 OnUp();
130 }
131
132 value_() = util::clamp(value_(), min_(), max_());
133 if (old_value != value_()) {
134 return true;
135 }
136
137 return ComponentBase::OnEvent(event);
138 }
139
140 bool OnMouseEvent(Event event) {
141 if (captured_mouse_) {
142 if (event.mouse().motion == Mouse::Released) {
143 captured_mouse_ = nullptr;
144 return true;
145 }
146
147 switch (options_.direction) {
148 case Direction::Right: {
149 value_() = min_() + (event.mouse().x - gauge_box_.x_min) *
150 (max_() - min_()) /
151 (gauge_box_.x_max - gauge_box_.x_min);
152 break;
153 }
154 case Direction::Left: {
155 value_() = max_() - (event.mouse().x - gauge_box_.x_min) *
156 (max_() - min_()) /
157 (gauge_box_.x_max - gauge_box_.x_min);
158 break;
159 }
160 case Direction::Down: {
161 value_() = min_() + (event.mouse().y - gauge_box_.y_min) *
162 (max_() - min_()) /
163 (gauge_box_.y_max - gauge_box_.y_min);
164 break;
165 }
166 case Direction::Up: {
167 value_() = max_() - (event.mouse().y - gauge_box_.y_min) *
168 (max_() - min_()) /
169 (gauge_box_.y_max - gauge_box_.y_min);
170 break;
171 }
172 }
173 value_() = std::max(min_(), std::min(max_(), value_()));
174 return true;
175 }
176
177 if (!event.mouse().IsPressed()) {
178 return false;
179 }
180
181 if (!gauge_box_.Contain(event.mouse().x, event.mouse().y)) {
182 return false;
183 }
184
185 captured_mouse_ = CaptureMouse(event);
186
187 if (captured_mouse_) {
188 TakeFocus();
189 return true;
190 }
191
192 return false;
193 }
194
195 bool Focusable() const final { return true; }
196
197 private:
198 Ref<T> value_;
199 ConstRef<T> min_;
200 ConstRef<T> max_;
201 ConstRef<T> increment_;
202 SliderOption<T> options_;
203 Box gauge_box_;
204 CapturedMouse captured_mouse_;
205};
206
207class SliderWithLabel : public ComponentBase {
208 public:
209 SliderWithLabel(ConstStringRef label, Component inner)
210 : label_(std::move(label)) {
211 Add(std::move(inner));
212 SetActiveChild(ChildAt(0));
213 }
214
215 private:
216 bool OnEvent(Event event) final {
217 if (ComponentBase::OnEvent(event)) {
218 return true;
219 }
220
221 if (!event.is_mouse()) {
222 return false;
223 }
224
225 mouse_hover_ = box_.Contain(event.mouse().x, event.mouse().y);
226
227 if (!mouse_hover_) {
228 return false;
229 }
230
231 if (!CaptureMouse(event)) {
232 return false;
233 }
234
235 return true;
236 }
237
238 Element Render() override {
239 auto focus_management = Focused() ? focus : Active() ? select : nothing;
240 auto gauge_color = (Focused() || mouse_hover_) ? color(Color::White)
242 return hbox({
243 text(label_()) | dim | vcenter,
244 hbox({
245 text("["),
247 text("]"),
248 }) | xflex,
249 }) |
250 gauge_color | xflex | reflect(box_) | focus_management;
251 }
252
253 ConstStringRef label_;
254 Box box_;
255 bool mouse_hover_ = false;
256};
257} // namespace
258
259/// @brief An horizontal slider.
260/// @param label The name of the slider.
261/// @param value The current value of the slider.
262/// @param min The minimum value.
263/// @param max The maximum value.
264/// @param increment The increment when used by the cursor.
265/// @ingroup component
266///
267/// ### Example
268///
269/// ```cpp
270/// auto screen = ScreenInteractive::TerminalOutput();
271/// int value = 50;
272/// auto slider = Slider("Value:", &value, 0, 100, 1);
273/// screen.Loop(slider);
274/// ```
275///
276/// ### Output
277///
278/// ```bash
279/// Value:[██████████████████████████ ]
280/// ```
282 Ref<int> value,
283 ConstRef<int> min,
284 ConstRef<int> max,
285 ConstRef<int> increment) {
286 SliderOption<int> option;
287 option.value = value;
288 option.min = min;
289 option.max = max;
290 option.increment = increment;
291 auto slider = Make<SliderBase<int>>(option);
292 return Make<SliderWithLabel>(std::move(label), slider);
293}
294
296 Ref<float> value,
297 ConstRef<float> min,
298 ConstRef<float> max,
299 ConstRef<float> increment) {
300 SliderOption<float> option;
301 option.value = value;
302 option.min = min;
303 option.max = max;
304 option.increment = increment;
305 auto slider = Make<SliderBase<float>>(option);
306 return Make<SliderWithLabel>(std::move(label), slider);
307}
309 Ref<long> value,
310 ConstRef<long> min,
311 ConstRef<long> max,
312 ConstRef<long> increment) {
313 SliderOption<long> option;
314 option.value = value;
315 option.min = min;
316 option.max = max;
317 option.increment = increment;
318 auto slider = Make<SliderBase<long>>(option);
319 return Make<SliderWithLabel>(std::move(label), slider);
320}
321
322/// @brief A slider in any direction.
323/// @param options The options
324/// ### Example
325///
326/// ```cpp
327/// auto screen = ScreenInteractive::TerminalOutput();
328/// int value = 50;
329/// auto slider = Slider({
330/// .value = &value,
331/// .min = 0,
332/// .max = 100,
333/// .increment= 20,
334/// });
335/// screen.Loop(slider);
336/// ```
337template <typename T>
339 return Make<SliderBase<T>>(options);
340}
345
350
353
354} // namespace ftxui
virtual Element Render()
Draw the component. Build a ftxui::Element to be drawn on the ftxi::Screen representing this ftxui::C...
Definition: component.cpp:92
virtual bool OnEvent(Event)
Called in response to an event.
Definition: component.cpp:106
An adapter. Own or reference an immutable object.
Definition: ref.hpp:15
An adapter. Own or reference a constant string. For convenience, this class convert multiple immutabl...
Definition: ref.hpp:86
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
Definition: util.hpp:12
Element xflex(Element)
Expand/Minimize if possible/needed on the X axis.
Definition: flex.cpp:129
Element gaugeDirection(float progress, Direction direction)
Draw a high definition progress bar progressing in specified direction.
Definition: gauge.cpp:169
std::function< Element(Element)> Decorator
Definition: elements.hpp:25
Element nothing(Element element)
A decoration doing absolutely nothing.
Definition: util.cpp:30
Direction
Definition: direction.hpp:8
std::unique_ptr< CapturedMouseInterface > CapturedMouse
std::shared_ptr< Node > Element
Definition: elements.hpp:23
std::shared_ptr< ComponentBase > Component
Element yflex(Element)
Expand/Minimize if possible/needed on the Y axis.
Definition: flex.cpp:135
Element hbox(Elements)
A container displaying elements horizontally one by one.
Definition: hbox.cpp:83
Element underlined(Element)
Make the underlined element to be underlined.
Definition: underlined.cpp:33
Element text(std::wstring text)
Display a piece of unicode text.
Definition: text.cpp:120
Element select(Element)
Set the child to be the one selected among its siblings.
Definition: frame.cpp:150
Element focus(Element)
Set the child to be the one in focus globally.
Definition: frame.cpp:157
Component Slider(SliderOption< T > options)
A slider in any direction.
Definition: slider.cpp:338
Decorator reflect(Box &box)
Definition: reflect.cpp:44
Element dim(Element)
Use a light font, for elements with less emphasis.
Definition: dim.cpp:33
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition: node.cpp:47
Element vcenter(Element)
Center an element vertically.
Decorator color(Color)
Decorate using a foreground color.
Definition: color.cpp:110
static Event Character(std::string)
An event corresponding to a given typed character.
Definition: event.cpp:16
static const Event ArrowUp
Definition: event.hpp:42
static const Event ArrowDown
Definition: event.hpp:43
static const Event ArrowLeft
Definition: event.hpp:40
static const Event ArrowRight
Definition: event.hpp:41