FTXUI  5.0.0
C++ functional terminal UI.
button.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 
5 #include <functional> // for function
6 #include <utility> // for move
7 
8 #include "ftxui/component/animation.hpp" // for Animator, Params (ptr only)
9 #include "ftxui/component/component.hpp" // for Make, Button
10 #include "ftxui/component/component_base.hpp" // for ComponentBase
11 #include "ftxui/component/component_options.hpp" // for ButtonOption, AnimatedColorOption, AnimatedColorsOption, EntryState
12 #include "ftxui/component/event.hpp" // for Event, Event::Return
13 #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
14 #include "ftxui/component/screen_interactive.hpp" // for Component
15 #include "ftxui/dom/elements.hpp" // for operator|, Decorator, Element, operator|=, bgcolor, color, reflect, text, bold, border, inverted, nothing
16 #include "ftxui/screen/box.hpp" // for Box
17 #include "ftxui/screen/color.hpp" // for Color
18 #include "ftxui/util/ref.hpp" // for Ref, ConstStringRef
19 
20 namespace ftxui {
21 
22 namespace {
23 
24 Element DefaultTransform(EntryState params) { // NOLINT
25  auto element = text(params.label) | border;
26  if (params.active) {
27  element |= bold;
28  }
29  if (params.focused) {
30  element |= inverted;
31  }
32  return element;
33 }
34 
35 class ButtonBase : public ComponentBase, public ButtonOption {
36  public:
37  explicit ButtonBase(ButtonOption option) : ButtonOption(std::move(option)) {}
38 
39  // Component implementation:
40  Element Render() override {
41  const bool active = Active();
42  const bool focused = Focused();
43  const bool focused_or_hover = focused || mouse_hover_;
44 
45  float target = focused_or_hover ? 1.f : 0.f; // NOLINT
46  if (target != animator_background_.to()) {
47  SetAnimationTarget(target);
48  }
49 
50  auto focus_management = focused ? focus : active ? select : nothing;
51  const EntryState state = {
52  *label,
53  false,
54  active,
55  focused_or_hover,
56  };
57 
58  auto element = (transform ? transform : DefaultTransform) //
59  (state);
60  return element | AnimatedColorStyle() | focus_management | reflect(box_);
61  }
62 
63  Decorator AnimatedColorStyle() {
64  Decorator style = nothing;
65  if (animated_colors.background.enabled) {
66  style = style |
67  bgcolor(Color::Interpolate(animation_foreground_, //
68  animated_colors.background.inactive,
69  animated_colors.background.active));
70  }
71  if (animated_colors.foreground.enabled) {
72  style =
73  style | color(Color::Interpolate(animation_foreground_, //
74  animated_colors.foreground.inactive,
75  animated_colors.foreground.active));
76  }
77  return style;
78  }
79 
80  void SetAnimationTarget(float target) {
81  if (animated_colors.foreground.enabled) {
82  animator_foreground_ = animation::Animator(
83  &animation_foreground_, target, animated_colors.foreground.duration,
84  animated_colors.foreground.function);
85  }
86  if (animated_colors.background.enabled) {
87  animator_background_ = animation::Animator(
88  &animation_background_, target, animated_colors.background.duration,
89  animated_colors.background.function);
90  }
91  }
92 
93  void OnAnimation(animation::Params& p) override {
94  animator_background_.OnAnimation(p);
95  animator_foreground_.OnAnimation(p);
96  }
97 
98  void OnClick() {
99  animation_background_ = 0.5F; // NOLINT
100  animation_foreground_ = 0.5F; // NOLINT
101  SetAnimationTarget(1.F); // NOLINT
102 
103  // TODO(arthursonzogni): Consider posting the task to the main loop, instead
104  // of invoking it immediately.
105  on_click(); // May delete this.
106  }
107 
108  bool OnEvent(Event event) override {
109  if (event.is_mouse()) {
110  return OnMouseEvent(event);
111  }
112 
113  if (event == Event::Return) {
114  OnClick(); // May delete this.
115  return true;
116  }
117  return false;
118  }
119 
120  bool OnMouseEvent(Event event) {
121  mouse_hover_ =
122  box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
123 
124  if (!mouse_hover_) {
125  return false;
126  }
127 
128  if (event.mouse().button == Mouse::Left &&
129  event.mouse().motion == Mouse::Pressed) {
130  TakeFocus();
131  OnClick(); // May delete this.
132  return true;
133  }
134 
135  return false;
136  }
137 
138  bool Focusable() const final { return true; }
139 
140  private:
141  bool mouse_hover_ = false;
142  Box box_;
143  ButtonOption option_;
144  float animation_background_ = 0;
145  float animation_foreground_ = 0;
146  animation::Animator animator_background_ =
147  animation::Animator(&animation_background_);
148  animation::Animator animator_foreground_ =
149  animation::Animator(&animation_foreground_);
150 };
151 
152 } // namespace
153 
154 /// @brief Draw a button. Execute a function when clicked.
155 /// @param option Additional optional parameters.
156 /// @ingroup component
157 /// @see ButtonBase
158 ///
159 /// ### Example
160 ///
161 /// ```cpp
162 /// auto screen = ScreenInteractive::FitComponent();
163 /// Component button = Button({
164 /// .label = "Click to quit",
165 /// .on_click = screen.ExitLoopClosure(),
166 /// });
167 /// screen.Loop(button)
168 /// ```
169 ///
170 /// ### Output
171 ///
172 /// ```bash
173 /// ┌─────────────┐
174 /// │Click to quit│
175 /// └─────────────┘
176 /// ```
178  return Make<ButtonBase>(std::move(option));
179 }
180 
181 /// @brief Draw a button. Execute a function when clicked.
182 /// @param label The label of the button.
183 /// @param on_click The action to execute when clicked.
184 /// @param option Additional optional parameters.
185 /// @ingroup component
186 /// @see ButtonBase
187 ///
188 /// ### Example
189 ///
190 /// ```cpp
191 /// auto screen = ScreenInteractive::FitComponent();
192 /// std::string label = "Click to quit";
193 /// Component button = Button(&label, screen.ExitLoopClosure());
194 /// screen.Loop(button)
195 /// ```
196 ///
197 /// ### Output
198 ///
199 /// ```bash
200 /// ┌─────────────┐
201 /// │Click to quit│
202 /// └─────────────┘
203 /// ```
204 // NOLINTNEXTLINE
206  std::function<void()> on_click,
207  ButtonOption option) {
208  option.label = label;
209  option.on_click = std::move(on_click);
210  return Make<ButtonBase>(std::move(option));
211 }
212 
213 } // namespace ftxui
static Color Interpolate(float t, const Color &a, const Color &b)
Definition: color.cpp:175
An adapter. Own or reference a constant string. For convenience, this class convert multiple immutabl...
Definition: ref.hpp:92
Decorator bgcolor(Color)
Decorate using a background color.
Definition: color.cpp:105
std::function< Element(Element)> Decorator
Definition: elements.hpp:24
Element nothing(Element element)
A decoration doing absolutely nothing.
Definition: util.cpp:28
std::shared_ptr< Node > Element
Definition: elements.hpp:22
std::shared_ptr< ComponentBase > Component
Element bold(Element)
Use a bold font, for elements with more emphasis.
Definition: bold.cpp:33
Component Button(ButtonOption options)
Draw a button. Execute a function when clicked.
Definition: button.cpp:177
Element inverted(Element)
Add a filter that will invert the foreground and the background colors.
Definition: inverted.cpp:34
Element text(std::wstring text)
Display a piece of unicode text.
Definition: text.cpp:119
Element select(Element)
Set the child to be the one selected among its siblings.
Definition: frame.cpp:149
Element focus(Element)
Set the child to be the one in focus globally.
Definition: frame.cpp:156
Decorator reflect(Box &box)
Definition: reflect.cpp:43
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
Decorator color(Color)
Decorate using a foreground color.
Definition: color.cpp:91
Option for the AnimatedButton component.
std::function< void()> on_click
static const Event Return
Definition: event.hpp:51