Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Node::Select for flexbox. #977

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 51 additions & 14 deletions src/ftxui/dom/flexbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,34 +92,34 @@ 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;
requirement_.selected_box = Box();
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);
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -194,6 +230,7 @@ class Flexbox : public Node {
bool need_iteration_ = true;
const FlexboxConfig config_;
const FlexboxConfig config_normalized_;
flexbox_helper::Global global_;
};

} // namespace
Expand Down
59 changes: 37 additions & 22 deletions src/ftxui/dom/flexbox_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,28 +68,34 @@ 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) {
SymmetryX(g.config);
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) {
SymmetryY(g.config);
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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions src/ftxui/dom/flexbox_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace ftxui::flexbox_helper {

// A block is a rectangle in the flexbox.
struct Block {
// Input:
int min_size_x = 0;
Expand All @@ -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
Expand Down
Loading