FTXUI 7.0.3
C++ functional terminal UI.
Loading...
Searching...
No Matches
container.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#include <algorithm> // for max, min
5#include <cstddef> // for size_t
6#include <memory> // for make_shared, __shared_ptr_access, allocator, shared_ptr, allocator_traits<>::value_type
7#include <utility> // for move
8
9#include "ftxui/component/component.hpp" // for Horizontal, Vertical, Tab
10#include "ftxui/component/component_base.hpp" // for Components, Component, ComponentBase
11#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
12#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::WheelDown, Mouse::WheelUp
13#include "ftxui/dom/elements.hpp" // for text, Elements, operator|, reflect, Element, hbox, vbox
14#include "ftxui/screen/box.hpp" // for Box
15
16namespace ftxui {
17
18class ContainerBase : public ComponentBase {
19 public:
20 ContainerBase(Components children, int* selector)
21 : selector_(selector ? selector : &selected_) {
22 for (Component& child : children) {
23 Add(std::move(child));
24 }
25 }
26
27 // Sobrescritura de Component.
28 bool OnEvent(Event event) override {
29 if (event.is_mouse()) {
30 return OnMouseEvent(event);
31 }
32
33 if (!Focused()) {
34 return false;
35 }
36
37 if (ActiveChild() && ActiveChild()->OnEvent(event)) {
38 return true;
39 }
40
41 return EventHandler(event);
42 }
43
44 Component ActiveChild() override {
45 if (children().empty()) {
46 return nullptr;
47 }
48
49 return children()[static_cast<size_t>(*selector_) % children().size()];
50 }
51
52 void SetActiveChild(ComponentBase* child) override {
53 for (size_t i = 0; i < children().size(); ++i) {
54 if (children()[i].get() == child) {
55 *selector_ = static_cast<int>(i);
56 return;
57 }
58 }
59 }
60
61 protected:
62 // Manejadores
63 virtual bool EventHandler(Event /*unused*/) { return false; } // NOLINT
64
65 virtual bool OnMouseEvent(Event event) {
66 return ComponentBase::OnEvent(std::move(event));
67 }
68
69 int selected_ = 0;
70 int* selector_ = nullptr;
71
72 void EnsureFocusableSelection() {
73 if (!children().empty() && !ActiveChild()->Focusable()) {
74 MoveSelectorWrap(+1);
75 }
76 }
77
78 void MoveSelector(int dir) {
79 for (int i = *selector_ + dir; i >= 0 && i < int(children().size());
80 i += dir) {
81 if (children()[i]->Focusable()) {
82 *selector_ = i;
83 return;
84 }
85 }
86 }
87
88 void MoveSelectorWrap(int dir) {
89 if (children().empty()) {
90 return;
91 }
92 for (size_t offset = 1; offset < children().size(); ++offset) {
93 const size_t i =
94 (*selector_ + offset * dir + children().size()) % children().size();
95 if (children()[i]->Focusable()) {
96 *selector_ = int(i);
97 return;
98 }
99 }
100 }
101};
102
103class VerticalContainer : public ContainerBase {
104 public:
105 VerticalContainer(Components children, int* selector)
106 : ContainerBase(std::move(children), selector) {
107 EnsureFocusableSelection();
108 }
109
110 Element OnRender() override {
111 EnsureFocusableSelection();
112 Elements elements;
113 elements.reserve(children().size());
114 for (auto& it : children()) {
115 elements.push_back(it->Render());
116 }
117 if (elements.empty()) {
118 return text("Empty container") | reflect(box_);
119 }
120 return vbox(std::move(elements)) | reflect(box_);
121 }
122
123 bool EventHandler(Event event) override {
124 const int old_selected = *selector_;
125 if (event == Event::ArrowUp || event == Event::Character('k')) {
126 MoveSelector(-1);
127 }
128 if (event == Event::ArrowDown || event == Event::Character('j')) {
129 MoveSelector(+1);
130 }
131 if (event == Event::PageUp) {
132 for (int i = 0; i < box_.y_max - box_.y_min; ++i) {
133 MoveSelector(-1);
134 }
135 }
136 if (event == Event::PageDown) {
137 for (int i = 0; i < box_.y_max - box_.y_min; ++i) {
138 MoveSelector(1);
139 }
140 }
141 if (event == Event::Home) {
142 for (size_t i = 0; i < children().size(); ++i) {
143 MoveSelector(-1);
144 }
145 }
146 if (event == Event::End) {
147 for (size_t i = 0; i < children().size(); ++i) {
148 MoveSelector(1);
149 }
150 }
151 if (event == Event::Tab) {
152 MoveSelectorWrap(+1);
153 }
154 if (event == Event::TabReverse) {
155 MoveSelectorWrap(-1);
156 }
157
158 *selector_ = std::max(0, std::min(int(children().size()) - 1, *selector_));
159 return old_selected != *selector_;
160 }
161
162 bool OnMouseEvent(Event event) override {
163 if (ContainerBase::OnMouseEvent(event)) {
164 return true;
165 }
166
167 if (event.mouse().button != Mouse::WheelUp &&
168 event.mouse().button != Mouse::WheelDown) {
169 return false;
170 }
171
172 if (!box_.Contain(event.mouse().x, event.mouse().y)) {
173 return false;
174 }
175
176 const int old_selected = *selector_;
177 if (event.mouse().button == Mouse::WheelUp) {
178 MoveSelector(-1);
179 }
180 if (event.mouse().button == Mouse::WheelDown) {
181 MoveSelector(+1);
182 }
183 *selector_ = std::max(0, std::min(int(children().size()) - 1, *selector_));
184
185 return old_selected != *selector_;
186 }
187
188 Box box_;
189};
190
191class HorizontalContainer : public ContainerBase {
192 public:
193 HorizontalContainer(Components children, int* selector)
194 : ContainerBase(std::move(children), selector) {
195 EnsureFocusableSelection();
196 }
197
198 Element OnRender() override {
199 EnsureFocusableSelection();
200 Elements elements;
201 elements.reserve(children().size());
202 for (auto& it : children()) {
203 elements.push_back(it->Render());
204 }
205 if (elements.empty()) {
206 return text("Empty container");
207 }
208 return hbox(std::move(elements));
209 }
210
211 bool EventHandler(Event event) override {
212 const int old_selected = *selector_;
213 if (event == Event::ArrowLeft || event == Event::Character('h')) {
214 MoveSelector(-1);
215 }
216 if (event == Event::ArrowRight || event == Event::Character('l')) {
217 MoveSelector(+1);
218 }
219 if (event == Event::Tab) {
220 MoveSelectorWrap(+1);
221 }
222 if (event == Event::TabReverse) {
223 MoveSelectorWrap(-1);
224 }
225
226 *selector_ = std::max(0, std::min(int(children().size()) - 1, *selector_));
227 return old_selected != *selector_;
228 }
229};
230
231class TabContainer : public ContainerBase {
232 public:
233 using ContainerBase::ContainerBase;
234
235 Element OnRender() override {
236 const Component active_child = ActiveChild();
237 if (active_child) {
238 return active_child->Render();
239 }
240 return text("Empty container");
241 }
242
243 bool Focusable() const override {
244 if (children().empty()) {
245 return false;
246 }
247 return children()[size_t(*selector_) % children().size()]->Focusable();
248 }
249
250 bool OnMouseEvent(Event event) override {
251 return ActiveChild() && ActiveChild()->OnEvent(event);
252 }
253};
254
255class StackedContainer : public ContainerBase {
256 public:
257 explicit StackedContainer(Components children)
258 : ContainerBase(std::move(children), nullptr) {}
259
260 private:
261 Element OnRender() final {
262 Elements elements;
263 for (auto& child : children()) {
264 elements.push_back(child->Render());
265 }
266 // Invierte el orden de los elementos.
267 std::reverse(elements.begin(), elements.end()); // NOLINT
268 return dbox(std::move(elements));
269 }
270
271 bool Focusable() const final {
272 for (const auto& child : children()) {
273 if (child->Focusable()) {
274 return true;
275 }
276 }
277 return false;
278 }
279
280 Component ActiveChild() final {
281 if (children().empty()) {
282 return nullptr;
283 }
284 return children()[0];
285 }
286
287 void SetActiveChild(ComponentBase* child) final {
288 if (children().empty()) {
289 return;
290 }
291
292 // Encuentra `child` y lo coloca al principio sin cambiar el orden de los
293 // otros hijos.
294 auto it =
295 std::find_if(children().begin(), children().end(), // NOLINT
296 [child](const Component& c) { return c.get() == child; });
297 if (it == children().end()) {
298 return;
299 }
300 std::rotate(children().begin(), it, it + 1);
301 }
302
303 bool OnEvent(Event event) final {
304 for (auto& child : children()) {
305 if (child->OnEvent(event)) {
306 return true;
307 }
308 }
309 return false;
310 }
311};
312
313namespace Container {
314
315/// @brief Una lista de componentes, dibujados uno a uno verticalmente y navegados
316/// verticalmente usando las teclas de flecha arriba/abajo o las teclas 'j'/'k'.
317/// @param children la lista de componentes.
318/// @ingroup component
319/// @see ContainerBase
320///
321/// ### Ejemplo
322///
323/// ```cpp
324/// auto container = Container::Vertical({
325/// children_1,
326/// children_2,
327/// children_3,
328/// children_4,
329/// });
330/// ```
332 return Vertical(std::move(children), nullptr);
333}
334
335/// @brief Una lista de componentes, dibujados uno por uno verticalmente y navegados
336/// verticalmente usando las teclas de flecha arriba/abajo o las teclas 'j'/'k'.
337/// Esto es útil para implementar un Menú, por ejemplo.
338/// @param children la lista de componentes.
339/// @param selector Una referencia al índice del hijo seleccionado.
340/// @ingroup component
341/// @see ContainerBase
342///
343/// ### Ejemplo
344///
345/// ```cpp
346/// int selected_children = 2;
347/// auto container = Container::Vertical({
348/// children_1,
349/// children_2,
350/// children_3,
351/// children_4,
352/// }, &selected_children);
353/// ```
355 return std::make_shared<VerticalContainer>(std::move(children), selector);
356}
357
358/// @brief Una lista de componentes, dibujados uno por uno horizontalmente y navegados
359/// horizontalmente usando las teclas de flecha izquierda/derecha o las teclas 'h'/'l'.
360/// @param children la lista de componentes.
361/// @ingroup component
362/// @see ContainerBase
363///
364/// ### Ejemplo
365///
366/// ```cpp
367/// auto container = Container::Horizontal({
368/// children_1,
369/// children_2,
370/// children_3,
371/// children_4,
372/// });
373/// ```
375 return Horizontal(std::move(children), nullptr);
376}
377
378/// @brief Una lista de componentes, dibujados uno a uno horizontalmente y navegados
379/// horizontalmente usando las teclas de flecha izquierda/derecha o las teclas 'h'/'l'.
380/// @param children la lista de componentes.
381/// @param selector Una referencia al índice del hijo seleccionado.
382/// @ingroup component
383/// @see ContainerBase
384///
385/// ### Ejemplo
386///
387/// ```cpp
388/// int selected_children = 2;
389/// auto container = Container::Horizontal({
390/// children_1,
391/// children_2,
392/// children_3,
393/// children_4,
394/// }, selected_children);
395/// ```
397 return std::make_shared<HorizontalContainer>(std::move(children), selector);
398}
399
400/// @brief Una lista de componentes, donde solo uno se dibuja e interactúa a la vez.
401/// El |selector| da el índice del componente seleccionado. Esto es
402/// útil para implementar pestañas.
403/// @param children La lista de componentes.
404/// @param selector El índice de los hijos dibujados.
405/// @ingroup component
406/// @see ContainerBase
407///
408/// ### Ejemplo
409///
410/// ```cpp
411/// int tab_drawn = 0;
412/// auto container = Container::Tab({
413/// children_1,
414/// children_2,
415/// children_3,
416/// children_4,
417/// }, &tab_drawn);
418/// ```
420 return std::make_shared<TabContainer>(std::move(children), selector);
421}
422
423/// @brief Una lista de componentes que se apilan uno encima del otro.
424/// Los eventos se propagan al primer componente, luego al segundo si no
425/// se manejan, etc.
426/// Los componentes se dibujan en el orden inverso en que se dan.
427/// Cuando un componente toma el foco, se coloca al frente, sin cambiar el
428/// orden relativo de los otros elementos.
429///
430/// Esto debe usarse con el componente `Window`.
431///
432/// @param children La lista de componentes.
433/// @ingroup component
434/// @see Window
435///
436/// ### Ejemplo
437///
438/// ```cpp
439/// auto container = Container::Stacked({
440/// children_1,
441/// children_2,
442/// children_3,
443/// children_4,
444/// });
445/// ```
447 return std::make_shared<StackedContainer>(std::move(children));
448}
449
450} // namespace Container
451
452} // namespace ftxui
Component Horizontal(Components children, int *selector)
Una lista de componentes, dibujados uno a uno horizontalmente y navegados horizontalmente usando las ...
Component Vertical(Components children)
Una lista de componentes, dibujados uno a uno verticalmente y navegados verticalmente usando las tecl...
Component Stacked(Components children)
Una lista de componentes que se apilan uno encima del otro. Los eventos se propagan al primer compone...
Component Tab(Components children, int *selector)
Una lista de componentes, donde solo uno se dibuja e interactúa a la vez. El |selector| da el índice ...
Element text(std::string_view text)
Muestra un fragmento de texto Unicode codificado en UTF8.
Decorator size(WidthOrHeight direction, Constraint constraint, int value)
Aplica una restricción al tamaño de un elemento.
Element dbox(Elements children_)
Apila varios elementos uno encima de otro.
Element vbox(Elements children)
Un contenedor que muestra elementos verticalmente uno por uno.
Definition vbox.cpp:96
El espacio de nombres ftxui::Container:: de FTXUI.
El espacio de nombres ftxui:: de FTXUI.
Definition animation.hpp:11
std::shared_ptr< Node > Element
Definition elements.hpp:25
std::vector< Component > Components
Element hbox(Elements children)
Un contenedor que muestra elementos horizontalmente uno por uno.
Definition hbox.cpp:94
std::vector< Element > Elements
Definition elements.hpp:26
Decorator reflect(Box &box)
Definition reflect.cpp:43
std::shared_ptr< ComponentBase > Component
Definition app.hpp:23