FTXUI 6.1.9
C++ functional terminal UI.
载入中...
搜索中...
未找到
flexbox.cpp
浏览该文件的文档.
1// Copyright 2020 Arthur Sonzogni. All rights reserved.
2// 本源代码的使用受 MIT 许可协议的约束,该协议可在 LICENSE 文件中找到。
3#include <algorithm> // for min, max
4#include <cstddef> // for size_t
5#include <memory> // for __shared_ptr_access, shared_ptr, allocator_traits<>::value_type, make_shared
6#include <tuple> // for ignore
7#include <utility> // for move, swap
8#include <vector> // for vector
9
10#include "ftxui/dom/elements.hpp" // for Element, Elements, flexbox, hflow, vflow
11#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig, FlexboxConfig::Direction, FlexboxConfig::Direction::Column, FlexboxConfig::AlignContent, FlexboxConfig::Direction::ColumnInversed, FlexboxConfig::Direction::Row, FlexboxConfig::JustifyContent, FlexboxConfig::Wrap, FlexboxConfig::AlignContent::FlexStart, FlexboxConfig::Direction::RowInversed, FlexboxConfig::JustifyContent::FlexStart, FlexboxConfig::Wrap::Wrap
12#include "ftxui/dom/flexbox_helper.hpp" // for Block, Global, Compute
13#include "ftxui/dom/node.hpp" // for Node, Elements, Node::Status
14#include "ftxui/dom/requirement.hpp" // for Requirement
15#include "ftxui/dom/selection.hpp" // for Selection
16#include "ftxui/screen/box.hpp" // for Box
17
18namespace ftxui {
19
20namespace {
21void Normalize(FlexboxConfig::Direction& direction) {
22 switch (direction) {
26 } break;
30 } break;
31 }
32}
33
34void Normalize(FlexboxConfig::AlignContent& align_content) {
36}
37
38void Normalize(FlexboxConfig::JustifyContent& justify_content) {
40}
41
42void Normalize(FlexboxConfig::Wrap& wrap) {
44}
45
46FlexboxConfig Normalize(FlexboxConfig config) {
47 Normalize(config.direction);
48 Normalize(config.wrap);
49 Normalize(config.justify_content);
50 Normalize(config.align_content);
51 return config;
52}
53
54class Flexbox : public Node {
55 public:
56 Flexbox(Elements children, FlexboxConfig config)
57 : Node(std::move(children)),
58 config_(config),
59 config_normalized_(Normalize(config)) {
60 requirement_.flex_grow_x = 1;
61 requirement_.flex_grow_y = 0;
62
63 if (IsColumnOriented()) {
64 std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y);
65 }
66 }
67
68 bool IsColumnOriented() const {
69 return config_.direction == FlexboxConfig::Direction::Column ||
71 }
72
73 void Layout(flexbox_helper::Global& global,
74 bool compute_requirement = false) {
75 global.blocks.reserve(children_.size());
76 for (auto& child : children_) {
77 flexbox_helper::Block block;
78 block.min_size_x = child->requirement().min_x;
79 block.min_size_y = child->requirement().min_y;
80 if (!compute_requirement) {
81 block.flex_grow_x = child->requirement().flex_grow_x;
82 block.flex_grow_y = child->requirement().flex_grow_y;
83 block.flex_shrink_x = child->requirement().flex_shrink_x;
84 block.flex_shrink_y = child->requirement().flex_shrink_y;
85 }
86 global.blocks.push_back(block);
87 }
88
90 }
91
92 void ComputeRequirement() override {
93 requirement_ = Requirement{};
94 for (auto& child : children_) {
95 child->ComputeRequirement();
96 }
97 global_ = flexbox_helper::Global();
99 if (IsColumnOriented()) {
100 global_.size_x = 100000; // NOLINT
101 global_.size_y = asked_;
102 } else {
103 global_.size_x = asked_;
104 global_.size_y = 100000; // NOLINT
105 }
106 Layout(global_, true);
107
108 if (global_.blocks.empty()) {
109 return;
110 }
111
112 // Compute the union of all the blocks:
113 Box box;
114 box.x_min = global_.blocks[0].x;
115 box.y_min = global_.blocks[0].y;
116 box.x_max = global_.blocks[0].x + global_.blocks[0].dim_x;
117 box.y_max = global_.blocks[0].y + global_.blocks[0].dim_y;
118 for (auto& b : global_.blocks) {
119 box.x_min = std::min(box.x_min, b.x);
120 box.y_min = std::min(box.y_min, b.y);
121 box.x_max = std::max(box.x_max, b.x + b.dim_x);
122 box.y_max = std::max(box.y_max, b.y + b.dim_y);
123 }
124 requirement_.min_x = box.x_max - box.x_min;
125 requirement_.min_y = box.y_max - box.y_min;
126
127 // Find the selection:
128 for (size_t i = 0; i < children_.size(); ++i) {
129 if (requirement_.focused.Prefer(children_[i]->requirement().focused)) {
130 requirement_.focused = children_[i]->requirement().focused;
131 // Shift |focused.box| according to its position inside this component:
132 auto& b = global_.blocks[i];
133 requirement_.focused.box.Shift(b.x, b.y);
134 requirement_.focused.box =
135 Box::Intersection(requirement_.focused.box, box);
136 }
137 }
138 }
139
140 void SetBox(Box box) override {
141 Node::SetBox(box);
142
143 const int asked_previous = asked_;
144 asked_ = std::min(asked_, IsColumnOriented() ? box.y_max - box.y_min + 1
145 : box.x_max - box.x_min + 1);
146 need_iteration_ = (asked_ != asked_previous);
147
148 flexbox_helper::Global global;
149 global.config = config_;
150 global.size_x = box.x_max - box.x_min + 1;
151 global.size_y = box.y_max - box.y_min + 1;
152 Layout(global);
153
154 for (size_t i = 0; i < children_.size(); ++i) {
155 auto& child = children_[i];
156 auto& b = global.blocks[i];
157
158 Box children_box;
159 children_box.x_min = box.x_min + b.x;
160 children_box.y_min = box.y_min + b.y;
161 children_box.x_max = box.x_min + b.x + b.dim_x - 1;
162 children_box.y_max = box.y_min + b.y + b.dim_y - 1;
163
164 const Box intersection = Box::Intersection(children_box, box);
165 child->SetBox(intersection);
166
167 need_iteration_ |= (intersection != children_box);
168 }
169 }
170
171 void Select(Selection& selection) override {
172 // If this Node box_ doesn't intersect with the selection, then no
173 // selection.
174 if (Box::Intersection(selection.GetBox(), box_).IsEmpty()) {
175 return;
176 }
177
178 Selection selection_lines = IsColumnOriented()
179 ? selection.SaturateVertical(box_)
180 : selection.SaturateHorizontal(box_);
181
182 size_t i = 0;
183 for (auto& line : global_.lines) {
184 Box box;
185 box.x_min = box_.x_min + line.x;
186 box.x_max = box_.x_min + line.x + line.dim_x - 1;
187 box.y_min = box_.y_min + line.y;
188 box.y_max = box_.y_min + line.y + line.dim_y - 1;
189
190 // If the line box doesn't intersect with the selection, then no
191 // selection.
192 if (Box::Intersection(selection.GetBox(), box).IsEmpty()) {
193 continue;
194 }
195
196 Selection selection_line = IsColumnOriented()
197 ? selection_lines.SaturateHorizontal(box)
198 : selection_lines.SaturateVertical(box);
199
200 for (auto& block : line.blocks) {
201 std::ignore = block;
202 children_[i]->Select(selection_line);
203 i++;
204 }
205 }
206 }
207
208 void Check(Status* status) override {
209 for (auto& child : children_) {
210 child->Check(status);
211 }
212
213 if (status->iteration == 0) {
214 asked_ = 6000; // NOLINT
215 need_iteration_ = true;
216 }
217
218 status->need_iteration |= need_iteration_;
219 }
220
221 int asked_ = 6000; // NOLINT
222 bool need_iteration_ = true;
223 const FlexboxConfig config_;
224 const FlexboxConfig config_normalized_;
225 flexbox_helper::Global global_;
226};
227
228} // namespace
229
230/// @brief 一个容器,用于在行/列中显示元素,并且在填满时能够换行到下一列/行。
231/// @param children 容器中的元素
232/// @param config 选项
233/// @return 容器。
234///
235/// #### Example
236///
237/// ```cpp
238/// flexbox({
239/// text("element 1"),
240/// text("element 2"),
241/// text("element 3"),
242/// }, FlexboxConfig()
243/// .Set(FlexboxConfig::Direction::Column)
244/// .Set(FlexboxConfig::Wrap::WrapInversed)
245/// .SetGapMainAxis(1)
246/// .SetGapCrossAxis(1)
247/// )
248/// ```
250 return std::make_shared<Flexbox>(std::move(children), config);
251}
252
253/// @brief 一个容器,用于从左到右在行中显示元素。当填满时,它会在下方开始新的一行。
254/// @param children 容器中的元素
255/// @return 容器。
256///
257/// #### Example
258///
259/// ```cpp
260/// hflow({
261/// text("element 1"),
262/// text("element 2"),
263/// text("element 3"),
264/// });
265/// ```
267 return flexbox(std::move(children), FlexboxConfig());
268}
269
270/// @brief 一个容器,用于从上到下在行中显示元素。当填满时,它会在右侧开始新的一列。
271/// @param children 容器中的元素
272/// @return 容器。
273///
274/// #### Example
275///
276/// ```cpp
277/// vflow({
278/// text("element 1"),
279/// text("element 2"),
280/// text("element 3"),
281/// });
282/// ```
284 return flexbox(std::move(children),
286}
287
288} // namespace ftxui
int asked_
bool need_iteration_
const FlexboxConfig config_
const FlexboxConfig config_normalized_
flexbox_helper::Global global_
@ FlexStart
项目放置在交叉轴的起始位置。
@ Column
弹性项目按列排列。
@ Row
弹性项目按行排列。
@ RowInversed
弹性项目按行反向排列。
virtual void SetBox(Box box)
为绘图元素分配位置和尺寸。
Wrap
默认情况下,弹性项目将全部尝试适应一行。您可以通过此属性更改它,并允许项目根据需要换行。
@ Wrap
弹性项目将换行到多行。
@ FlexStart
项目与弹性盒子方向的起始对齐。
FlexboxConfig 是一个配置结构体,定义了弹性盒子容器的布局属性。
static auto Intersection(Box a, Box b) -> Box
定义 box.cpp:11
void Compute(Global &global)
#include "ftxui/component/component_base.hpp" // 用于 ComponentBase
Element flexbox(Elements, FlexboxConfig config=FlexboxConfig())
一个容器,用于在行/列中显示元素,并且在填满时能够换行到下一列/行。
std::shared_ptr< Node > Element
Element hflow(Elements)
一个容器,用于从左到右在行中显示元素。当填满时,它会在下方开始新的一行。
std::vector< Element > Elements
Element vflow(Elements)
一个容器,用于从上到下在行中显示元素。当填满时,它会在右侧开始新的一列。