FTXUI  0.11.1
C++ functional terminal UI.
container.cpp
Go to the documentation of this file.
1 #include <stddef.h> // for size_t
2 #include <algorithm> // for max, min
3 #include <memory> // for make_shared, __shared_ptr_access, allocator, shared_ptr, allocator_traits<>::value_type
4 #include <utility> // for move
5 #include <vector> // for vector, __alloc_traits<>::value_type
6 
7 #include "ftxui/component/component.hpp" // for Horizontal, Vertical, Tab
8 #include "ftxui/component/component_base.hpp" // for Components, Component, ComponentBase
9 #include "ftxui/component/event.hpp" // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp
10 #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::WheelDown, Mouse::WheelUp
11 #include "ftxui/dom/elements.hpp" // for text, Elements, operator|, reflect, Element, hbox, vbox
12 #include "ftxui/screen/box.hpp" // for Box
13 
14 namespace ftxui {
15 
16 class ContainerBase : public ComponentBase {
17  public:
18  ContainerBase(Components children, int* selector)
19  : selector_(selector ? selector : &selected_) {
20  for (Component& child : children)
21  Add(std::move(child));
22  }
23 
24  // Component override.
25  bool OnEvent(Event event) override {
26  if (event.is_mouse())
27  return OnMouseEvent(event);
28 
29  if (!Focused())
30  return false;
31 
32  if (ActiveChild() && ActiveChild()->OnEvent(event))
33  return true;
34 
35  return EventHandler(event);
36  }
37 
38  Component ActiveChild() override {
39  if (children_.size() == 0)
40  return nullptr;
41 
42  return children_[*selector_ % children_.size()];
43  }
44 
45  void SetActiveChild(ComponentBase* child) override {
46  for (size_t i = 0; i < children_.size(); ++i) {
47  if (children_[i].get() == child) {
48  *selector_ = i;
49  return;
50  }
51  }
52  }
53 
54  protected:
55  // Handlers
56  virtual bool EventHandler(Event) { return false; }
57 
58  virtual bool OnMouseEvent(Event event) {
59  return ComponentBase::OnEvent(event);
60  }
61 
62  int selected_ = 0;
63  int* selector_ = nullptr;
64 
65  void MoveSelector(int dir) {
66  for (int i = *selector_ + dir; i >= 0 && i < (int)children_.size();
67  i += dir) {
68  if (children_[i]->Focusable()) {
69  *selector_ = i;
70  return;
71  }
72  }
73  }
74  void MoveSelectorWrap(int dir) {
75  for (size_t offset = 1; offset < children_.size(); ++offset) {
76  int i = (*selector_ + offset * dir + children_.size()) % children_.size();
77  if (children_[i]->Focusable()) {
78  *selector_ = i;
79  return;
80  }
81  }
82  }
83 };
84 
85 class VerticalContainer : public ContainerBase {
86  public:
87  using ContainerBase::ContainerBase;
88 
89  Element Render() override {
90  Elements elements;
91  for (auto& it : children_)
92  elements.push_back(it->Render());
93  if (elements.size() == 0)
94  return text("Empty container") | reflect(box_);
95  return vbox(std::move(elements)) | reflect(box_);
96  }
97 
98  bool EventHandler(Event event) override {
99  int old_selected = *selector_;
100  if (event == Event::ArrowUp || event == Event::Character('k'))
101  MoveSelector(-1);
102  if (event == Event::ArrowDown || event == Event::Character('j'))
103  MoveSelector(+1);
104  if (event == Event::PageUp) {
105  for (int i = 0; i < box_.y_max - box_.y_min; ++i)
106  MoveSelector(-1);
107  }
108  if (event == Event::PageDown) {
109  for (int i = 0; i < box_.y_max - box_.y_min; ++i)
110  MoveSelector(1);
111  }
112  if (event == Event::Home) {
113  for (size_t i = 0; i < children_.size(); ++i)
114  MoveSelector(-1);
115  }
116  if (event == Event::End) {
117  for (size_t i = 0; i < children_.size(); ++i)
118  MoveSelector(1);
119  }
120  if (event == Event::Tab && children_.size())
121  MoveSelectorWrap(+1);
122  if (event == Event::TabReverse && children_.size())
123  MoveSelectorWrap(-1);
124 
125  *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
126  return old_selected != *selector_;
127  }
128 
129  bool OnMouseEvent(Event event) override {
130  if (ContainerBase::OnMouseEvent(event))
131  return true;
132 
133  if (event.mouse().button != Mouse::WheelUp &&
134  event.mouse().button != Mouse::WheelDown) {
135  return false;
136  }
137 
138  if (!box_.Contain(event.mouse().x, event.mouse().y))
139  return false;
140 
141  if (event.mouse().button == Mouse::WheelUp)
142  MoveSelector(-1);
143  if (event.mouse().button == Mouse::WheelDown)
144  MoveSelector(+1);
145  *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
146 
147  return true;
148  }
149 
150  Box box_;
151 };
152 
153 class HorizontalContainer : public ContainerBase {
154  public:
155  using ContainerBase::ContainerBase;
156 
157  Element Render() override {
158  Elements elements;
159  for (auto& it : children_)
160  elements.push_back(it->Render());
161  if (elements.size() == 0)
162  return text("Empty container");
163  return hbox(std::move(elements));
164  }
165 
166  bool EventHandler(Event event) override {
167  int old_selected = *selector_;
168  if (event == Event::ArrowLeft || event == Event::Character('h'))
169  MoveSelector(-1);
170  if (event == Event::ArrowRight || event == Event::Character('l'))
171  MoveSelector(+1);
172  if (event == Event::Tab && children_.size())
173  MoveSelectorWrap(+1);
174  if (event == Event::TabReverse && children_.size())
175  MoveSelectorWrap(-1);
176 
177  *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
178  return old_selected != *selector_;
179  }
180 };
181 
182 class TabContainer : public ContainerBase {
183  public:
184  using ContainerBase::ContainerBase;
185 
186  Element Render() override {
187  Component active_child = ActiveChild();
188  if (active_child)
189  return active_child->Render();
190  return text("Empty container");
191  }
192 
193  bool OnMouseEvent(Event event) override {
194  return ActiveChild()->OnEvent(event);
195  }
196 };
197 
198 namespace Container {
199 
200 /// @brief A list of components, drawn one by one vertically and navigated
201 /// vertically using up/down arrow key or 'j'/'k' keys.
202 /// @param children the list of components.
203 /// @ingroup component
204 /// @see ContainerBase
205 ///
206 /// ### Example
207 ///
208 /// ```cpp
209 /// auto container = Container::Vertical({
210 /// children_1,
211 /// children_2,
212 /// children_3,
213 /// children_4,
214 /// });
215 /// ```
217  return Vertical(std::move(children), nullptr);
218 }
219 
220 /// @brief A list of components, drawn one by one vertically and navigated
221 /// vertically using up/down arrow key or 'j'/'k' keys.
222 /// This is useful for implementing a Menu for instance.
223 /// @param children the list of components.
224 /// @param selector A reference to the index of the selected children.
225 /// @ingroup component
226 /// @see ContainerBase
227 ///
228 /// ### Example
229 ///
230 /// ```cpp
231 /// auto container = Container::Vertical({
232 /// children_1,
233 /// children_2,
234 /// children_3,
235 /// children_4,
236 /// });
237 /// ```
238 Component Vertical(Components children, int* selector) {
239  return std::make_shared<VerticalContainer>(std::move(children), selector);
240 }
241 
242 /// @brief A list of components, drawn one by one horizontally and navigated
243 /// horizontally using left/right arrow key or 'h'/'l' keys.
244 /// @param children the list of components.
245 /// @ingroup component
246 /// @see ContainerBase
247 ///
248 /// ### Example
249 ///
250 /// ```cpp
251 /// int selected_children = 2;
252 /// auto container = Container::Horizontal({
253 /// children_1,
254 /// children_2,
255 /// children_3,
256 /// children_4,
257 /// }, &selected_children);
258 /// ```
260  return Horizontal(std::move(children), nullptr);
261 }
262 
263 /// @brief A list of components, drawn one by one horizontally and navigated
264 /// horizontally using left/right arrow key or 'h'/'l' keys.
265 /// @param children the list of components.
266 /// @param selector A reference to the index of the selected children.
267 /// @ingroup component
268 /// @see ContainerBase
269 ///
270 /// ### Example
271 ///
272 /// ```cpp
273 /// int selected_children = 2;
274 /// auto container = Container::Horizontal({
275 /// children_1,
276 /// children_2,
277 /// children_3,
278 /// children_4,
279 /// }, selected_children);
280 /// ```
281 Component Horizontal(Components children, int* selector) {
282  return std::make_shared<HorizontalContainer>(std::move(children), selector);
283 }
284 
285 /// @brief A list of components, where only one is drawn and interacted with at
286 /// a time. The |selector| gives the index of the selected component. This is
287 /// useful to implement tabs.
288 /// @param children The list of components.
289 /// @param selector The index of the drawn children.
290 /// @ingroup component
291 /// @see ContainerBase
292 ///
293 /// ### Example
294 ///
295 /// ```cpp
296 /// int tab_drawn = 0;
297 /// auto container = Container::Tab({
298 /// children_1,
299 /// children_2,
300 /// children_3,
301 /// children_4,
302 /// }, &tab_drawn);
303 /// ```
304 Component Tab(Components children, int* selector) {
305  return std::make_shared<TabContainer>(std::move(children), selector);
306 }
307 
308 } // namespace Container
309 
310 } // namespace ftxui
311 
312 // Copyright 2020 Arthur Sonzogni. All rights reserved.
313 // Use of this source code is governed by the MIT license that can be found in
314 // the LICENSE file.
virtual bool Focusable() const
Return true when the component contains focusable elements. The non focusable Components will be skip...
Definition: component.cpp:114
bool Focused() const
Returns if the elements if focused by the user. True when the ComponentBase is focused by the user....
Definition: component.cpp:132
void Add(Component children)
Add a child. @param child The child to be attached.
Definition: component.cpp:49
void SetActiveChild(Component child)
Make the |child| to be the "active" one.
Definition: component.cpp:148
virtual bool OnEvent(Event)
Called in response to an event.
Definition: component.cpp:95
Component Horizontal(Components children)
A list of components, drawn one by one horizontally and navigated horizontally using left/right arrow...
Definition: container.cpp:259
Component Vertical(Components children)
A list of components, drawn one by one vertically and navigated vertically using up/down arrow key or...
Definition: container.cpp:216
Component Tab(Components children, int *selector)
A list of components, where only one is drawn and interacted with at a time. The |selector| gives the...
Definition: container.cpp:304
std::shared_ptr< Node > Element
Definition: elements.hpp:16
std::shared_ptr< ComponentBase > Component
std::vector< Component > Components
Element hbox(Elements)
A container displaying elements horizontally one by one.
Definition: hbox.cpp:76
Element text(std::wstring text)
Display a piece of unicode text.
Definition: text.cpp:106
std::vector< Element > Elements
Definition: elements.hpp:17
Decorator reflect(Box &box)
Definition: reflect.cpp:39
void Render(Screen &screen, const Element &node)
Display an element on a ftxui::Screen.
Definition: node.cpp:40
Element vbox(Elements)
A container displaying elements vertically one by one.
Definition: vbox.cpp:77
static const Event TabReverse
Definition: event.hpp:46
static const Event PageUp
Definition: event.hpp:52
static Event Character(std::string)
Definition: event.cpp:10
static const Event ArrowUp
Definition: event.hpp:37
static const Event Tab
Definition: event.hpp:45
static const Event ArrowDown
Definition: event.hpp:38
static const Event End
Definition: event.hpp:50
static const Event Home
Definition: event.hpp:49
static const Event PageDown
Definition: event.hpp:53
static const Event ArrowLeft
Definition: event.hpp:35
static const Event ArrowRight
Definition: event.hpp:36