FTXUI  5.0.0
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 <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/screen/box.hpp" // for Box
16
17namespace ftxui {
18
19namespace {
20void Normalize(FlexboxConfig::Direction& direction) {
21 switch (direction) {
25 } break;
29 } break;
30 }
31}
32
33void Normalize(FlexboxConfig::AlignContent& align_content) {
35}
36
37void Normalize(FlexboxConfig::JustifyContent& justify_content) {
39}
40
43}
44
45FlexboxConfig Normalize(FlexboxConfig config) {
46 Normalize(config.direction);
47 Normalize(config.wrap);
48 Normalize(config.justify_content);
49 Normalize(config.align_content);
50 return config;
51}
52
53class Flexbox : public Node {
54 public:
55 Flexbox(Elements children, FlexboxConfig config)
56 : Node(std::move(children)),
57 config_(config),
58 config_normalized_(Normalize(config)) {
59 requirement_.flex_grow_x = 1;
60 requirement_.flex_grow_y = 0;
61
62 if (IsColumnOriented()) {
63 std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y);
64 }
65 }
66
67 bool IsColumnOriented() const {
68 return config_.direction == FlexboxConfig::Direction::Column ||
70 }
71
72 void Layout(flexbox_helper::Global& global,
73 bool compute_requirement = false) {
74 global.blocks.reserve(children_.size());
75 for (auto& child : children_) {
76 flexbox_helper::Block block;
77 block.min_size_x = child->requirement().min_x;
78 block.min_size_y = child->requirement().min_y;
80 block.flex_grow_x = child->requirement().flex_grow_x;
81 block.flex_grow_y = child->requirement().flex_grow_y;
82 block.flex_shrink_x = child->requirement().flex_shrink_x;
83 block.flex_shrink_y = child->requirement().flex_shrink_y;
84 }
85 global.blocks.push_back(block);
86 }
87
89 }
90
91 void ComputeRequirement() override {
92 for (auto& child : children_) {
93 child->ComputeRequirement();
94 }
95 flexbox_helper::Global global;
96 global.config = config_normalized_;
97 if (IsColumnOriented()) {
98 global.size_x = 100000; // NOLINT
99 global.size_y = asked_;
100 } else {
101 global.size_x = asked_;
102 global.size_y = 100000; // NOLINT
103 }
104 Layout(global, true);
105
106 // Reset:
107 requirement_.selection = Requirement::Selection::NORMAL;
108 requirement_.selected_box = Box();
109 requirement_.min_x = 0;
110 requirement_.min_y = 0;
111
112 if (global.blocks.empty()) {
113 return;
114 }
115
116 // Compute the union of all the blocks:
117 Box box;
118 box.x_min = global.blocks[0].x;
119 box.y_min = global.blocks[0].y;
120 box.x_max = global.blocks[0].x + global.blocks[0].dim_x;
121 box.y_max = global.blocks[0].y + global.blocks[0].dim_y;
122 for (auto& b : global.blocks) {
123 box.x_min = std::min(box.x_min, b.x);
124 box.y_min = std::min(box.y_min, b.y);
125 box.x_max = std::max(box.x_max, b.x + b.dim_x);
126 box.y_max = std::max(box.y_max, b.y + b.dim_y);
127 }
128 requirement_.min_x = box.x_max - box.x_min;
129 requirement_.min_y = box.y_max - box.y_min;
130
131 // Find the selection:
132 for (size_t i = 0; i < children_.size(); ++i) {
133 if (requirement_.selection >= children_[i]->requirement().selection) {
134 continue;
135 }
136 requirement_.selection = children_[i]->requirement().selection;
137 Box selected_box = children_[i]->requirement().selected_box;
138
139 // Shift |selected_box| according to its position inside this component:
140 auto& b = global.blocks[i];
141 selected_box.x_min += b.x;
142 selected_box.y_min += b.y;
143 selected_box.x_max += b.x;
144 selected_box.y_max += b.y;
145 requirement_.selected_box = Box::Intersection(selected_box, box);
146 }
147 }
148
149 void SetBox(Box box) override {
151
152 const int asked_previous = asked_;
153 asked_ = std::min(asked_, IsColumnOriented() ? box.y_max - box.y_min + 1
154 : box.x_max - box.x_min + 1);
155 need_iteration_ = (asked_ != asked_previous);
156
157 flexbox_helper::Global global;
158 global.config = config_;
159 global.size_x = box.x_max - box.x_min + 1;
160 global.size_y = box.y_max - box.y_min + 1;
161 Layout(global);
162
163 for (size_t i = 0; i < children_.size(); ++i) {
164 auto& child = children_[i];
165 auto& b = global.blocks[i];
166
167 Box children_box;
168 children_box.x_min = box.x_min + b.x;
169 children_box.y_min = box.y_min + b.y;
170 children_box.x_max = box.x_min + b.x + b.dim_x - 1;
171 children_box.y_max = box.y_min + b.y + b.dim_y - 1;
172
174 child->SetBox(intersection);
175
176 need_iteration_ |= (intersection != children_box);
177 }
178 }
179
180 void Check(Status* status) override {
181 for (auto& child : children_) {
182 child->Check(status);
183 }
184
185 if (status->iteration == 0) {
186 asked_ = 6000; // NOLINT
187 need_iteration_ = true;
188 }
189
190 status->need_iteration |= need_iteration_;
191 }
192
193 int asked_ = 6000; // NOLINT
194 bool need_iteration_ = true;
195 const FlexboxConfig config_;
196 const FlexboxConfig config_normalized_;
197};
198
199} // namespace
200
201/// @brief A container displaying elements on row/columns and capable of
202/// wrapping on the next column/row when full.
203/// @param children The elements in the container
204/// @param config The option
205/// @return The container.
206///
207/// #### Example
208///
209/// ```cpp
210/// flexbox({
211/// text("element 1"),
212/// text("element 2"),
213/// text("element 3"),
214/// }, FlexboxConfig()
215// .Set(FlexboxConfig::Direction::Column)
216// .Set(FlexboxConfig::Wrap::WrapInversed)
217// .SetGapMainAxis(1)
218// .SetGapCrossAxis(1)
219// )
220/// ```
221Element flexbox(Elements children, FlexboxConfig config) {
222 return std::make_shared<Flexbox>(std::move(children), config);
223}
224
225/// @brief A container displaying elements in rows from left to right. When
226/// filled, it starts on a new row below.
227/// @param children The elements in the container
228/// @return The container.
229///
230/// #### Example
231///
232/// ```cpp
233/// hflow({
234/// text("element 1"),
235/// text("element 2"),
236/// text("element 3"),
237/// });
238/// ```
240 return flexbox(std::move(children), FlexboxConfig());
241}
242
243/// @brief A container displaying elements in rows from top to bottom. When
244/// filled, it starts on a new columns on the right.
245/// filled, it starts on a new row.
246/// is full, it starts a new row.
247/// @param children The elements in the container
248/// @return The container.
249///
250/// #### Example
251///
252/// ```cpp
253/// vflow({
254/// text("element 1"),
255/// text("element 2"),
256/// text("element 3"),
257/// });
258/// ```
260 return flexbox(std::move(children),
261 FlexboxConfig().Set(FlexboxConfig::Direction::Column));
262}
263
264} // namespace ftxui
virtual void SetBox(Box box)
Assign a position and a dimension to an element for drawing.
Definition node.cpp:27
void Compute(Global &global)
Element flexbox(Elements, FlexboxConfig config=FlexboxConfig())
std::shared_ptr< Node > Element
Definition elements.hpp:22
std::shared_ptr< T > Make(Args &&... args)
Definition component.hpp:26
Element hflow(Elements)
std::vector< Element > Elements
Definition elements.hpp:23
Element vflow(Elements)
static auto Intersection(Box a, Box b) -> Box
Definition box.cpp:12
@ 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.