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 auto it =
294 std::find_if(children().begin(), children().end(), // NOLINT
295 [child](const Component& c) { return c.get() == child; });
296 if (it == children().end()) {
297 return;
298 }
299 std::rotate(children().begin(), it, it + 1);
300 }
301
302 bool OnEvent(Event event) final {
303 for (auto& child : children()) {
304 if (child->OnEvent(event)) {
305 return true;
306 }
307 }
308 return false;
309 }
310};
311
312namespace Container {
313
314/// @brief コンポーネントのリスト。垂直方向に1つずつ描画され、上下の矢印キーまたは'j'/'k'キーを使用して垂直方向にナビゲートされます。
315/// @param children コンポーネントのリスト。
316/// @ingroup component
317/// @see ContainerBase
318///
319/// ### Example
320///
321/// ```cpp
322/// auto container = Container::Vertical({
323/// children_1,
324/// children_2,
325/// children_3,
326/// children_4,
327/// });
328/// ```
330 return Vertical(std::move(children), nullptr);
331}
332
333/// @brief 上下矢印キーまたは 'j'/'k' キーを使って垂直方向に
334/// ナビゲートされる、垂直に一つずつ描画されるコンポーネントのリスト。
335/// これは例えばメニューの実装に便利です。
336/// @param children コンポーネントのリスト。
337/// @param selector 選択された子への参照。
338/// @ingroup component
339/// @see ContainerBase
340///
341/// ### Example
342///
343/// ```cpp
344/// int selected_children = 2;
345/// auto container = Container::Vertical({
346/// children_1,
347/// children_2,
348/// children_3,
349/// children_4,
350/// }, &selected_children);
351/// ```
353 return std::make_shared<VerticalContainer>(std::move(children), selector);
354}
355
356/// @brief 左右矢印キーまたは 'h'/'l' キーを使って水平方向に
357/// ナビゲートされる、水平に一つずつ描画されるコンポーネントのリスト。
358/// @param children コンポーネントのリスト。
359/// @ingroup component
360/// @see ContainerBase
361///
362/// ### Example
363///
364/// ```cpp
365/// auto container = Container::Horizontal({
366/// children_1,
367/// children_2,
368/// children_3,
369/// children_4,
370/// });
371/// ```
373 return Horizontal(std::move(children), nullptr);
374}
375
376/// @brief 水平方向に左右矢印キーまたは 'h'/'l' キーで操作される、1つずつ
377/// 水平に描画されるコンポーネントのリスト。
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 一度に1つだけ描画され、操作されるコンポーネントのリスト。
399/// |selector| は選択されたコンポーネントのインデックスを示します。これは
400/// タブを実装するのに便利です。
401/// @param children コンポーネントのリスト。
402/// @param selector 描画される子要素のインデックス。
403/// @ingroup component
404/// @see ContainerBase
405///
406/// ### 例
407///
408/// ```cpp
409/// int tab_drawn = 0;
410/// auto container = Container::Tab({
411/// children_1,
412/// children_2,
413/// children_3,
414/// children_4,
415/// }, &tab_drawn);
416/// ```
418 return std::make_shared<TabContainer>(std::move(children), selector);
419}
420
421/// @brief 互いに重ねて配置されるコンポーネントのリスト。
422/// イベントは最初のコンポーネントに伝播され、処理されなければ次に、
423/// というように伝播していきます。
424/// コンポーネントは、与えられた順序の逆順で描画されます。
425/// コンポーネントがフォーカスを取得すると、他の要素の相対的な順序を変えずに
426/// 最前面に配置されます。
427///
428/// これは `Window` コンポーネントと一緒に使用する必要があります。
429///
430/// @param children コンポーネントのリスト。
431/// @ingroup component
432/// @see Window
433///
434/// ### 例
435///
436/// ```cpp
437/// auto container = Container::Stacked({
438/// children_1,
439/// children_2,
440/// children_3,
441/// children_4,
442/// });
443/// ```
445 return std::make_shared<StackedContainer>(std::move(children));
446}
447
448} // namespace Container
449
450} // namespace ftxui
Component Horizontal(Components children, int *selector)
水平方向に左右矢印キーまたは 'h'/'l' キーで操作される、1つずつ 水平に描画されるコンポーネントのリスト。
Component Vertical(Components children)
コンポーネントのリスト。垂直方向に1つずつ描画され、上下の矢印キーまたは'j'/'k'キーを使用して垂直方向にナビゲートされます。
Component Stacked(Components children)
互いに重ねて配置されるコンポーネントのリスト。 イベントは最初のコンポーネントに伝播され、処理されなければ次に、 というように伝播していきます。 コンポーネントは、与えられた順序の逆順で描画されます。 ...
Component Tab(Components children, int *selector)
一度に1つだけ描画され、操作されるコンポーネントのリスト。 |selector| は選択されたコンポーネントのインデックスを示します。これは タブを実装するのに便利です。
Element text(std::string_view text)
UTF8エンコードされたユニコードテキストを表示します。
Decorator size(WidthOrHeight direction, Constraint constraint, int value)
要素のサイズに制約を適用する。
Element dbox(Elements children_)
複数の要素を互いに重ねて表示する。
Element vbox(Elements children)
要素を縦に一つずつ表示するコンテナ。
Definition vbox.cpp:95
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)
要素を水平方向に1つずつ表示するコンテナ。
Definition hbox.cpp:93
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