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