FTXUI 6.1.9
C++ functional terminal UI.
Loading...
Searching...
No Matches
flexbox.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 min, max
5#include <cstddef> // for size_t
6#include <memory> // for __shared_ptr_access, shared_ptr, allocator_traits<>::value_type, make_shared
7#include <tuple> // for ignore
8#include <utility> // for move, swap
9#include <vector> // for vector
10
11#include "ftxui/dom/elements.hpp" // for Element, Elements, flexbox, hflow, vflow
12#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
13#include "ftxui/dom/flexbox_helper.hpp" // for Block, Global, Compute
14#include "ftxui/dom/node.hpp" // for Node, Elements, Node::Status
15#include "ftxui/dom/requirement.hpp" // for Requirement
16#include "ftxui/dom/selection.hpp" // for Selection
17#include "ftxui/screen/box.hpp" // for Box
18
19namespace ftxui {
20
21namespace {
22void Normalize(FlexboxConfig::Direction& direction) {
23 switch (direction) {
27 } break;
31 } break;
32 }
33}
34
35void Normalize(FlexboxConfig::AlignContent& align_content) {
37}
38
39void Normalize(FlexboxConfig::JustifyContent& justify_content) {
41}
42
43void Normalize(FlexboxConfig::Wrap& wrap) {
45}
46
47FlexboxConfig Normalize(FlexboxConfig config) {
48 Normalize(config.direction);
49 Normalize(config.wrap);
50 Normalize(config.justify_content);
51 Normalize(config.align_content);
52 return config;
53}
54
55class Flexbox : public Node {
56 public:
57 Flexbox(Elements children, FlexboxConfig config)
58 : Node(std::move(children)),
59 config_(config),
60 config_normalized_(Normalize(config)) {
61 requirement_.flex_grow_x = 1;
62 requirement_.flex_grow_y = 0;
63
64 if (IsColumnOriented()) {
65 std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y);
66 }
67 }
68
69 bool IsColumnOriented() const {
70 return config_.direction == FlexboxConfig::Direction::Column ||
72 }
73
74 void Layout(flexbox_helper::Global& global,
75 bool compute_requirement = false) {
76 global.blocks.reserve(children_.size());
77 for (auto& child : children_) {
78 flexbox_helper::Block block;
79 block.min_size_x = child->requirement().min_x;
80 block.min_size_y = child->requirement().min_y;
81 if (!compute_requirement) {
82 block.flex_grow_x = child->requirement().flex_grow_x;
83 block.flex_grow_y = child->requirement().flex_grow_y;
84 block.flex_shrink_x = child->requirement().flex_shrink_x;
85 block.flex_shrink_y = child->requirement().flex_shrink_y;
86 }
87 global.blocks.push_back(block);
88 }
89
91 }
92
93 void ComputeRequirement() override {
94 requirement_ = Requirement{};
95 for (auto& child : children_) {
96 child->ComputeRequirement();
97 }
98 global_ = flexbox_helper::Global();
100 if (IsColumnOriented()) {
101 global_.size_x = 100000; // NOLINT
102 global_.size_y = asked_;
103 } else {
104 global_.size_x = asked_;
105 global_.size_y = 100000; // NOLINT
106 }
107 Layout(global_, true);
108
109 if (global_.blocks.empty()) {
110 return;
111 }
112
113 // Compute the union of all the blocks:
114 Box box;
115 box.x_min = global_.blocks[0].x;
116 box.y_min = global_.blocks[0].y;
117 box.x_max = global_.blocks[0].x + global_.blocks[0].dim_x;
118 box.y_max = global_.blocks[0].y + global_.blocks[0].dim_y;
119 for (auto& b : global_.blocks) {
120 box.x_min = std::min(box.x_min, b.x);
121 box.y_min = std::min(box.y_min, b.y);
122 box.x_max = std::max(box.x_max, b.x + b.dim_x);
123 box.y_max = std::max(box.y_max, b.y + b.dim_y);
124 }
125 requirement_.min_x = box.x_max - box.x_min;
126 requirement_.min_y = box.y_max - box.y_min;
127
128 // Find the selection:
129 for (size_t i = 0; i < children_.size(); ++i) {
130 if (requirement_.focused.Prefer(children_[i]->requirement().focused)) {
131 requirement_.focused = children_[i]->requirement().focused;
132 // Shift |focused.box| according to its position inside this component:
133 auto& b = global_.blocks[i];
134 requirement_.focused.box.Shift(b.x, b.y);
135 requirement_.focused.box =
136 Box::Intersection(requirement_.focused.box, box);
137 }
138 }
139 }
140
141 void SetBox(Box box) override {
142 Node::SetBox(box);
143
144 const int asked_previous = asked_;
145 asked_ = std::min(asked_, IsColumnOriented() ? box.y_max - box.y_min + 1
146 : box.x_max - box.x_min + 1);
147 need_iteration_ = (asked_ != asked_previous);
148
149 flexbox_helper::Global global;
150 global.config = config_;
151 global.size_x = box.x_max - box.x_min + 1;
152 global.size_y = box.y_max - box.y_min + 1;
153 Layout(global);
154
155 for (size_t i = 0; i < children_.size(); ++i) {
156 auto& child = children_[i];
157 auto& b = global.blocks[i];
158
159 Box children_box;
160 children_box.x_min = box.x_min + b.x;
161 children_box.y_min = box.y_min + b.y;
162 children_box.x_max = box.x_min + b.x + b.dim_x - 1;
163 children_box.y_max = box.y_min + b.y + b.dim_y - 1;
164
165 const Box intersection = Box::Intersection(children_box, box);
166 child->SetBox(intersection);
167
168 need_iteration_ |= (intersection != children_box);
169 }
170 }
171
172 void Select(Selection& selection) override {
173 // If this Node box_ doesn't intersect with the selection, then no
174 // selection.
175 if (Box::Intersection(selection.GetBox(), box_).IsEmpty()) {
176 return;
177 }
178
179 Selection selection_lines = IsColumnOriented()
180 ? selection.SaturateVertical(box_)
181 : selection.SaturateHorizontal(box_);
182
183 size_t i = 0;
184 for (auto& line : global_.lines) {
185 Box box;
186 box.x_min = box_.x_min + line.x;
187 box.x_max = box_.x_min + line.x + line.dim_x - 1;
188 box.y_min = box_.y_min + line.y;
189 box.y_max = box_.y_min + line.y + line.dim_y - 1;
190
191 // If the line box doesn't intersect with the selection, then no
192 // selection.
193 if (Box::Intersection(selection.GetBox(), box).IsEmpty()) {
194 continue;
195 }
196
197 Selection selection_line = IsColumnOriented()
198 ? selection_lines.SaturateHorizontal(box)
199 : selection_lines.SaturateVertical(box);
200
201 for (auto& block : line.blocks) {
202 std::ignore = block;
203 children_[i]->Select(selection_line);
204 i++;
205 }
206 }
207 }
208
209 void Check(Status* status) override {
210 for (auto& child : children_) {
211 child->Check(status);
212 }
213
214 if (status->iteration == 0) {
215 asked_ = 6000; // NOLINT
216 need_iteration_ = true;
217 }
218
219 status->need_iteration |= need_iteration_;
220 }
221
222 int asked_ = 6000; // NOLINT
223 bool need_iteration_ = true;
224 const FlexboxConfig config_;
225 const FlexboxConfig config_normalized_;
226 flexbox_helper::Global global_;
227};
228
229} // namespace
230
231/// @brief 行/列に要素を表示し、一杯になると次の列/行に折り返すことができるコンテナ。
232/// @param children コンテナ内の要素
233/// @param config オプション
234/// @return コンテナ。
235///
236/// #### 例
237///
238/// ```cpp
239/// flexbox({
240/// text("element 1"),
241/// text("element 2"),
242/// text("element 3"),
243/// }, FlexboxConfig()
244/// .Set(FlexboxConfig::Direction::Column)
245/// .Set(FlexboxConfig::Wrap::WrapInversed)
246/// .SetGapMainAxis(1)
247/// .SetGapCrossAxis(1)
248/// )
249/// ```
251 return std::make_shared<Flexbox>(std::move(children), config);
252}
253
254/// @brief 要素を左から右へ行で表示するコンテナ。一杯になると、下の新しい行から開始します。
255/// @param children コンテナ内の要素
256/// @return コンテナ。
257///
258/// #### 例
259///
260/// ```cpp
261/// hflow({
262/// text("element 1"),
263/// text("element 2"),
264/// text("element 3"),
265/// });
266/// ```
268 return flexbox(std::move(children), FlexboxConfig());
269}
270
271/// @brief 要素を上から下へ行で表示するコンテナ。一杯になると、右側の新しい列から開始します。
272/// @param children コンテナ内の要素
273/// @return コンテナ。
274///
275/// #### 例
276///
277/// ```cpp
278/// vflow({
279/// text("element 1"),
280/// text("element 2"),
281/// text("element 3"),
282/// });
283/// ```
285 return flexbox(std::move(children),
287}
288
289} // namespace ftxui
int asked_
Definition flexbox.cpp:222
bool need_iteration_
Definition flexbox.cpp:223
const FlexboxConfig config_
Definition flexbox.cpp:224
const FlexboxConfig config_normalized_
Definition flexbox.cpp:225
flexbox_helper::Global global_
Definition flexbox.cpp:226
@ FlexStart
アイテムは交差軸の開始位置に配置されます。
@ Column
フレックスアイテムは列に配置されます。
@ Row
フレックスアイテムは行に配置されます。
@ RowInversed
フレックスアイテムは行に配置されますが、逆順になります。
virtual void SetBox(Box box)
描画のために要素に位置と次元を割り当てます。
Definition node.cpp:41
@ Wrap
フレックスアイテムは複数行に折り返されます。
@ FlexStart
アイテムはflexboxの方向の開始位置に揃えられます。
FlexboxConfigは、flexboxコンテナのレイアウトプロパティを定義する構成構造体です。
static auto Intersection(Box a, Box b) -> Box
Definition box.cpp:10
void Compute(Global &global)
FTXUI ftxui:: 名前空間
Definition animation.hpp:9
Element flexbox(Elements, FlexboxConfig config=FlexboxConfig())
行/列に要素を表示し、一杯になると次の列/行に折り返すことができるコンテナ。
Definition flexbox.cpp:250
std::shared_ptr< Node > Element
Definition elements.hpp:21
Element hflow(Elements)
要素を左から右へ行で表示するコンテナ。一杯になると、下の新しい行から開始します。
Definition flexbox.cpp:267
std::vector< Element > Elements
Definition elements.hpp:22
Element vflow(Elements)
要素を上から下へ行で表示するコンテナ。一杯になると、右側の新しい列から開始します。
Definition flexbox.cpp:284