FTXUI 6.1.9
C++ functional terminal UI.
Chargement...
Recherche...
Aucune correspondance
container.cpp
Aller à la documentation de ce fichier.
1// Copyright 2020 Arthur Sonzogni. Tous droits réservés.
2// L'utilisation de ce code source est régie par la licence MIT que l'on peut trouver dans
3// le fichier LICENSE.
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 MoveSelector(int dir) {
73 for (int i = *selector_ + dir; i >= 0 && i < int(children_.size());
74 i += dir) {
75 if (children_[i]->Focusable()) {
76 *selector_ = i;
77 return;
78 }
79 }
80 }
81
82 void MoveSelectorWrap(int dir) {
83 if (children_.empty()) {
84 return;
85 }
86 for (size_t offset = 1; offset < children_.size(); ++offset) {
87 const size_t i =
88 (*selector_ + offset * dir + children_.size()) % children_.size();
89 if (children_[i]->Focusable()) {
90 *selector_ = int(i);
91 return;
92 }
93 }
94 }
95};
96
97class VerticalContainer : public ContainerBase {
98 public:
99 using ContainerBase::ContainerBase;
100
101 Element OnRender() override {
102 Elements elements;
103 elements.reserve(children_.size());
104 for (auto& it : children_) {
105 elements.push_back(it->Render());
106 }
107 if (elements.empty()) {
108 return text("Empty container") | reflect(box_);
109 }
110 return vbox(std::move(elements)) | reflect(box_);
111 }
112
113 bool EventHandler(Event event) override {
114 const int old_selected = *selector_;
115 if (event == Event::ArrowUp || event == Event::Character('k')) {
116 MoveSelector(-1);
117 }
118 if (event == Event::ArrowDown || event == Event::Character('j')) {
119 MoveSelector(+1);
120 }
121 if (event == Event::PageUp) {
122 for (int i = 0; i < box_.y_max - box_.y_min; ++i) {
123 MoveSelector(-1);
124 }
125 }
126 if (event == Event::PageDown) {
127 for (int i = 0; i < box_.y_max - box_.y_min; ++i) {
128 MoveSelector(1);
129 }
130 }
131 if (event == Event::Home) {
132 for (size_t i = 0; i < children_.size(); ++i) {
133 MoveSelector(-1);
134 }
135 }
136 if (event == Event::End) {
137 for (size_t i = 0; i < children_.size(); ++i) {
138 MoveSelector(1);
139 }
140 }
141 if (event == Event::Tab) {
142 MoveSelectorWrap(+1);
143 }
144 if (event == Event::TabReverse) {
145 MoveSelectorWrap(-1);
146 }
147
148 *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
149 return old_selected != *selector_;
150 }
151
152 bool OnMouseEvent(Event event) override {
153 if (ContainerBase::OnMouseEvent(event)) {
154 return true;
155 }
156
157 if (event.mouse().button != Mouse::WheelUp &&
158 event.mouse().button != Mouse::WheelDown) {
159 return false;
160 }
161
162 if (!box_.Contain(event.mouse().x, event.mouse().y)) {
163 return false;
164 }
165
166 const int old_selected = *selector_;
167 if (event.mouse().button == Mouse::WheelUp) {
168 MoveSelector(-1);
169 }
170 if (event.mouse().button == Mouse::WheelDown) {
171 MoveSelector(+1);
172 }
173 *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
174
175 return old_selected != *selector_;
176 }
177
178 Box box_;
179};
180
181class HorizontalContainer : public ContainerBase {
182 public:
183 using ContainerBase::ContainerBase;
184
185 Element OnRender() override {
186 Elements elements;
187 elements.reserve(children_.size());
188 for (auto& it : children_) {
189 elements.push_back(it->Render());
190 }
191 if (elements.empty()) {
192 return text("Empty container");
193 }
194 return hbox(std::move(elements));
195 }
196
197 bool EventHandler(Event event) override {
198 const int old_selected = *selector_;
199 if (event == Event::ArrowLeft || event == Event::Character('h')) {
200 MoveSelector(-1);
201 }
202 if (event == Event::ArrowRight || event == Event::Character('l')) {
203 MoveSelector(+1);
204 }
205 if (event == Event::Tab) {
206 MoveSelectorWrap(+1);
207 }
208 if (event == Event::TabReverse) {
209 MoveSelectorWrap(-1);
210 }
211
212 *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
213 return old_selected != *selector_;
214 }
215};
216
217class TabContainer : public ContainerBase {
218 public:
219 using ContainerBase::ContainerBase;
220
221 Element OnRender() override {
222 const Component active_child = ActiveChild();
223 if (active_child) {
224 return active_child->Render();
225 }
226 return text("Empty container");
227 }
228
229 bool Focusable() const override {
230 if (children_.empty()) {
231 return false;
232 }
233 return children_[size_t(*selector_) % children_.size()]->Focusable();
234 }
235
236 bool OnMouseEvent(Event event) override {
237 return ActiveChild() && ActiveChild()->OnEvent(event);
238 }
239};
240
241class StackedContainer : public ContainerBase {
242 public:
243 explicit StackedContainer(Components children)
244 : ContainerBase(std::move(children), nullptr) {}
245
246 private:
247 Element OnRender() final {
248 Elements elements;
249 for (auto& child : children_) {
250 elements.push_back(child->Render());
251 }
252 // Reverse the order of the elements.
253 std::reverse(elements.begin(), elements.end());
254 return dbox(std::move(elements));
255 }
256
257 bool Focusable() const final {
258 for (const auto& child : children_) {
259 if (child->Focusable()) {
260 return true;
261 }
262 }
263 return false;
264 }
265
266 Component ActiveChild() final {
267 if (children_.empty()) {
268 return nullptr;
269 }
270 return children_[0];
271 }
272
273 void SetActiveChild(ComponentBase* child) final {
274 if (children_.empty()) {
275 return;
276 }
277
278 // Find `child` and put it at the beginning without change the order of the
279 // other children.
280 auto it =
281 std::find_if(children_.begin(), children_.end(),
282 [child](const Component& c) { return c.get() == child; });
283 if (it == children_.end()) {
284 return;
285 }
286 std::rotate(children_.begin(), it, it + 1);
287 }
288
289 bool OnEvent(Event event) final {
290 for (auto& child : children_) {
291 if (child->OnEvent(event)) {
292 return true;
293 }
294 }
295 return false;
296 }
297};
298
299namespace Container {
300
301/// @brief Une liste de composants, dessinés un par un verticalement et navigués
302/// verticalement en utilisant les flèches haut/bas ou les touches 'j'/'k'.
303/// @param children la liste des composants.
304/// @ingroup component
305/// @see ContainerBase
306///
307/// ### Exemple
308///
309/// ```cpp
310/// auto container = Container::Vertical({
311/// children_1,
312/// children_2,
313/// children_3,
314/// children_4,
315/// });
316/// ```
318 return Vertical(std::move(children), nullptr);
319}
320
321/// @brief Une liste de composants, dessinés un par un verticalement et navigués
322/// verticalement en utilisant les flèches haut/bas ou les touches 'j'/'k'.
323/// Ceci est utile pour implémenter un Menu par exemple.
324/// @param children la liste des composants.
325/// @param selector Une référence à l'index de l'enfant sélectionné.
326/// @ingroup component
327/// @see ContainerBase
328///
329/// ### Exemple
330///
331/// ```cpp
332/// auto container = Container::Vertical({
333/// children_1,
334/// children_2,
335/// children_3,
336/// children_4,
337/// }, &selected_children);
338/// ```
339Component Vertical(Components children, int* selector) {
340 return std::make_shared<VerticalContainer>(std::move(children), selector);
341}
342
343/// @brief Une liste de composants, dessinés un par un horizontalement et navigués
344/// horizontalement en utilisant les flèches gauche/droite ou les touches 'h'/'l'.
345/// @param children la liste des composants.
346/// @ingroup component
347/// @see ContainerBase
348///
349/// ### Exemple
350///
351/// ```cpp
352/// int selected_children = 2;
353/// auto container = Container::Horizontal({
354/// children_1,
355/// children_2,
356/// children_3,
357/// children_4,
358/// });
359/// ```
361 return Horizontal(std::move(children), nullptr);
362}
363
364/// @brief Une liste de composants, dessinés un par un horizontalement et navigués
365/// horizontalement en utilisant les flèches gauche/droite ou les touches 'h'/'l'.
366/// @param children la liste des composants.
367/// @param selector Une référence à l'index de l'enfant sélectionné.
368/// @ingroup component
369/// @see ContainerBase
370///
371/// ### Exemple
372///
373/// ```cpp
374/// int selected_children = 2;
375/// auto container = Container::Horizontal({
376/// children_1,
377/// children_2,
378/// children_3,
379/// children_4,
380/// }, selected_children);
381/// ```
382Component Horizontal(Components children, int* selector) {
383 return std::make_shared<HorizontalContainer>(std::move(children), selector);
384}
385
386/// @brief Une liste de composants, où un seul est dessiné et interagit avec à
387/// la fois. Le |selector| donne l'index du composant sélectionné. Ceci est
388/// utile pour implémenter des onglets.
389/// @param children La liste des composants.
390/// @param selector L'index de l'enfant dessiné.
391/// @ingroup component
392/// @see ContainerBase
393///
394/// ### Exemple
395///
396/// ```cpp
397/// int tab_drawn = 0;
398/// auto container = Container::Tab({
399/// children_1,
400/// children_2,
401/// children_3,
402/// children_4,
403/// }, &tab_drawn);
404/// ```
405Component Tab(Components children, int* selector) {
406 return std::make_shared<TabContainer>(std::move(children), selector);
407}
408
409/// @brief Une liste de composants à empiler les uns sur les autres.
410/// Les événements sont propagés au premier composant, puis au second s'il n'est pas
411/// géré, etc.
412/// Les composants sont dessinés dans l'ordre inverse de leur insertion.
413/// Lorsqu'un composant prend le focus, il est placé au premier plan, sans modifier l'ordre
414/// relatif des autres éléments.
415///
416/// Ceci devrait être utilisé avec le composant `Window`.
417///
418/// @param children La liste des composants.
419/// @ingroup component
420/// @see Window
421///
422/// ### Exemple
423///
424/// ```cpp
425/// auto container = Container::Stacked({
426/// children_1,
427/// children_2,
428/// children_3,
429/// children_4,
430/// });
431/// ```
433 return std::make_shared<StackedContainer>(std::move(children));
434}
435
436} // namespace Container
437
438} // namespace ftxui
static const Event TabReverse
Definition event.hpp:55
virtual bool Focusable() const
Retourne vrai si le composant contient des éléments focusables. Les composants non focusables seront ...
bool Focused() const
Indique si les éléments sont focusés par l'utilisateur. Vrai lorsque le ComponentBase est focusé par ...
static const Event PageUp
Definition event.hpp:61
void Add(Component children)
Ajoute un enfant. @param child L'enfant à attacher.
Definition component.cpp:70
static const Event ArrowUp
Definition event.hpp:41
static const Event Tab
Definition event.hpp:54
static const Event ArrowDown
Definition event.hpp:42
static const Event End
Definition event.hpp:60
static const Event Home
Definition event.hpp:59
virtual bool OnEvent(Event)
Appelé en réponse à un événement.
static const Event PageDown
Definition event.hpp:62
static const Event ArrowLeft
Definition event.hpp:39
static const Event ArrowRight
Definition event.hpp:40
Component Horizontal(Components children)
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::wstring text)
Affiche un morceau de texte unicode.
Definition text.cpp:160
Element dbox(Elements)
Empile plusieurs éléments les uns sur les autres.
Element vbox(Elements)
Un conteneur affichant les éléments verticalement un par un.
Definition vbox.cpp:96
bool Contain(int x, int y) const
Definition box.cpp:42
int y_min
Definition box.hpp:19
int y_max
Definition box.hpp:20
L'espace de noms FTXUI ftxui::Container::
L'espace de noms FTXUI ftxui::
Definition animation.hpp:10
std::shared_ptr< Node > Element
Definition elements.hpp:22
std::vector< Component > Components
Element hbox(Elements)
Un conteneur affichant les éléments horizontalement un par un.
Definition hbox.cpp:94
std::vector< Element > Elements
Definition elements.hpp:23
Decorator reflect(Box &box)
Definition reflect.cpp:43
std::shared_ptr< ComponentBase > Component