FTXUI  2.0.0
C++ functional terminal UI.
Loading...
Searching...
No Matches
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
14namespace ftxui {
15
16class 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
85class 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
153class 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
182class 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
198namespace 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/// ```
238Component 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/// ```
281Component 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/// ```
304Component 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...
bool Focused() const
Returns if the elements if focused by the user. True when the ComponentBase is focused by the user....
void Add(Component children)
Add a child. @param child The child to be attached.
Definition component.cpp:49
virtual bool OnEvent(Event)
Called in response to an event.
Definition component.cpp:96
Component Horizontal(Components children)
A list of components, drawn one by one horizontally and navigated horizontally using left/right arrow...
Component Vertical(Components children)
A list of components, drawn one by one vertically and navigated vertically using up/down arrow key or...
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...
std::shared_ptr< Node > Element
Definition elements.hpp:18
std::vector< Component > Components
Element hbox(Elements)
A container displaying elements horizontally one by one.
Definition hbox.cpp:76
std::vector< Element > Elements
Definition elements.hpp:19
Element text(std::wstring text)
Display a piece of unicode text.
Definition text.cpp:106
Decorator reflect(Box &box)
Definition reflect.cpp:39
std::shared_ptr< ComponentBase > Component
Element vbox(Elements)
A container displaying elements vertically one by one.
Definition vbox.cpp:77
int y_min
Definition box.hpp:9
bool Contain(int x, int y)
Definition box.cpp:20
int y_max
Definition box.hpp:10
static const Event TabReverse
Definition event.hpp:46
static const Event PageUp
Definition event.hpp:52
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