FTXUI 6.1.9
C++ functional terminal UI.
载入中...
搜索中...
未找到
container.cpp
浏览该文件的文档.
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 // Component override.
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 // Handlers
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 一个组件列表,垂直逐个绘制,并使用上/下箭头键或“j”/“k”键垂直导航。
302/// @param children 组件列表。
303/// @ingroup component
304/// @see ContainerBase
305///
306/// ### Example
307///
308/// ```cpp
309/// auto container = Container::Vertical({
310/// children_1,
311/// children_2,
312/// children_3,
313/// children_4,
314/// });
315/// ```
317 return Vertical(std::move(children), nullptr);
318}
319
320/// @brief 一个组件列表,垂直逐个绘制,并使用上/下箭头键或“j”/“k”键垂直导航。
321/// 这对于实现菜单很有用。
322/// @param children 组件列表。
323/// @param selector 对所选子组件索引的引用。
324/// @ingroup component
325/// @see ContainerBase
326///
327/// ### Example
328///
329/// ```cpp
330/// auto container = Container::Vertical({
331/// children_1,
332/// children_2,
333/// children_3,
334/// children_4,
335/// }, &selected_children);
336/// ```
337Component Vertical(Components children, int* selector) {
338 return std::make_shared<VerticalContainer>(std::move(children), selector);
339}
340
341/// @brief 一个组件列表,水平逐个绘制,并使用左/右箭头键或“h”/“l”键水平导航。
342/// @param children 组件列表。
343/// @ingroup component
344/// @see ContainerBase
345///
346/// ### Example
347///
348/// ```cpp
349/// int selected_children = 2;
350/// auto container = Container::Horizontal({
351/// children_1,
352/// children_2,
353/// children_3,
354/// children_4,
355/// });
356/// ```
358 return Horizontal(std::move(children), nullptr);
359}
360
361/// @brief 一个组件列表,水平逐个绘制,并使用左/右箭头键或“h”/“l”键水平导航。
362/// @param children 组件列表。
363/// @param selector 对所选子组件索引的引用。
364/// @ingroup component
365/// @see ContainerBase
366///
367/// ### Example
368///
369/// ```cpp
370/// int selected_children = 2;
371/// auto container = Container::Horizontal({
372/// children_1,
373/// children_2,
374/// children_3,
375/// children_4,
376/// }, selected_children);
377/// ```
378Component Horizontal(Components children, int* selector) {
379 return std::make_shared<HorizontalContainer>(std::move(children), selector);
380}
381
382/// @brief 一个组件列表,一次只绘制并与其交互一个。|selector| 提供所选组件的索引。这对于实现选项卡很有用。
383/// @param children 组件列表。
384/// @param selector 绘制的子组件的索引。
385/// @ingroup component
386/// @see ContainerBase
387///
388/// ### Example
389///
390/// ```cpp
391/// int tab_drawn = 0;
392/// auto container = Container::Tab({
393/// children_1,
394/// children_2,
395/// children_3,
396/// children_4,
397/// }, &tab_drawn);
398/// ```
399Component Tab(Components children, int* selector) {
400 return std::make_shared<TabContainer>(std::move(children), selector);
401}
402
403/// @brief 一个组件列表,它们相互堆叠。
404/// 事件传播到第一个组件,如果未处理,则传播到第二个,依此类推。
405/// 组件以给定顺序的相反顺序绘制。
406/// 当一个组件获得焦点时,它会被置于最前面,而不改变其他元素的相对顺序。
407///
408/// 这应该与 `Window` 组件一起使用。
409///
410/// @param children 组件列表。
411/// @ingroup component
412/// @see Window
413///
414/// ### Example
415///
416/// ```cpp
417/// auto container = Container::Stacked({
418/// children_1,
419/// children_2,
420/// children_3,
421/// children_4,
422/// });
423/// ```
425 return std::make_shared<StackedContainer>(std::move(children));
426}
427
428} // namespace Container
429
430} // namespace ftxui
static const Event TabReverse
virtual bool Focusable() const
当组件包含可聚焦元素时返回 true。 使用键盘导航时,不可聚焦的组件将被跳过。
bool Focused() const
返回元素是否被用户聚焦。 当 ComponentBase 被用户聚焦时返回 true。当一个元素及其所有祖先都是其父级的 ActiveChild() 并且它是 Focusable() 时,该元素被聚焦。
static const Event PageUp
void Add(Component children)
添加一个子项。 @param child 要附加的子项。
static const Event ArrowUp
static const Event Tab
static const Event ArrowDown
static const Event End
static const Event Home
virtual bool OnEvent(Event)
响应事件时调用。
static const Event PageDown
static const Event ArrowLeft
static const Event ArrowRight
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文本。
Element dbox(Elements)
将多个元素堆叠在一起。
Element vbox(Elements)
垂直一个接一个显示元素的容器。
bool Contain(int x, int y) const
定义 box.cpp:42
FTXUI ftxui::Container:: 命名空间
#include "ftxui/component/component_base.hpp" // 用于 ComponentBase
std::shared_ptr< Node > Element
std::vector< Component > Components
Element hbox(Elements)
一个按水平顺序逐一显示元素的容器。
std::vector< Element > Elements
Decorator reflect(Box &box)
std::shared_ptr< ComponentBase > Component