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) {
|
bool ScreenInteractive::HandleSelection(bool handled, Event event) {
|
||||||
if (handled) {
|
if (handled) {
|
||||||
selection_pending_ = nullptr;
|
selection_pending_ = nullptr;
|
||||||
selection_data_.empty = false;
|
selection_data_.empty = true;
|
||||||
selection_ = nullptr;
|
selection_ = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -93,28 +93,28 @@ class Flexbox : public Node {
|
|||||||
for (auto& child : children_) {
|
for (auto& child : children_) {
|
||||||
child->ComputeRequirement();
|
child->ComputeRequirement();
|
||||||
}
|
}
|
||||||
flexbox_helper::Global global;
|
global_ = flexbox_helper::Global();
|
||||||
global.config = config_normalized_;
|
global_.config = config_normalized_;
|
||||||
if (IsColumnOriented()) {
|
if (IsColumnOriented()) {
|
||||||
global.size_x = 100000; // NOLINT
|
global_.size_x = 100000; // NOLINT
|
||||||
global.size_y = asked_;
|
global_.size_y = asked_;
|
||||||
} else {
|
} else {
|
||||||
global.size_x = asked_;
|
global_.size_x = asked_;
|
||||||
global.size_y = 100000; // NOLINT
|
global_.size_y = 100000; // NOLINT
|
||||||
}
|
}
|
||||||
Layout(global, true);
|
Layout(global_, true);
|
||||||
|
|
||||||
if (global.blocks.empty()) {
|
if (global_.blocks.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the union of all the blocks:
|
// Compute the union of all the blocks:
|
||||||
Box box;
|
Box box;
|
||||||
box.x_min = global.blocks[0].x;
|
box.x_min = global_.blocks[0].x;
|
||||||
box.y_min = global.blocks[0].y;
|
box.y_min = global_.blocks[0].y;
|
||||||
box.x_max = global.blocks[0].x + global.blocks[0].dim_x;
|
box.x_max = global_.blocks[0].x + global_.blocks[0].dim_x;
|
||||||
box.y_max = global.blocks[0].y + global.blocks[0].dim_y;
|
box.y_max = global_.blocks[0].y + global_.blocks[0].dim_y;
|
||||||
for (auto& b : global.blocks) {
|
for (auto& b : global_.blocks) {
|
||||||
box.x_min = std::min(box.x_min, b.x);
|
box.x_min = std::min(box.x_min, b.x);
|
||||||
box.y_min = std::min(box.y_min, b.y);
|
box.y_min = std::min(box.y_min, b.y);
|
||||||
box.x_max = std::max(box.x_max, b.x + b.dim_x);
|
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)) {
|
if (requirement_.focused.Prefer(children_[i]->requirement().focused)) {
|
||||||
requirement_.focused = children_[i]->requirement().focused;
|
requirement_.focused = children_[i]->requirement().focused;
|
||||||
// Shift |focused.box| according to its position inside this component:
|
// 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.Shift(b.x, b.y);
|
||||||
requirement_.focused.box =
|
requirement_.focused.box =
|
||||||
Box::Intersection(requirement_.focused.box, 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 {
|
void Check(Status* status) override {
|
||||||
for (auto& child : children_) {
|
for (auto& child : children_) {
|
||||||
child->Check(status);
|
child->Check(status);
|
||||||
@ -184,6 +221,7 @@ class Flexbox : public Node {
|
|||||||
bool need_iteration_ = true;
|
bool need_iteration_ = true;
|
||||||
const FlexboxConfig config_;
|
const FlexboxConfig config_;
|
||||||
const FlexboxConfig config_normalized_;
|
const FlexboxConfig config_normalized_;
|
||||||
|
flexbox_helper::Global global_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -68,6 +68,10 @@ void SymmetryXY(Global& g) {
|
|||||||
std::swap(b.x, b.y);
|
std::swap(b.x, b.y);
|
||||||
std::swap(b.dim_x, b.dim_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) {
|
void SymmetryX(Global& g) {
|
||||||
@ -75,6 +79,9 @@ void SymmetryX(Global& g) {
|
|||||||
for (auto& b : g.blocks) {
|
for (auto& b : g.blocks) {
|
||||||
b.x = g.size_x - b.x - b.dim_x;
|
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) {
|
void SymmetryY(Global& g) {
|
||||||
@ -82,14 +89,13 @@ void SymmetryY(Global& g) {
|
|||||||
for (auto& b : g.blocks) {
|
for (auto& b : g.blocks) {
|
||||||
b.y = g.size_y - b.y - b.dim_y;
|
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 {
|
void SetX(Global& global) {
|
||||||
std::vector<Block*> blocks;
|
for (auto& line : global.lines) {
|
||||||
};
|
|
||||||
|
|
||||||
void SetX(Global& global, std::vector<Line> lines) {
|
|
||||||
for (auto& line : lines) {
|
|
||||||
std::vector<box_helper::Element> elements;
|
std::vector<box_helper::Element> elements;
|
||||||
elements.reserve(line.blocks.size());
|
elements.reserve(line.blocks.size());
|
||||||
for (auto* block : line.blocks) {
|
for (auto* block : line.blocks) {
|
||||||
@ -110,19 +116,24 @@ void SetX(Global& global, std::vector<Line> lines) {
|
|||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
for (size_t i = 0; i < line.blocks.size(); ++i) {
|
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]->x = x;
|
||||||
|
line.blocks[i]->dim_x = elements[i].size;
|
||||||
x += elements[i].size;
|
x += elements[i].size;
|
||||||
x += global.config.gap_x;
|
x += global.config.gap_x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& line : global.lines) {
|
||||||
|
line.x = 0;
|
||||||
|
line.dim_x = global.size_x;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
void SetY(Global& g, std::vector<Line> lines) {
|
void SetY(Global& g) {
|
||||||
std::vector<box_helper::Element> elements;
|
std::vector<box_helper::Element> elements;
|
||||||
elements.reserve(lines.size());
|
elements.reserve(g.lines.size());
|
||||||
for (auto& line : lines) {
|
for (auto& line : g.lines) {
|
||||||
box_helper::Element element;
|
box_helper::Element element;
|
||||||
element.flex_shrink = line.blocks.front()->flex_shrink_y;
|
element.flex_shrink = line.blocks.front()->flex_shrink_y;
|
||||||
element.flex_grow = line.blocks.front()->flex_grow_y;
|
element.flex_grow = line.blocks.front()->flex_grow_y;
|
||||||
@ -202,9 +213,9 @@ void SetY(Global& g, std::vector<Line> lines) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// [Align items]
|
// [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];
|
auto& element = elements[i];
|
||||||
for (auto* block : lines[i].blocks) {
|
for (auto* block : g.lines[i].blocks) {
|
||||||
const bool stretch =
|
const bool stretch =
|
||||||
block->flex_grow_y != 0 ||
|
block->flex_grow_y != 0 ||
|
||||||
g.config.align_content == FlexboxConfig::AlignContent::Stretch;
|
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) {
|
void JustifyContent(Global& g) {
|
||||||
for (auto& line : lines) {
|
for (auto& line : g.lines) {
|
||||||
Block* last = line.blocks.back();
|
Block* last = line.blocks.back();
|
||||||
int remaining_space = g.size_x - last->x - last->dim_x;
|
int remaining_space = g.size_x - last->x - last->dim_x;
|
||||||
switch (g.config.justify_content) {
|
switch (g.config.justify_content) {
|
||||||
@ -315,38 +332,36 @@ void Compute2(Global& global) {
|
|||||||
|
|
||||||
void Compute3(Global& global) {
|
void Compute3(Global& global) {
|
||||||
// Step 1: Lay out every elements into rows:
|
// Step 1: Lay out every elements into rows:
|
||||||
std::vector<Line> lines;
|
|
||||||
{
|
{
|
||||||
Line line;
|
Line line;
|
||||||
int x = 0;
|
int x = 0;
|
||||||
line.blocks.reserve(global.blocks.size());
|
|
||||||
for (auto& block : global.blocks) {
|
for (auto& block : global.blocks) {
|
||||||
// Does it fit the end of the row?
|
// Does it fit the end of the row?
|
||||||
// No? Then we need to start a new one:
|
// No? Then we need to start a new one:
|
||||||
if (x + block.min_size_x > global.size_x) {
|
if (x + block.min_size_x > global.size_x) {
|
||||||
x = 0;
|
x = 0;
|
||||||
if (!line.blocks.empty()) {
|
if (!line.blocks.empty()) {
|
||||||
lines.push_back(std::move(line));
|
global.lines.push_back(std::move(line));
|
||||||
}
|
}
|
||||||
line = 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());
|
block.line_position = static_cast<int>(line.blocks.size());
|
||||||
line.blocks.push_back(&block);
|
line.blocks.push_back(&block);
|
||||||
x += block.min_size_x + global.config.gap_x;
|
x += block.min_size_x + global.config.gap_x;
|
||||||
}
|
}
|
||||||
if (!line.blocks.empty()) {
|
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.
|
// Step 2: Set positions on the X axis.
|
||||||
SetX(global, lines);
|
SetX(global);
|
||||||
JustifyContent(global, lines); // Distribute remaining space.
|
JustifyContent(global); // Distribute remaining space.
|
||||||
|
|
||||||
// Step 3: Set positions on the Y axis.
|
// Step 3: Set positions on the Y axis.
|
||||||
SetY(global, lines);
|
SetY(global);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
namespace ftxui::flexbox_helper {
|
namespace ftxui::flexbox_helper {
|
||||||
|
|
||||||
|
// A block is a rectangle in the flexbox.
|
||||||
struct Block {
|
struct Block {
|
||||||
// Input:
|
// Input:
|
||||||
int min_size_x = 0;
|
int min_size_x = 0;
|
||||||
@ -28,13 +29,24 @@ struct Block {
|
|||||||
bool overflow = false;
|
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 {
|
struct Global {
|
||||||
std::vector<Block> blocks;
|
std::vector<Block> blocks;
|
||||||
|
std::vector<Line> lines;
|
||||||
FlexboxConfig config;
|
FlexboxConfig config;
|
||||||
int size_x;
|
int size_x;
|
||||||
int size_y;
|
int size_y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void Compute(Global& global);
|
void Compute(Global& global);
|
||||||
|
|
||||||
} // namespace ftxui::flexbox_helper
|
} // namespace ftxui::flexbox_helper
|
||||||
|
Loading…
Reference in New Issue
Block a user