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