FTXUI 6.1.9
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// 本原始碼的使用受 MIT 授權條款約束,詳情請參閱 LICENSE 檔案。
3#include <algorithm> // for max, min
4#include <cstddef> // for size_t
5#include <memory> // for make_shared, __shared_ptr_access, allocator, shared_ptr, allocator_traits<>::value_type
6#include <utility> // for move
7
8#include "ftxui/component/component.hpp" // for Horizontal, Vertical, Tab
9#include "ftxui/component/component_base.hpp" // for Components, Component, ComponentBase
10#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
11#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::WheelDown, Mouse::WheelUp
12#include "ftxui/dom/elements.hpp" // for text, Elements, operator|, reflect, Element, hbox, vbox
13#include "ftxui/screen/box.hpp" // for Box
14
15namespace ftxui {
16
17class ContainerBase : public ComponentBase {
18 public:
19 ContainerBase(Components children, int* selector)
20 : selector_(selector ? selector : &selected_) {
21 for (Component& child : children) {
22 Add(std::move(child));
23 }
24 }
25
26 // Component override.
27 bool OnEvent(Event event) override {
28 if (event.is_mouse()) {
29 return OnMouseEvent(event);
30 }
31
32 if (!Focused()) {
33 return false;
34 }
35
36 if (ActiveChild() && ActiveChild()->OnEvent(event)) {
37 return true;
38 }
39
40 return EventHandler(event);
41 }
42
43 Component ActiveChild() override {
44 if (children_.empty()) {
45 return nullptr;
46 }
47
48 return children_[static_cast<size_t>(*selector_) % children_.size()];
49 }
50
51 void SetActiveChild(ComponentBase* child) override {
52 for (size_t i = 0; i < children_.size(); ++i) {
53 if (children_[i].get() == child) {
54 *selector_ = static_cast<int>(i);
55 return;
56 }
57 }
58 }
59
60 protected:
61 // Handlers
62 virtual bool EventHandler(Event /*unused*/) { return false; } // NOLINT
63
64 virtual bool OnMouseEvent(Event event) {
65 return ComponentBase::OnEvent(std::move(event));
66 }
67
68 int selected_ = 0;
69 int* selector_ = nullptr;
70
71 void MoveSelector(int dir) {
72 for (int i = *selector_ + dir; i >= 0 && i < int(children_.size());
73 i += dir) {
74 if (children_[i]->Focusable()) {
75 *selector_ = i;
76 return;
77 }
78 }
79 }
80
81 void MoveSelectorWrap(int dir) {
82 if (children_.empty()) {
83 return;
84 }
85 for (size_t offset = 1; offset < children_.size(); ++offset) {
86 const size_t i =
87 (*selector_ + offset * dir + children_.size()) % children_.size();
88 if (children_[i]->Focusable()) {
89 *selector_ = int(i);
90 return;
91 }
92 }
93 }
94};
95
96class VerticalContainer : public ContainerBase {
97 public:
98 using ContainerBase::ContainerBase;
99
100 Element OnRender() override {
101 Elements elements;
102 elements.reserve(children_.size());
103 for (auto& it : children_) {
104 elements.push_back(it->Render());
105 }
106 if (elements.empty()) {
107 return text("Empty container") | reflect(box_);
108 }
109 return vbox(std::move(elements)) | reflect(box_);
110 }
111
112 bool EventHandler(Event event) override {
113 const int old_selected = *selector_;
114 if (event == Event::ArrowUp || event == Event::Character('k')) {
115 MoveSelector(-1);
116 }
117 if (event == Event::ArrowDown || event == Event::Character('j')) {
118 MoveSelector(+1);
119 }
120 if (event == Event::PageUp) {
121 for (int i = 0; i < box_.y_max - box_.y_min; ++i) {
122 MoveSelector(-1);
123 }
124 }
125 if (event == Event::PageDown) {
126 for (int i = 0; i < box_.y_max - box_.y_min; ++i) {
127 MoveSelector(1);
128 }
129 }
130 if (event == Event::Home) {
131 for (size_t i = 0; i < children_.size(); ++i) {
132 MoveSelector(-1);
133 }
134 }
135 if (event == Event::End) {
136 for (size_t i = 0; i < children_.size(); ++i) {
137 MoveSelector(1);
138 }
139 }
140 if (event == Event::Tab) {
141 MoveSelectorWrap(+1);
142 }
143 if (event == Event::TabReverse) {
144 MoveSelectorWrap(-1);
145 }
146
147 *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
148 return old_selected != *selector_;
149 }
150
151 bool OnMouseEvent(Event event) override {
152 if (ContainerBase::OnMouseEvent(event)) {
153 return true;
154 }
155
156 if (event.mouse().button != Mouse::WheelUp &&
157 event.mouse().button != Mouse::WheelDown) {
158 return false;
159 }
160
161 if (!box_.Contain(event.mouse().x, event.mouse().y)) {
162 return false;
163 }
164
165 const int old_selected = *selector_;
166 if (event.mouse().button == Mouse::WheelUp) {
167 MoveSelector(-1);
168 }
169 if (event.mouse().button == Mouse::WheelDown) {
170 MoveSelector(+1);
171 }
172 *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
173
174 return old_selected != *selector_;
175 }
176
177 Box box_;
178};
179
180class HorizontalContainer : public ContainerBase {
181 public:
182 using ContainerBase::ContainerBase;
183
184 Element OnRender() override {
185 Elements elements;
186 elements.reserve(children_.size());
187 for (auto& it : children_) {
188 elements.push_back(it->Render());
189 }
190 if (elements.empty()) {
191 return text("Empty container");
192 }
193 return hbox(std::move(elements));
194 }
195
196 bool EventHandler(Event event) override {
197 const int old_selected = *selector_;
198 if (event == Event::ArrowLeft || event == Event::Character('h')) {
199 MoveSelector(-1);
200 }
201 if (event == Event::ArrowRight || event == Event::Character('l')) {
202 MoveSelector(+1);
203 }
204 if (event == Event::Tab) {
205 MoveSelectorWrap(+1);
206 }
207 if (event == Event::TabReverse) {
208 MoveSelectorWrap(-1);
209 }
210
211 *selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
212 return old_selected != *selector_;
213 }
214};
215
216class TabContainer : public ContainerBase {
217 public:
218 using ContainerBase::ContainerBase;
219
220 Element OnRender() override {
221 const Component active_child = ActiveChild();
222 if (active_child) {
223 return active_child->Render();
224 }
225 return text("Empty container");
226 }
227
228 bool Focusable() const override {
229 if (children_.empty()) {
230 return false;
231 }
232 return children_[size_t(*selector_) % children_.size()]->Focusable();
233 }
234
235 bool OnMouseEvent(Event event) override {
236 return ActiveChild() && ActiveChild()->OnEvent(event);
237 }
238};
239
240class StackedContainer : public ContainerBase {
241 public:
242 explicit StackedContainer(Components children)
243 : ContainerBase(std::move(children), nullptr) {}
244
245 private:
246 Element OnRender() final {
247 Elements elements;
248 for (auto& child : children_) {
249 elements.push_back(child->Render());
250 }
251 // Reverse the order of the elements.
252 std::reverse(elements.begin(), elements.end());
253 return dbox(std::move(elements));
254 }
255
256 bool Focusable() const final {
257 for (const auto& child : children_) {
258 if (child->Focusable()) {
259 return true;
260 }
261 }
262 return false;
263 }
264
265 Component ActiveChild() final {
266 if (children_.empty()) {
267 return nullptr;
268 }
269 return children_[0];
270 }
271
272 void SetActiveChild(ComponentBase* child) final {
273 if (children_.empty()) {
274 return;
275 }
276
277 // Find `child` and put it at the beginning without change the order of the
278 // other children.
279 auto it =
280 std::find_if(children_.begin(), children_.end(),
281 [child](const Component& c) { return c.get() == child; });
282 if (it == children_.end()) {
283 return;
284 }
285 std::rotate(children_.begin(), it, it + 1);
286 }
287
288 bool OnEvent(Event event) final {
289 for (auto& child : children_) {
290 if (child->OnEvent(event)) {
291 return true;
292 }
293 }
294 return false;
295 }
296};
297
298namespace Container {
299
300/// @brief 一個元件列表,垂直地一個接一個繪製,並使用上/下箭頭鍵或 'j'/'k' 鍵進行垂直導航。
301/// @param children 元件列表。
302/// @ingroup component
303/// @see ContainerBase
304///
305/// ### 範例
306///
307/// ```cpp
308/// auto container = Container::Vertical({
309/// children_1,
310/// children_2,
311/// children_3,
312/// children_4,
313/// });
314/// ```
316 return Vertical(std::move(children), nullptr);
317}
318
319/// @brief 一個元件列表,垂直地一個接一個繪製,並使用上/下箭頭鍵或 'j'/'k' 鍵進行垂直導航。
320/// 這對於實作選單很有用。
321/// @param children 元件列表。
322/// @param selector 所選子元件索引的參考。
323/// @ingroup component
324/// @see ContainerBase
325///
326/// ### 範例
327///
328/// ```cpp
329/// auto container = Container::Vertical({
330/// children_1,
331/// children_2,
332/// children_3,
333/// children_4,
334/// }, &selected_children);
335/// ```
336Component Vertical(Components children, int* selector) {
337 return std::make_shared<VerticalContainer>(std::move(children), selector);
338}
339
340/// @brief 一個元件列表,水平地一個接一個繪製,並使用左/右箭頭鍵或 'h'/'l' 鍵進行水平導航。
341/// @param children 元件列表。
342/// @ingroup component
343/// @see ContainerBase
344///
345/// ### 範例
346///
347/// ```cpp
348/// int selected_children = 2;
349/// auto container = Container::Horizontal({
350/// children_1,
351/// children_2,
352/// children_3,
353/// children_4,
354/// });
355/// ```
357 return Horizontal(std::move(children), nullptr);
358}
359
360/// @brief 一個元件列表,水平地一個接一個繪製,並使用左/右箭頭鍵或 'h'/'l' 鍵進行水平導航。
361/// @param children 元件列表。
362/// @param selector 所選子元件索引的參考。
363/// @ingroup component
364/// @see ContainerBase
365///
366/// ### 範例
367///
368/// ```cpp
369/// int selected_children = 2;
370/// auto container = Container::Horizontal({
371/// children_1,
372/// children_2,
373/// children_3,
374/// children_4,
375/// }, selected_children);
376/// ```
377Component Horizontal(Components children, int* selector) {
378 return std::make_shared<HorizontalContainer>(std::move(children), selector);
379}
380
381/// @brief 一個元件列表,一次只繪製一個並與之互動。|selector| 給出所選元件的索引。這對於實作分頁很有用。
382/// @param children 元件列表。
383/// @param selector 繪製中的子元件索引。
384/// @ingroup component
385/// @see ContainerBase
386///
387/// ### 範例
388///
389/// ```cpp
390/// int tab_drawn = 0;
391/// auto container = Container::Tab({
392/// children_1,
393/// children_2,
394/// children_3,
395/// children_4,
396/// }, &tab_drawn);
397/// ```
398Component Tab(Components children, int* selector) {
399 return std::make_shared<TabContainer>(std::move(children), selector);
400}
401
402/// @brief 一個元件列表,將彼此堆疊。
403/// 事件會傳播到第一個元件,如果未處理則傳播到第二個,依此類推。
404/// 元件以給定的相反順序繪製。
405/// 當一個元件獲得焦點時,它會被放到最前面,而不改變其他元素的相對順序。
406///
407/// 這應該與 `Window` 元件一起使用。
408///
409/// @param children 元件列表。
410/// @ingroup component
411/// @see Window
412///
413/// ### 範例
414///
415/// ```cpp
416/// auto container = Container::Stacked({
417/// children_1,
418/// children_2,
419/// children_3,
420/// children_4,
421/// });
422/// ```
424 return std::make_shared<StackedContainer>(std::move(children));
425}
426
427} // namespace Container
428
429} // namespace ftxui
static const Event TabReverse
Definition event.hpp:54
virtual bool Focusable() const
當組件包含可聚焦元素時返回 true。 使用鍵盤導航時,不可聚焦的組件將被跳過。
bool Focused() const
返回元素是否被使用者聚焦。 當 ComponentBase 被使用者聚焦時返回 true。當一個元素及其所有祖先都是其父項的 ActiveChild() 並且它是 Focusable() 時,該元素被聚...
static const Event PageUp
Definition event.hpp:60
void Add(Component children)
新增一個子項。 @param child 要附加的子項。
Definition component.cpp:69
static const Event ArrowUp
Definition event.hpp:40
static const Event Tab
Definition event.hpp:53
static const Event ArrowDown
Definition event.hpp:41
static const Event End
Definition event.hpp:59
static const Event Home
Definition event.hpp:58
virtual bool OnEvent(Event)
回應事件時呼叫。
static const Event PageDown
Definition event.hpp:61
static const Event ArrowLeft
Definition event.hpp:38
static const Event ArrowRight
Definition event.hpp:39
Component Horizontal(Components children)
一個元件列表,水平地一個接一個繪製,並使用左/右箭頭鍵或 'h'/'l' 鍵進行水平導航。
Component Vertical(Components children)
一個元件列表,垂直地一個接一個繪製,並使用上/下箭頭鍵或 'j'/'k' 鍵進行垂直導航。
Component Stacked(Components children)
一個元件列表,將彼此堆疊。 事件會傳播到第一個元件,如果未處理則傳播到第二個,依此類推。 元件以給定的相反順序繪製。 當一個元件獲得焦點時,它會被放到最前面,而不改變其他元素的相對順序。
Component Tab(Components children, int *selector)
一個元件列表,一次只繪製一個並與之互動。|selector| 給出所選元件的索引。這對於實作分頁很有用。
Element text(std::wstring text)
顯示一段 Unicode 文字。
Definition text.cpp:160
Element dbox(Elements)
將多個元素堆疊在一起。
Element vbox(Elements)
一個垂直一個接一個顯示元素的容器。
Definition vbox.cpp:95
bool Contain(int x, int y) const
Definition box.cpp:42
int y_min
Definition box.hpp:17
int y_max
Definition box.hpp:18
FTXUI ftxui::Container:: 命名空間
FTXUI 的 ftxui:: 命名空間
Definition animation.hpp:10
std::shared_ptr< Node > Element
Definition elements.hpp:22
std::vector< Component > Components
Element hbox(Elements)
一個逐一水平顯示元素的容器。
Definition hbox.cpp:94
std::vector< Element > Elements
Definition elements.hpp:23
Decorator reflect(Box &box)
Definition reflect.cpp:42
std::shared_ptr< ComponentBase > Component