FTXUI 7.0.3
C++ functional terminal UI.
Loading...
Searching...
No Matches
component.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 find_if
5#include <cassert> // for assert
6#include <cstddef> // for size_t
7#include <iterator> // for begin, end
8#include <memory> // for unique_ptr, make_unique
9#include <utility> // for move
10#include <vector> // for vector, __alloc_traits<>::value_type
11
12#include "ftxui/component/app.hpp" // for Component, App
13#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
15#include "ftxui/component/component_base.hpp" // for ComponentBase, Components
16#include "ftxui/component/event.hpp" // for Event
17#include "ftxui/dom/elements.hpp" // for text, Element
18#include "ftxui/dom/node.hpp" // for Node, Elements
19#include "ftxui/screen/box.hpp" // for Box
20
21namespace ftxui::animation {
22class Params;
23} // namespace ftxui::animation
24
25namespace ftxui {
26
27namespace {
28class CaptureMouseImpl : public CapturedMouseInterface {};
29} // namespace
30
31struct ComponentBase::Impl {
32 Components children;
33 ComponentBase* parent = nullptr;
34 bool in_render = false;
35};
36
37ComponentBase::ComponentBase() : impl_(std::make_unique<Impl>()) {}
38
39ComponentBase::ComponentBase(Components children)
40 : impl_(std::make_unique<Impl>()) {
41 impl_->children = std::move(children);
42}
43
44ComponentBase::~ComponentBase() {
45 DetachAllChildren();
46}
47
48Components& ComponentBase::children() {
49 return impl_->children;
50}
51
52const Components& ComponentBase::children() const {
53 return impl_->children;
54}
55
56/// @brief 返回父 ComponentBase,如果沒有則返回空。
57/// @see Detach
58/// @see Parent
59ComponentBase* ComponentBase::Parent() const {
60 return impl_->parent;
61}
62
63/// @brief 存取索引 `i` 處的子項。
64Component& ComponentBase::ChildAt(size_t i) {
65 assert(i < ChildCount()); // NOLINT
66 return impl_->children[i];
67}
68
69/// @brief 返回子項的數量。
70size_t ComponentBase::ChildCount() const {
71 return impl_->children.size();
72}
73
74/// @brief 返回組件在其父項中的索引。如果沒有父項則返回 -1。
75int ComponentBase::Index() const {
76 if (impl_->parent == nullptr) {
77 return -1;
78 }
79 int index = 0;
80 for (const Component& child : impl_->parent->impl_->children) {
81 if (child.get() == this) {
82 return index;
83 }
84 index++;
85 }
86 return -1; // 不會執行到此處。
87}
88
89/// @brief 新增一個子項目。
90/// @param child 要附加的子項目。
91void ComponentBase::Add(Component child) {
92 child->Detach();
93 child->impl_->parent = this;
94 impl_->children.push_back(std::move(child));
95}
96
97/// @brief 將此子項從其父項中分離。
98/// @see Detach
99/// @see Parent
100void ComponentBase::Detach() {
101 if (impl_->parent == nullptr) {
102 return;
103 }
104 auto it = std::find_if(std::begin(impl_->parent->impl_->children), // NOLINT
105 std::end(impl_->parent->impl_->children), //
106 [this](const Component& that) { //
107 return this == that.get();
108 });
109 ComponentBase* parent = impl_->parent;
110 impl_->parent = nullptr;
111 parent->impl_->children.erase(it); // 可能會刪除 |this|。
112}
113
114/// @brief 移除所有子項。
115void ComponentBase::DetachAllChildren() {
116 while (!impl_->children.empty()) {
117 impl_->children[0]->Detach();
118 }
119}
120
121/// @brief 繪製組件。
122/// 建構一個 ftxui::Element,用於在表示此 ftxui::ComponentBase 的 ftxui::Screen 上繪製。請覆寫 OnRender() 以修改渲染。
123Element ComponentBase::Render() {
124 // 有些使用者可能會在 `T::OnRender()` 中呼叫
125 // `ComponentBase::Render()`。為了避免無限遞迴,我們使用一個旗標。
126 if (impl_->in_render) {
127 return ComponentBase::OnRender();
128 }
129
130 impl_->in_render = true;
131 Element element = OnRender();
132 impl_->in_render = false;
133
134 class Wrapper : public Node {
135 public:
136 bool active_ = false;
137 bool focused_ = false;
138
139 Wrapper(Element child, bool active, bool focused)
140 : Node({std::move(child)}), active_(active), focused_(focused) {}
141
142 void SetBox(Box box) override {
143 Node::SetBox(box);
144 children_[0]->SetBox(box);
145 }
146
147 void ComputeRequirement() override {
148 Node::ComputeRequirement();
149 requirement_.focused.component_active = active_;
150 requirement_.focused.component_focused = focused_;
151 }
152 };
153
154 return std::make_shared<Wrapper>(std::move(element), Active(), Focused());
155}
156
157/// @brief 繪製組件。
158/// 建構一個 ftxui::Element,用於在表示此 ftxui::ComponentBase 的 ftxi::Screen 上繪製。此函數旨在被覆寫。
159Element ComponentBase::OnRender() {
160 if (impl_->children.size() == 1) {
161 return impl_->children.front()->Render();
162 }
163
164 return text("Not implemented component");
165}
166
167/// @brief 回應事件時呼叫。
168/// @param event 事件。
169/// @return 當事件已處理時返回 true。
170/// 預設實作會在每個子項上呼叫 OnEvent,直到其中一個返回 true。如果都沒有返回 true,則返回 false。
171bool ComponentBase::OnEvent(Event event) { // NOLINT
172 for (Component& child : impl_->children) { // NOLINT
173 if (child->OnEvent(event)) {
174 return true;
175 }
176 }
177 return false;
178}
179
180/// @brief 回應動畫事件時呼叫。
181/// @param params 動畫的參數
182/// 預設實作會將事件分派給每個子項。
183void ComponentBase::OnAnimation(animation::Params& params) {
184 for (const Component& child : impl_->children) {
185 child->OnAnimation(params);
186 }
187}
188
189/// @brief 返回目前活躍的子項。
190/// @return 目前活躍的子項。
191Component ComponentBase::ActiveChild() {
192 for (auto& child : impl_->children) {
193 if (child->Focusable()) {
194 return child;
195 }
196 }
197 return nullptr;
198}
199
200/// @brief 當組件包含可聚焦元素時返回 true。
201/// 使用鍵盤導航時,不可聚焦的組件將被跳過。
202bool ComponentBase::Focusable() const {
203 for (const Component& child : impl_->children) { // NOLINT
204 if (child->Focusable()) {
205 return true;
206 }
207 }
208 return false;
209}
210
211/// @brief 返回該元素是否為其父項的目前活躍子項。
212bool ComponentBase::Active() const {
213 return impl_->parent == nullptr || impl_->parent->ActiveChild().get() == this;
214}
215
216/// @brief 返回元素是否被使用者聚焦。
217/// 當 ComponentBase 被使用者聚焦時返回 true。當一個元素及其所有祖先都是其父項的 ActiveChild() 並且它是 Focusable() 時,該元素被聚焦。
218bool ComponentBase::Focused() const {
219 const auto* current = this;
220 while (current && current->Active()) {
221 current = current->impl_->parent;
222 }
223 return !current && Focusable();
224}
225
226/// @brief 使 |child| 成為「活躍」的子項。
227/// @param child 要變為活躍的子項。
228void ComponentBase::SetActiveChild([[maybe_unused]] ComponentBase* child) {}
229
230/// @brief 使 |child| 成為「活躍」的子項。
231/// @param child 要變為活躍的子項。
232void ComponentBase::SetActiveChild(Component child) { // NOLINT
233 SetActiveChild(child.get());
234}
235
236/// @brief 配置所有祖先以將焦點給予此組件。
237void ComponentBase::TakeFocus() {
238 ComponentBase* child = this;
239 while (ComponentBase* parent = child->impl_->parent) {
240 parent->SetActiveChild(child);
241 child = parent;
242 }
243}
244
245/// @brief 如果可用,則取得 CapturedMouse。它們只有一個組件。它代表一個優先於其他組件的組件。
246/// @param event 事件
247CapturedMouse ComponentBase::CaptureMouse(const Event& event) { // NOLINT
248 if (event.screen_) {
249 return event.screen_->CaptureMouse();
250 }
251 return std::make_unique<CaptureMouseImpl>();
252}
253
254void ComponentBase::Reserved1() {}
255void ComponentBase::Reserved2() {}
256void ComponentBase::Reserved3() {}
257void ComponentBase::Reserved4() {}
258void ComponentBase::Reserved5() {}
259void ComponentBase::Reserved6() {}
260void ComponentBase::Reserved7() {}
261void ComponentBase::Reserved8() {}
262
263} // namespace ftxui
FTXUI ftxui:: 命名空間
Definition animation.hpp:11
std::unique_ptr< CapturedMouseInterface > CapturedMouse
std::shared_ptr< Node > Element
Definition elements.hpp:25
std::vector< Component > Components
const Element & element
Definition node.hpp:92
std::shared_ptr< ComponentBase > Component
Definition app.hpp:23