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