FTXUI  3.0.0
C++ functional terminal UI.
Loading...
Searching...
No Matches
flexbox.cpp
Go to the documentation of this file.
1#include <algorithm> // for min, max
2#include <cstddef> // for size_t
3#include <memory> // for __shared_ptr_access, shared_ptr, allocator_traits<>::value_type, make_shared
4#include <utility> // for move, swap
5#include <vector> // for vector
6
7#include "ftxui/dom/elements.hpp" // for Element, Elements, flexbox, hflow, vflow
8#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
9#include "ftxui/dom/flexbox_helper.hpp" // for Block, Global, Compute
10#include "ftxui/dom/node.hpp" // for Node, Elements, Node::Status
11#include "ftxui/dom/requirement.hpp" // for Requirement
12#include "ftxui/screen/box.hpp" // for Box
13
14namespace ftxui {
15
16namespace {
17void Normalize(FlexboxConfig::Direction& direction) {
18 switch (direction) {
22 } break;
26 } break;
27 }
28}
29
30void Normalize(FlexboxConfig::AlignContent& align_content) {
32}
33
34void Normalize(FlexboxConfig::JustifyContent& justify_content) {
36}
37
38void Normalize(FlexboxConfig::Wrap& wrap) {
40}
41
42FlexboxConfig Normalize(FlexboxConfig config) {
43 Normalize(config.direction);
44 Normalize(config.wrap);
45 Normalize(config.justify_content);
46 Normalize(config.align_content);
47 return config;
48}
49
50class Flexbox : public Node {
51 public:
52 Flexbox(Elements children, FlexboxConfig config)
53 : Node(std::move(children)),
54 config_(config),
55 config_normalized_(Normalize(config)) {
56 requirement_.flex_grow_x = 1;
57 requirement_.flex_grow_y = 0;
58
59 if (IsColumnOriented()) {
60 std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y);
61 }
62 }
63
64 bool IsColumnOriented() const {
65 return config_.direction == FlexboxConfig::Direction::Column ||
67 }
68
69 void Layout(flexbox_helper::Global& global,
70 bool compute_requirement = false) {
71 for (auto& child : children_) {
72 flexbox_helper::Block block;
73 block.min_size_x = child->requirement().min_x;
74 block.min_size_y = child->requirement().min_y;
75 if (!compute_requirement) {
76 block.flex_grow_x = child->requirement().flex_grow_x;
77 block.flex_grow_y = child->requirement().flex_grow_y;
78 block.flex_shrink_x = child->requirement().flex_shrink_x;
79 block.flex_shrink_y = child->requirement().flex_shrink_y;
80 }
81 global.blocks.push_back(block);
82 }
83
85 }
86
87 void ComputeRequirement() override {
88 for (auto& child : children_) {
89 child->ComputeRequirement();
90 }
91 flexbox_helper::Global global;
92 global.config = config_normalized_;
93 if (IsColumnOriented()) {
94 global.size_x = 100000; // NOLINT
95 global.size_y = asked_;
96 } else {
97 global.size_x = asked_;
98 global.size_y = 100000; // NOLINT
99 }
100 Layout(global, true);
101
102 if (global.blocks.empty()) {
103 requirement_.min_x = 0;
104 requirement_.min_y = 0;
105 return;
106 }
107
108 Box box;
109 box.x_min = global.blocks[0].x;
110 box.y_min = global.blocks[0].y;
111 box.x_max = global.blocks[0].x + global.blocks[0].dim_x;
112 box.y_max = global.blocks[0].y + global.blocks[0].dim_y;
113
114 for (auto& b : global.blocks) {
115 box.x_min = std::min(box.x_min, b.x);
116 box.y_min = std::min(box.y_min, b.y);
117 box.x_max = std::max(box.x_max, b.x + b.dim_x);
118 box.y_max = std::max(box.y_max, b.y + b.dim_y);
119 }
120
121 requirement_.min_x = box.x_max - box.x_min;
122 requirement_.min_y = box.y_max - box.y_min;
123 }
124
125 void SetBox(Box box) override {
126 Node::SetBox(box);
127
128 asked_ = std::min(asked_, IsColumnOriented() ? box.y_max - box.y_min + 1
129 : box.x_max - box.x_min + 1);
130 flexbox_helper::Global global;
131 global.config = config_;
132 global.size_x = box.x_max - box.x_min + 1;
133 global.size_y = box.y_max - box.y_min + 1;
134 Layout(global);
135
136 need_iteration_ = false;
137 for (size_t i = 0; i < children_.size(); ++i) {
138 auto& child = children_[i];
139 auto& b = global.blocks[i];
140
141 Box children_box;
142 children_box.x_min = box.x_min + b.x;
143 children_box.y_min = box.y_min + b.y;
144 children_box.x_max = box.x_min + b.x + b.dim_x - 1;
145 children_box.y_max = box.y_min + b.y + b.dim_y - 1;
146
147 Box intersection = Box::Intersection(children_box, box);
148 child->SetBox(intersection);
149
150 need_iteration_ |= (intersection != children_box);
151 }
152 }
153
154 void Check(Status* status) override {
155 for (auto& child : children_) {
156 child->Check(status);
157 }
158
159 if (status->iteration == 0) {
160 asked_ = 6000; // NOLINT
161 need_iteration_ = true;
162 }
163
164 status->need_iteration |= need_iteration_;
165 }
166
167 int asked_ = 6000; // NOLINT
168 bool need_iteration_ = true;
169 const FlexboxConfig config_;
170 const FlexboxConfig config_normalized_;
171};
172
173} // namespace
174
175/// @brief A container displaying elements on row/columns and capable of
176/// wrapping on the next column/row when full.
177/// @param children The elements in the container
178/// @param config The option
179/// @return The container.
180///
181/// #### Example
182///
183/// ```cpp
184/// flexbox({
185/// text("element 1"),
186/// text("element 2"),
187/// text("element 3"),
188/// }, FlexboxConfig()
189// .Set(FlexboxConfig::Direction::Column)
190// .Set(FlexboxConfig::Wrap::WrapInversed)
191// .SetGapMainAxis(1)
192// .SetGapCrossAxis(1)
193// )
194/// ```
195Element flexbox(Elements children, FlexboxConfig config) {
196 return std::make_shared<Flexbox>(std::move(children), config);
197}
198
199/// @brief A container displaying elements in rows from left to right. When
200/// filled, it starts on a new row below.
201/// @param children The elements in the container
202/// @return The container.
203///
204/// #### Example
205///
206/// ```cpp
207/// hflow({
208/// text("element 1"),
209/// text("element 2"),
210/// text("element 3"),
211/// });
212/// ```
213Element hflow(Elements children) {
214 return flexbox(std::move(children), FlexboxConfig());
215}
216
217/// @brief A container displaying elements in rows from top to bottom. When
218/// filled, it starts on a new columns on the right.
219/// filled, it starts on a new row.
220/// is full, it starts a new row.
221/// @param children The elements in the container
222/// @return The container.
223///
224/// #### Example
225///
226/// ```cpp
227/// vflow({
228/// text("element 1"),
229/// text("element 2"),
230/// text("element 3"),
231/// });
232/// ```
233Element vflow(Elements children) {
234 return flexbox(std::move(children),
235 FlexboxConfig().Set(FlexboxConfig::Direction::Column));
236}
237
238} // namespace ftxui
239
240// Copyright 2020 Arthur Sonzogni. All rights reserved.
241// Use of this source code is governed by the MIT license that can be found in
242// the LICENSE file.
virtual void SetBox(Box box)
Assign a position and a dimension to an element for drawing.
Definition node.cpp:22
void Compute(Global &global)
Element flexbox(Elements, FlexboxConfig config=FlexboxConfig())
std::shared_ptr< Node > Element
Definition elements.hpp:18
Element hflow(Elements)
std::vector< Element > Elements
Definition elements.hpp:19
Element vflow(Elements)
static auto Intersection(Box a, Box b) -> Box
Definition box.cpp:9
@ FlexStart
items are placed at the start of the cross axis.
@ Column
Flex items are laid out in a column.
@ Row
Flex items are laid out in a row.
@ RowInversed
Flex items are laid out in a row, but in reverse order.
@ Wrap
Flex items will wrap onto multiple lines.
@ FlexStart
Items are aligned to the start of flexbox's direction.