From 9f87637aaca362b821c441533935115e32960eac Mon Sep 17 00:00:00 2001 From: ArthurSonzogni Date: Fri, 27 Dec 2024 11:36:03 +0100 Subject: [PATCH] Implement Node::Select for flexbox. --- src/ftxui/dom/flexbox.cpp | 65 +++++++++++++++++++++++++------- src/ftxui/dom/flexbox_helper.cpp | 59 ++++++++++++++++++----------- src/ftxui/dom/flexbox_helper.hpp | 12 ++++++ 3 files changed, 100 insertions(+), 36 deletions(-) diff --git a/src/ftxui/dom/flexbox.cpp b/src/ftxui/dom/flexbox.cpp index 336527b19..d814a8a2e 100644 --- a/src/ftxui/dom/flexbox.cpp +++ b/src/ftxui/dom/flexbox.cpp @@ -92,16 +92,16 @@ 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); // Reset: requirement_.selection = Requirement::Selection::NORMAL; @@ -109,17 +109,17 @@ class Flexbox : public Node { requirement_.min_x = 0; requirement_.min_y = 0; - 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); @@ -137,7 +137,7 @@ class Flexbox : public Node { Box selected_box = children_[i]->requirement().selected_box; // Shift |selected_box| according to its position inside this component: - auto& b = global.blocks[i]; + auto& b = global_.blocks[i]; selected_box.x_min += b.x; selected_box.y_min += b.y; selected_box.x_max += b.x; @@ -177,6 +177,42 @@ 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) { + children_[i]->Select(selection_line); + i++; + } + } + } + void Check(Status* status) override { for (auto& child : children_) { child->Check(status); @@ -194,6 +230,7 @@ class Flexbox : public Node { bool need_iteration_ = true; const FlexboxConfig config_; const FlexboxConfig config_normalized_; + flexbox_helper::Global global_; }; } // namespace diff --git a/src/ftxui/dom/flexbox_helper.cpp b/src/ftxui/dom/flexbox_helper.cpp index eb3e8134e..361ae89b9 100644 --- a/src/ftxui/dom/flexbox_helper.cpp +++ b/src/ftxui/dom/flexbox_helper.cpp @@ -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 blocks; -}; - -void SetX(Global& global, std::vector lines) { - for (auto& line : lines) { +void SetX(Global& global) { + for (auto& line : global.lines) { std::vector elements; elements.reserve(line.blocks.size()); for (auto* block : line.blocks) { @@ -110,19 +116,24 @@ void SetX(Global& global, std::vector 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 lines) { +void SetY(Global& g) { std::vector 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 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 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 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 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(lines.size()); + block.line = static_cast(global.lines.size()); block.line_position = static_cast(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 diff --git a/src/ftxui/dom/flexbox_helper.hpp b/src/ftxui/dom/flexbox_helper.hpp index 8173f3bf1..1c80f06ed 100644 --- a/src/ftxui/dom/flexbox_helper.hpp +++ b/src/ftxui/dom/flexbox_helper.hpp @@ -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 blocks; + int x = 0; + int y = 0; + int dim_x = 0; + int dim_y = 0; +}; + struct Global { std::vector blocks; + std::vector lines; FlexboxConfig config; int size_x; int size_y; }; + void Compute(Global& global); } // namespace ftxui::flexbox_helper