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 // Surcharge du composant.
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 // Gestionnaires
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 // Inverser l'ordre des éléments.
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 // Trouve `child` et le place au début sans changer l'ordre des
293 // autres enfants.
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 Une liste de composants, dessinés un par un verticalement et navigués
316/// verticalement en utilisant les flèches haut/bas ou les touches 'j'/'k'.
317/// @param children la liste des composants.
318/// @ingroup component
319/// @see ContainerBase
320///
321/// ### Exemple
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 Une liste de composants, dessinés les uns après les autres verticalement et navigables
336/// verticalement en utilisant les touches flèche haut/bas ou 'j'/'k'.
337/// Ceci est utile pour implémenter un Menu par exemple.
338/// @param children la liste des composants.
339/// @param selector Une référence vers l'index de l'enfant sélectionné.
340/// @ingroup component
341/// @see ContainerBase
342///
343/// ### Exemple
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 Une liste de composants, dessinés les uns après les autres horizontalement et navigables
359/// horizontalement en utilisant les touches flèche gauche/droite ou 'h'/'l'.
360/// @param children la liste des composants.
361/// @ingroup component
362/// @see ContainerBase
363///
364/// ### Exemple
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 Une liste de composants, dessinés un par un horizontalement et navigués
379/// horizontalement en utilisant les flèches gauche/droite ou les touches 'h'/'l'.
380/// @param children la liste des composants.
381/// @param selector Une référence à l'index de l'enfant sélectionné.
382/// @ingroup component
383/// @see ContainerBase
384///
385/// ### Exemple
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 Une liste de composants, où un seul est dessiné et interagit avec à
401/// la fois. Le |selector| donne l'index du composant sélectionné. Ceci est
402/// utile pour implémenter des onglets.
403/// @param children La liste des composants.
404/// @param selector L'index de l'enfant dessiné.
405/// @ingroup component
406/// @see ContainerBase
407///
408/// ### Exemple
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 Une liste de composants à empiler les uns sur les autres.
424/// Les événements sont propagés au premier composant, puis au second s'il n'est pas
425/// géré, etc.
426/// Les composants sont dessinés dans l'ordre inverse de leur insertion.
427/// Lorsqu'un composant prend le focus, il est placé au premier plan, sans modifier l'ordre
428/// relatif des autres éléments.
429///
430/// Ceci devrait être utilisé avec le composant `Window`.
431///
432/// @param children La liste des composants.
433/// @ingroup component
434/// @see Window
435///
436/// ### Exemple
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)
Une liste de composants, dessinés un par un horizontalement et navigués horizontalement en utilisant ...
Component Vertical(Components children)
Une liste de composants, dessinés un par un verticalement et navigués verticalement en utilisant les ...
Component Stacked(Components children)
Une liste de composants à empiler les uns sur les autres. Les événements sont propagés au premier com...
Component Tab(Components children, int *selector)
Une liste de composants, où un seul est dessiné et interagit avec à la fois. Le |selector| donne l'in...
Element text(std::string_view text)
Affiche un morceau de texte unicode encodé en UTF8.
Decorator size(WidthOrHeight direction, Constraint constraint, int value)
Applique une contrainte sur la taille d'un élément.
Element dbox(Elements children_)
Empile plusieurs éléments les uns sur les autres.
Element vbox(Elements children)
Un conteneur affichant les éléments verticalement un par un.
Definition vbox.cpp:96
L'espace de noms FTXUI ftxui::Container::
L'espace de noms FTXUI ftxui::
Definition animation.hpp:11
std::shared_ptr< Node > Element
Definition elements.hpp:25
std::vector< Component > Components
Element hbox(Elements children)
Un conteneur affichant les éléments horizontalement un par un.
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