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 // 元件覆寫。
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 // 處理函式
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 // 反轉元素的順序。
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 // 找出 `child` 並將它放到開頭,而不改變
293 // 其他子項目的順序。
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 一個元件列表,垂直地一個接一個繪製,並使用上/下箭頭鍵或 'j'/'k' 鍵進行垂直導航。
316/// @param children 元件列表。
317/// @ingroup component
318/// @see ContainerBase
319///
320/// ### 範例
321///
322/// ```cpp
323/// auto container = Container::Vertical({
324/// children_1,
325/// children_2,
326/// children_3,
327/// children_4,
328/// });
329/// ```
331 return Vertical(std::move(children), nullptr);
332}
333
334/// @brief 一個元件清單,垂直逐一繪製,並使用上下方向鍵
335/// 或 'j'/'k' 鍵進行垂直導覽。
336/// 這對於實作選單(Menu)非常有用。
337/// @param children 元件清單。
338/// @param selector 選定子項目索引的參照。
339/// @ingroup component
340/// @see ContainerBase
341///
342/// ### 範例
343///
344/// ```cpp
345/// int selected_children = 2;
346/// auto container = Container::Vertical({
347/// children_1,
348/// children_2,
349/// children_3,
350/// children_4,
351/// }, &selected_children);
352/// ```
354 return std::make_shared<VerticalContainer>(std::move(children), selector);
355}
356
357/// @brief 一個元件清單,水平逐一繪製,並使用左右方向鍵
358/// 或 'h'/'l' 鍵進行水平導覽。
359/// @param children 元件清單。
360/// @ingroup component
361/// @see ContainerBase
362///
363/// ### 範例
364///
365/// ```cpp
366/// auto container = Container::Horizontal({
367/// children_1,
368/// children_2,
369/// children_3,
370/// children_4,
371/// });
372/// ```
374 return Horizontal(std::move(children), nullptr);
375}
376
377/// @brief 一個元件列表,水平地一個接一個繪製,並使用左/右箭頭鍵或 'h'/'l' 鍵進行水平導航。
378/// @param children 元件列表。
379/// @param selector 所選子元件索引的參考。
380/// @ingroup component
381/// @see ContainerBase
382///
383/// ### 範例
384///
385/// ```cpp
386/// int selected_children = 2;
387/// auto container = Container::Horizontal({
388/// children_1,
389/// children_2,
390/// children_3,
391/// children_4,
392/// }, selected_children);
393/// ```
395 return std::make_shared<HorizontalContainer>(std::move(children), selector);
396}
397
398/// @brief 一個元件列表,一次只繪製一個並與之互動。|selector| 給出所選元件的索引。這對於實作分頁很有用。
399/// @param children 元件列表。
400/// @param selector 繪製中的子元件索引。
401/// @ingroup component
402/// @see ContainerBase
403///
404/// ### 範例
405///
406/// ```cpp
407/// int tab_drawn = 0;
408/// auto container = Container::Tab({
409/// children_1,
410/// children_2,
411/// children_3,
412/// children_4,
413/// }, &tab_drawn);
414/// ```
416 return std::make_shared<TabContainer>(std::move(children), selector);
417}
418
419/// @brief 一個元件列表,將彼此堆疊。
420/// 事件會傳播到第一個元件,如果未處理則傳播到第二個,依此類推。
421/// 元件以給定的相反順序繪製。
422/// 當一個元件獲得焦點時,它會被放到最前面,而不改變其他元素的相對順序。
423///
424/// 這應該與 `Window` 元件一起使用。
425///
426/// @param children 元件列表。
427/// @ingroup component
428/// @see Window
429///
430/// ### 範例
431///
432/// ```cpp
433/// auto container = Container::Stacked({
434/// children_1,
435/// children_2,
436/// children_3,
437/// children_4,
438/// });
439/// ```
441 return std::make_shared<StackedContainer>(std::move(children));
442}
443
444} // namespace Container
445
446} // namespace ftxui
Component Horizontal(Components children, int *selector)
一個元件列表,水平地一個接一個繪製,並使用左/右箭頭鍵或 'h'/'l' 鍵進行水平導航。
Component Vertical(Components children)
一個元件列表,垂直地一個接一個繪製,並使用上/下箭頭鍵或 'j'/'k' 鍵進行垂直導航。
Component Stacked(Components children)
一個元件列表,將彼此堆疊。 事件會傳播到第一個元件,如果未處理則傳播到第二個,依此類推。 元件以給定的相反順序繪製。 當一個元件獲得焦點時,它會被放到最前面,而不改變其他元素的相對順序。
Component Tab(Components children, int *selector)
一個元件列表,一次只繪製一個並與之互動。|selector| 給出所選元件的索引。這對於實作分頁很有用。
Element text(std::string_view text)
顯示一段 UTF8 編碼的 Unicode 文字。
Decorator size(WidthOrHeight direction, Constraint constraint, int value)
對元素的大小套用限制。
Element dbox(Elements children_)
將多個元素堆疊在一起。
Element vbox(Elements children)
一個垂直一個接一個顯示元素的容器。
Definition vbox.cpp:96
FTXUI ftxui::Container:: 命名空間
FTXUI ftxui:: 命名空間
Definition animation.hpp:11
std::shared_ptr< Node > Element
Definition elements.hpp:25
std::vector< Component > Components
Element hbox(Elements children)
一個逐一水平顯示元素的容器。
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