mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-05-05 22:01:13 +08:00
Implement Node::Select for flexbox. (#977)
This commit is contained in:
parent
f2fb434e31
commit
96e8b8d92e
@ -850,7 +850,7 @@ void ScreenInteractive::HandleTask(Component component, Task& task) {
|
||||
bool ScreenInteractive::HandleSelection(bool handled, Event event) {
|
||||
if (handled) {
|
||||
selection_pending_ = nullptr;
|
||||
selection_data_.empty = false;
|
||||
selection_data_.empty = true;
|
||||
selection_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
@ -93,28 +93,28 @@ class Flexbox : public Node {
|
||||
for (auto& child : children_) {
|
||||
child->ComputeRequirement();
|
||||
}
|
||||
flexbox_helper::Global global;
|
||||
global.config = config_normalized_;
|
||||
global_ = flexbox_helper::Global();
|
||||
global_.config = config_normalized_;
|
||||
if (IsColumnOriented()) {
|
||||
global.size_x = 100000; // NOLINT
|
||||
global.size_y = asked_;
|
||||
global_.size_x = 100000; // NOLINT
|
||||
global_.size_y = asked_;
|
||||
} else {
|
||||
global.size_x = asked_;
|
||||
global.size_y = 100000; // NOLINT
|
||||
global_.size_x = asked_;
|
||||
global_.size_y = 100000; // NOLINT
|
||||
}
|
||||
Layout(global, true);
|
||||
Layout(global_, true);
|
||||
|
||||
if (global.blocks.empty()) {
|
||||
if (global_.blocks.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the union of all the blocks:
|
||||
Box box;
|
||||
box.x_min = global.blocks[0].x;
|
||||
box.y_min = global.blocks[0].y;
|
||||
box.x_max = global.blocks[0].x + global.blocks[0].dim_x;
|
||||
box.y_max = global.blocks[0].y + global.blocks[0].dim_y;
|
||||
for (auto& b : global.blocks) {
|
||||
box.x_min = global_.blocks[0].x;
|
||||
box.y_min = global_.blocks[0].y;
|
||||
box.x_max = global_.blocks[0].x + global_.blocks[0].dim_x;
|
||||
box.y_max = global_.blocks[0].y + global_.blocks[0].dim_y;
|
||||
for (auto& b : global_.blocks) {
|
||||
box.x_min = std::min(box.x_min, b.x);
|
||||
box.y_min = std::min(box.y_min, b.y);
|
||||
box.x_max = std::max(box.x_max, b.x + b.dim_x);
|
||||
@ -128,7 +128,7 @@ class Flexbox : public Node {
|
||||
if (requirement_.focused.Prefer(children_[i]->requirement().focused)) {
|
||||
requirement_.focused = children_[i]->requirement().focused;
|
||||
// Shift |focused.box| according to its position inside this component:
|
||||
auto& b = global.blocks[i];
|
||||
auto& b = global_.blocks[i];
|
||||
requirement_.focused.box.Shift(b.x, b.y);
|
||||
requirement_.focused.box =
|
||||
Box::Intersection(requirement_.focused.box, box);
|
||||
@ -167,6 +167,43 @@ class Flexbox : public Node {
|
||||
}
|
||||
}
|
||||
|
||||
void Select(Selection& selection) override {
|
||||
// If this Node box_ doesn't intersect with the selection, then no
|
||||
// selection.
|
||||
if (Box::Intersection(selection.GetBox(), box_).IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Selection selection_lines = IsColumnOriented()
|
||||
? selection.SaturateVertical(box_)
|
||||
: selection.SaturateHorizontal(box_);
|
||||
|
||||
size_t i = 0;
|
||||
for (auto& line : global_.lines) {
|
||||
Box box;
|
||||
box.x_min = box_.x_min + line.x;
|
||||
box.x_max = box_.x_min + line.x + line.dim_x - 1;
|
||||
box.y_min = box_.y_min + line.y;
|
||||
box.y_max = box_.y_min + line.y + line.dim_y - 1;
|
||||
|
||||
// If the line box doesn't intersect with the selection, then no
|
||||
// selection.
|
||||
if (Box::Intersection(selection.GetBox(), box).IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Selection selection_line =
|
||||
IsColumnOriented() ? selection_lines.SaturateHorizontal(box)
|
||||
: selection_lines.SaturateVertical(box);
|
||||
|
||||
for (auto& block : line.blocks) {
|
||||
std::ignore = block;
|
||||
children_[i]->Select(selection_line);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Check(Status* status) override {
|
||||
for (auto& child : children_) {
|
||||
child->Check(status);
|
||||
@ -184,6 +221,7 @@ class Flexbox : public Node {
|
||||
bool need_iteration_ = true;
|
||||
const FlexboxConfig config_;
|
||||
const FlexboxConfig config_normalized_;
|
||||
flexbox_helper::Global global_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -68,6 +68,10 @@ void SymmetryXY(Global& g) {
|
||||
std::swap(b.x, b.y);
|
||||
std::swap(b.dim_x, b.dim_y);
|
||||
}
|
||||
for (auto& l : g.lines) {
|
||||
std::swap(l.x, l.y);
|
||||
std::swap(l.dim_x, l.dim_y);
|
||||
}
|
||||
}
|
||||
|
||||
void SymmetryX(Global& g) {
|
||||
@ -75,6 +79,9 @@ void SymmetryX(Global& g) {
|
||||
for (auto& b : g.blocks) {
|
||||
b.x = g.size_x - b.x - b.dim_x;
|
||||
}
|
||||
for (auto& l : g.lines) {
|
||||
l.x = g.size_x - l.x - l.dim_x;
|
||||
}
|
||||
}
|
||||
|
||||
void SymmetryY(Global& g) {
|
||||
@ -82,14 +89,13 @@ void SymmetryY(Global& g) {
|
||||
for (auto& b : g.blocks) {
|
||||
b.y = g.size_y - b.y - b.dim_y;
|
||||
}
|
||||
for (auto& l : g.lines) {
|
||||
l.y = g.size_y - l.y - l.dim_y;
|
||||
}
|
||||
}
|
||||
|
||||
struct Line {
|
||||
std::vector<Block*> blocks;
|
||||
};
|
||||
|
||||
void SetX(Global& global, std::vector<Line> lines) {
|
||||
for (auto& line : lines) {
|
||||
void SetX(Global& global) {
|
||||
for (auto& line : global.lines) {
|
||||
std::vector<box_helper::Element> elements;
|
||||
elements.reserve(line.blocks.size());
|
||||
for (auto* block : line.blocks) {
|
||||
@ -110,19 +116,24 @@ void SetX(Global& global, std::vector<Line> lines) {
|
||||
|
||||
int x = 0;
|
||||
for (size_t i = 0; i < line.blocks.size(); ++i) {
|
||||
line.blocks[i]->dim_x = elements[i].size;
|
||||
line.blocks[i]->x = x;
|
||||
line.blocks[i]->dim_x = elements[i].size;
|
||||
x += elements[i].size;
|
||||
x += global.config.gap_x;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& line : global.lines) {
|
||||
line.x = 0;
|
||||
line.dim_x = global.size_x;
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void SetY(Global& g, std::vector<Line> lines) {
|
||||
void SetY(Global& g) {
|
||||
std::vector<box_helper::Element> elements;
|
||||
elements.reserve(lines.size());
|
||||
for (auto& line : lines) {
|
||||
elements.reserve(g.lines.size());
|
||||
for (auto& line : g.lines) {
|
||||
box_helper::Element element;
|
||||
element.flex_shrink = line.blocks.front()->flex_shrink_y;
|
||||
element.flex_grow = line.blocks.front()->flex_grow_y;
|
||||
@ -202,9 +213,9 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
}
|
||||
|
||||
// [Align items]
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
for (size_t i = 0; i < g.lines.size(); ++i) {
|
||||
auto& element = elements[i];
|
||||
for (auto* block : lines[i].blocks) {
|
||||
for (auto* block : g.lines[i].blocks) {
|
||||
const bool stretch =
|
||||
block->flex_grow_y != 0 ||
|
||||
g.config.align_content == FlexboxConfig::AlignContent::Stretch;
|
||||
@ -237,10 +248,16 @@ void SetY(Global& g, std::vector<Line> lines) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ys.push_back(g.size_y);
|
||||
for (size_t i = 0; i < g.lines.size(); ++i) {
|
||||
g.lines[i].y = ys[i];
|
||||
g.lines[i].dim_y = ys[i + 1] - ys[i];
|
||||
}
|
||||
}
|
||||
|
||||
void JustifyContent(Global& g, std::vector<Line> lines) {
|
||||
for (auto& line : lines) {
|
||||
void JustifyContent(Global& g) {
|
||||
for (auto& line : g.lines) {
|
||||
Block* last = line.blocks.back();
|
||||
int remaining_space = g.size_x - last->x - last->dim_x;
|
||||
switch (g.config.justify_content) {
|
||||
@ -315,38 +332,36 @@ void Compute2(Global& global) {
|
||||
|
||||
void Compute3(Global& global) {
|
||||
// Step 1: Lay out every elements into rows:
|
||||
std::vector<Line> lines;
|
||||
{
|
||||
Line line;
|
||||
int x = 0;
|
||||
line.blocks.reserve(global.blocks.size());
|
||||
for (auto& block : global.blocks) {
|
||||
// Does it fit the end of the row?
|
||||
// No? Then we need to start a new one:
|
||||
if (x + block.min_size_x > global.size_x) {
|
||||
x = 0;
|
||||
if (!line.blocks.empty()) {
|
||||
lines.push_back(std::move(line));
|
||||
global.lines.push_back(std::move(line));
|
||||
}
|
||||
line = Line();
|
||||
}
|
||||
|
||||
block.line = static_cast<int>(lines.size());
|
||||
block.line = static_cast<int>(global.lines.size());
|
||||
block.line_position = static_cast<int>(line.blocks.size());
|
||||
line.blocks.push_back(&block);
|
||||
x += block.min_size_x + global.config.gap_x;
|
||||
}
|
||||
if (!line.blocks.empty()) {
|
||||
lines.push_back(std::move(line));
|
||||
global.lines.push_back(std::move(line));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Set positions on the X axis.
|
||||
SetX(global, lines);
|
||||
JustifyContent(global, lines); // Distribute remaining space.
|
||||
SetX(global);
|
||||
JustifyContent(global); // Distribute remaining space.
|
||||
|
||||
// Step 3: Set positions on the Y axis.
|
||||
SetY(global, lines);
|
||||
SetY(global);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
namespace ftxui::flexbox_helper {
|
||||
|
||||
// A block is a rectangle in the flexbox.
|
||||
struct Block {
|
||||
// Input:
|
||||
int min_size_x = 0;
|
||||
@ -28,13 +29,24 @@ struct Block {
|
||||
bool overflow = false;
|
||||
};
|
||||
|
||||
// A line is a row of blocks.
|
||||
struct Line {
|
||||
std::vector<Block*> blocks;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int dim_x = 0;
|
||||
int dim_y = 0;
|
||||
};
|
||||
|
||||
struct Global {
|
||||
std::vector<Block> blocks;
|
||||
std::vector<Line> lines;
|
||||
FlexboxConfig config;
|
||||
int size_x;
|
||||
int size_y;
|
||||
};
|
||||
|
||||
|
||||
void Compute(Global& global);
|
||||
|
||||
} // namespace ftxui::flexbox_helper
|
||||
|
Loading…
Reference in New Issue
Block a user