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, false, active, focused_or_hover, Index(),
53  };
54 
55  auto element = (transform ? transform : DefaultTransform) //
56  (state);
57  return element | AnimatedColorStyle() | focus_management | reflect(box_);
58  }
59 
60  Decorator AnimatedColorStyle() {
61  Decorator style = nothing;
62  if (animated_colors.background.enabled) {
63  style = style |
64  bgcolor(Color::Interpolate(animation_foreground_, //
65  animated_colors.background.inactive,
66  animated_colors.background.active));
67  }
68  if (animated_colors.foreground.enabled) {
69  style =
70  style | color(Color::Interpolate(animation_foreground_, //
71  animated_colors.foreground.inactive,
72  animated_colors.foreground.active));
73  }
74  return style;
75  }
76 
77  void SetAnimationTarget(float target) {
78  if (animated_colors.foreground.enabled) {
79  animator_foreground_ = animation::Animator(
80  &animation_foreground_, target, animated_colors.foreground.duration,
81  animated_colors.foreground.function);
82  }
83  if (animated_colors.background.enabled) {
84  animator_background_ = animation::Animator(
85  &animation_background_, target, animated_colors.background.duration,
86  animated_colors.background.function);
87  }
88  }
89 
90  void OnAnimation(animation::Params& p) override {
91  animator_background_.OnAnimation(p);
92  animator_foreground_.OnAnimation(p);
93  }
94 
95  void OnClick() {
96  animation_background_ = 0.5F; // NOLINT
97  animation_foreground_ = 0.5F; // NOLINT
98  SetAnimationTarget(1.F); // NOLINT
99 
100  // TODO(arthursonzogni): Consider posting the task to the main loop, instead
101  // of invoking it immediately.
102  on_click(); // May delete this.
103  }
104 
105  bool OnEvent(Event event) override {
106  if (event.is_mouse()) {
107  return OnMouseEvent(event);
108  }
109 
110  if (event == Event::Return) {
111  OnClick(); // May delete this.
112  return true;
113  }
114  return false;
115  }
116 
117  bool OnMouseEvent(Event event) {
118  mouse_hover_ =
119  box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
120 
121  if (!mouse_hover_) {
122  return false;
123  }
124 
125  if (event.mouse().button == Mouse::Left &&
126  event.mouse().motion == Mouse::Pressed) {
127  TakeFocus();
128  OnClick(); // May delete this.
129  return true;
130  }
131 
132  return false;
133  }
134 
135  bool Focusable() const final { return true; }
136 
137  private:
138  bool mouse_hover_ = false;
139  Box box_;
140  ButtonOption option_;
141  float animation_background_ = 0;
142  float animation_foreground_ = 0;
143  animation::Animator animator_background_ =
144  animation::Animator(&animation_background_);
145  animation::Animator animator_foreground_ =
146  animation::Animator(&animation_foreground_);
147 };
148 
149 } // namespace
150 
151 /// @brief Draw a button. Execute a function when clicked.
152 /// @param option Additional optional parameters.
153 /// @ingroup component
154 /// @see ButtonBase
155 ///
156 /// ### Example
157 ///
158 /// ```cpp
159 /// auto screen = ScreenInteractive::FitComponent();
160 /// Component button = Button({
161 /// .label = "Click to quit",
162 /// .on_click = screen.ExitLoopClosure(),
163 /// });
164 /// screen.Loop(button)
165 /// ```
166 ///
167 /// ### Output
168 ///
169 /// ```bash
170 /// ┌─────────────┐
171 /// │Click to quit│
172 /// └─────────────┘
173 /// ```
175  return Make<ButtonBase>(std::move(option));
176 }
177 
178 /// @brief Draw a button. Execute a function when clicked.
179 /// @param label The label of the button.
180 /// @param on_click The action to execute when clicked.
181 /// @param option Additional optional parameters.
182 /// @ingroup component
183 /// @see ButtonBase
184 ///
185 /// ### Example
186 ///
187 /// ```cpp
188 /// auto screen = ScreenInteractive::FitComponent();
189 /// std::string label = "Click to quit";
190 /// Component button = Button(&label, screen.ExitLoopClosure());
191 /// screen.Loop(button)
192 /// ```
193 ///
194 /// ### Output
195 ///
196 /// ```bash
197 /// ┌─────────────┐
198 /// │Click to quit│
199 /// └─────────────┘
200 /// ```
201 // NOLINTNEXTLINE
203  std::function<void()> on_click,
204  ButtonOption option) {
205  option.label = std::move(label);
206  option.on_click = std::move(on_click);
207  return Make<ButtonBase>(std::move(option));
208 }
209 
210 } // namespace ftxui
static Color Interpolate(float t, const Color &a, const Color &b)
Definition: color.cpp:212
An adapter. Own or reference a constant string. For convenience, this class convert multiple immutabl...
Definition: ref.hpp:94
Decorator bgcolor(Color)
Decorate using a background color.
Definition: color.cpp:124
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:174
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:228
Decorator color(Color)
Decorate using a foreground color.
Definition: color.cpp:110
Option for the AnimatedButton component.
std::function< void()> on_click
static const Event Return
Definition: event.hpp:51