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// このソースコードの使用は、LICENSEファイルにあるMITライセンスによって管理されています。
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 // ハンドラ
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 // 要素の順序を反転させる。
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 // `child`を見つけて、他の子の相対的な順序を変更せずに先頭に配置します。
278 auto it =
279 std::find_if(children_.begin(), children_.end(),
280 [child](const Component& c) { return c.get() == child; });
281 if (it == children_.end()) {
282 return;
283 }
284 std::rotate(children_.begin(), it, it + 1);
285 }
286
287 bool OnEvent(Event event) final {
288 for (auto& child : children_) {
289 if (child->OnEvent(event)) {
290 return true;
291 }
292 }
293 return false;
294 }
295};
296
297namespace Container {
298
299/// @brief コンポーネントのリスト。垂直方向に1つずつ描画され、上下の矢印キーまたは'j'/'k'キーを使用して垂直方向にナビゲートされます。
300/// @param children コンポーネントのリスト。
301/// @ingroup component
302/// @see ContainerBase
303///
304/// ### Example
305///
306/// ```cpp
307/// auto container = Container::Vertical({
308/// children_1,
309/// children_2,
310/// children_3,
311/// children_4,
312/// });
313/// ```
315 return Vertical(std::move(children), nullptr);
316}
317
318/// @brief コンポーネントのリスト。垂直方向に1つずつ描画され、上下の矢印キーまたは'j'/'k'キーを使用して垂直方向にナビゲートされます。
319/// これは例えばMenuを実装するのに便利です。
320/// @param children コンポーネントのリスト。
321/// @param selector 選択された子のインデックスへの参照。
322/// @ingroup component
323/// @see ContainerBase
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 コンポーネントのリスト。水平方向に1つずつ描画され、左右の矢印キーまたは'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 コンポーネントのリスト。水平方向に1つずつ描画され、左右の矢印キーまたは'h'/'l'キーを使用して水平方向にナビゲートされます。
362/// @param children コンポーネントのリスト。
363/// @param selector 選択された子のインデックスへの参照。
364/// @ingroup component
365/// @see ContainerBase
366/// @ingroup component
367/// @see ContainerBase
368///
369/// ### Example
370///
371/// ```cpp
372/// int selected_children = 2;
373/// auto container = Container::Horizontal({
374/// children_1,
375/// children_2,
376/// children_3,
377/// children_4,
378/// }, selected_children);
379/// ```
380Component Horizontal(Components children, int* selector) {
381 return std::make_shared<HorizontalContainer>(std::move(children), selector);
382}
383
384/// @brief コンポーネントのリスト。一度に1つだけ描画され、操作されます。|selector|は選択されたコンポーネントのインデックスを提供します。これはタブを実装するのに便利です。
385/// @param children コンポーネントのリスト。
386/// @param selector 描画される子のインデックス。
387/// @ingroup component
388/// @see ContainerBase
389/// @ingroup component
390/// @see ContainerBase
391///
392/// ### Example
393///
394/// ```cpp
395/// int tab_drawn = 0;
396/// auto container = Container::Tab({
397/// children_1,
398/// children_2,
399/// children_3,
400/// children_4,
401/// }, &tab_drawn);
402/// ```
403Component Tab(Components children, int* selector) {
404 return std::make_shared<TabContainer>(std::move(children), selector);
405}
406
407/// @brief 互いの上にスタックされるコンポーネントのリスト。
408/// イベントは、最初のコンポーネントに伝播され、処理されない場合は2番目のコンポーネントに伝播されます。
409/// コンポーネントは与えられた順序とは逆の順序で描画されます。
410/// コンポーネントがフォーカスを取得すると、他の要素の相対的な順序を変更せずに前面に配置されます。
411///
412/// これは`Window`コンポーネントと一緒に使用する必要があります。
413///
414/// @param children コンポーネントのリスト。
415/// @ingroup component
416/// @see Window
417/// @ingroup component
418/// @see Window
419///
420/// ### Example
421///
422/// ```cpp
423/// auto container = Container::Stacked({
424/// children_1,
425/// children_2,
426/// children_3,
427/// children_4,
428/// });
429/// ```
431 return std::make_shared<StackedContainer>(std::move(children));
432}
433
434} // namespace Container
435
436} // namespace ftxui
static const Event TabReverse
Definition event.hpp:55
virtual bool Focusable() const
コンポーネントがフォーカス可能な要素を含んでいる場合にtrueを返します。 フォーカス不可能なコンポーネントは、キーボードでナビゲートする際にスキップされます。
bool Focused() const
要素がユーザーによってフォーカスされているかどうかを返します。 ComponentBaseがユーザーによってフォーカスされている場合にtrueを返します。要素は、そのすべての子孫が親のActiveChi...
static const Event PageUp
Definition event.hpp:61
void Add(Component children)
子を追加します。 @param child 添付する子。
Definition component.cpp:69
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)
イベントに応じて呼び出されます。
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)
コンポーネントのリスト。水平方向に1つずつ描画され、左右の矢印キーまたは'h'/'l'キーを使用して水平方向にナビゲートされます。
Component Vertical(Components children)
コンポーネントのリスト。垂直方向に1つずつ描画され、上下の矢印キーまたは'j'/'k'キーを使用して垂直方向にナビゲートされます。
Component Stacked(Components children)
互いの上にスタックされるコンポーネントのリスト。 イベントは、最初のコンポーネントに伝播され、処理されない場合は2番目のコンポーネントに伝播されます。 コンポーネントは与えられた順序とは逆の順序で描画さ...
Component Tab(Components children, int *selector)
コンポーネントのリスト。一度に1つだけ描画され、操作されます。|selector|は選択されたコンポーネントのインデックスを提供します。これはタブを実装するのに便利です。
Element text(std::wstring text)
ユニコードテキストを表示します。
Definition text.cpp:160
Element dbox(Elements)
複数の要素を重ねて表示します。
Element vbox(Elements)
要素を縦に一つずつ表示するコンテナ。
Definition vbox.cpp:94
bool Contain(int x, int y) const
Definition box.cpp:41
int y_min
Definition box.hpp:17
int y_max
Definition box.hpp:18
FTXUIのftxui::Container::名前空間
FTXUI ftxui:: 名前空間
Definition animation.hpp:9
std::shared_ptr< Node > Element
Definition elements.hpp:21
std::vector< Component > Components
Element hbox(Elements)
要素を水平方向に1つずつ表示するコンテナ。
Definition hbox.cpp:93
std::vector< Element > Elements
Definition elements.hpp:22
Decorator reflect(Box &box)
Definition reflect.cpp:42
std::shared_ptr< ComponentBase > Component