FTXUI 7.0.3
C++ functional terminal UI.
Loading...
Searching...
No Matches
node.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 <ftxui/screen/box.hpp> // for Box
5#include <string>
6#include <utility> // for move
7
8#include <cstddef>
9#include "ftxui/dom/node.hpp"
10#include "ftxui/dom/selection.hpp" // for Selection
11#include "ftxui/screen/screen.hpp" // for Screen
12#include "ftxui/screen/terminal.hpp" // for GetQuirks
13
14namespace ftxui {
15
16Node::Node() = default;
17Node::Node(Elements children) : children_(std::move(children)) {}
18Node::~Node() = default;
19
20/// @brief 計算元素所需的空間大小。
21void Node::ComputeRequirement() {
22 if (children_.empty()) {
23 return;
24 }
25 for (auto& child : children_) {
26 child->ComputeRequirement();
27 }
28
29 // 預設情況下,需求是第一個子元素的需求。
30 requirement_ = children_[0]->requirement();
31
32 // 傳遞聚焦需求。
33 for (size_t i = 1; i < children_.size(); ++i) {
34 if (requirement_.focused.Prefer(children_[i]->requirement().focused)) {
35 requirement_.focused = children_[i]->requirement().focused;
36 }
37 }
38}
39
40/// @brief 為元素分配繪圖位置和尺寸。
41void Node::SetBox(Box box) {
42 box_ = box;
43}
44
45/// @brief 計算元素的選取範圍。
46void Node::Select(Selection& selection) {
47 // 如果此節點的 box_ 與選取範圍沒有交集,則不進行選取。
48 if (Box::Intersection(selection.GetBox(), box_).IsEmpty()) {
49 return;
50 }
51
52 // 預設情況下,我們將選取交由子元素處理。
53 for (auto& child : children_) {
54 child->Select(selection);
55 }
56}
57
58/// @brief 在 ftxui::Screen 上顯示元素。
59void Node::Render(Screen& screen) {
60 for (auto& child : children_) {
61 child->Render(screen);
62 }
63}
64
65void Node::Check(Status* status) {
66 for (auto& child : children_) {
67 child->Check(status);
68 }
69 status->need_iteration |= (status->iteration == 0);
70}
71
72std::string Node::GetSelectedContent(Selection& selection) {
73 std::string content;
74
75 for (auto& child : children_) {
76 content += child->GetSelectedContent(selection);
77 }
78
79 return content;
80}
81
82void Node::Reserved1() {}
83void Node::Reserved2() {}
84void Node::Reserved3() {}
85void Node::Reserved4() {}
86void Node::Reserved5() {}
87void Node::Reserved6() {}
88void Node::Reserved7() {}
89void Node::Reserved8() {}
90
91/// @brief 在 ftxui::Screen 上顯示元素。
92/// @ingroup dom
93void Render(Screen& screen, const Element& element) {
94 Selection selection;
95 Render(screen, element.get(), selection);
96}
97
98/// @brief 在 ftxui::Screen 上顯示元素。
99/// @ingroup dom
100void Render(Screen& screen, Node* node) {
101 Selection selection;
102 Render(screen, node, selection);
103}
104
105void Render(Screen& screen, Node* node, Selection& selection) {
106 Box box;
107 box.x_min = 0;
108 box.y_min = 0;
109 box.x_max = screen.dimx() - 1;
110 box.y_max = screen.dimy() - 1;
111
112 Node::Status status;
113 node->Check(&status);
114 const int max_iterations = 20;
115 while (status.need_iteration && status.iteration < max_iterations) {
116 // 步驟 1:找出這個元素想要的維度。
117 node->ComputeRequirement();
118
119 // 步驟 2:為元素指定一個維度。
120 node->SetBox(box);
121
122 // 檢查此元素是否需要另一輪配置演算法的迭代。
123 status.need_iteration = false;
124 status.iteration++;
125 node->Check(&status);
126 }
127
128 // 步驟 3:選取
129 if (!selection.IsEmpty()) {
130 node->Select(selection);
131 }
132
133 bool use_cursor = node->requirement().focused.enabled;
134 if (!Terminal::GetQuirks().CursorHiding() &&
135 node->requirement().focused.cursor_shape ==
136 Screen::Cursor::Shape::Hidden) {
137 // 將游標設定在正確的位置,可以讓使用 CJK(中文、
138 // 日文、韓文……)字元的使用者,在正確的位置看到他們的
139 // [輸入法編輯器]。詳見 [issue]。
140 //
141 // [輸入法編輯器]:
142 // https://en.wikipedia.org/wiki/Input_method
143 //
144 // [issue]:
145 // https://github.com/ArthurSonzogni/FTXUI/issues/2#issuecomment-505282355
146 //
147 // 不幸的是,Microsoft 終端機並未正確處理隱藏游標。
148 // 反而是游標所在的字元被隱藏,這是個
149 // 大問題。因此,我們無法啟用將游標設定到正確
150 // 位置的功能。它會顯示在右下角。
151 // 詳見:
152 // https://github.com/microsoft/terminal/issues/1203
153 // https://github.com/microsoft/terminal/issues/3093
154 use_cursor = false;
155 }
156
157 if (use_cursor) {
158 screen.SetCursor(Screen::Cursor{
159 node->requirement().focused.node->box_.x_min,
160 node->requirement().focused.node->box_.y_min,
161 node->requirement().focused.cursor_shape,
162 });
163 } else {
164 screen.SetCursor(Screen::Cursor{
165 screen.dimx() - 1,
166 screen.dimy() - 1,
167 Screen::Cursor::Shape::Hidden,
168 });
169 }
170
171 // 步驟 4:繪製元素。
172 screen.stencil = box;
173 node->Render(screen);
174
175 // 步驟 5:套用 shaders
176 screen.ApplyShader();
177}
178
179std::string GetNodeSelectedContent(Screen& screen,
180 Node* node,
181 Selection& selection) {
182 Box box;
183 box.x_min = 0;
184 box.y_min = 0;
185 box.x_max = screen.dimx() - 1;
186 box.y_max = screen.dimy() - 1;
187
188 Node::Status status;
189 node->Check(&status);
190 const int max_iterations = 20;
191 while (status.need_iteration && status.iteration < max_iterations) {
192 // 步驟 1:找出這個元素想要的維度。
193 node->ComputeRequirement();
194
195 // 步驟 2:為元素指定一個維度。
196 node->SetBox(box);
197
198 // 檢查此元素是否需要另一輪配置演算法的迭代。
199 status.need_iteration = false;
200 status.iteration++;
201 node->Check(&status);
202 }
203
204 // 步驟 3:選取
205 node->Select(selection);
206
207 // 步驟 4:取得選取的內容。
208 return node->GetSelectedContent(selection);
209}
210
211} // namespace ftxui
FTXUI ftxui:: 命名空間
Definition animation.hpp:11
std::string GetNodeSelectedContent(Screen &screen, Node *node, Selection &selection)
Definition node.cpp:179
std::shared_ptr< Node > Element
Definition elements.hpp:25
std::vector< Element > Elements
Definition elements.hpp:26
const Element & element
Definition node.hpp:92
void Render(Screen &screen, Node *node, Selection &selection)
Definition node.cpp:105
Node * node
Definition node.hpp:93