Factorize box layout functions. (#185)

|hbox| and |vbox| are similar. They are just the same component, but
drawing in a different direction.

This patchs factorize the layout logic. Goal is to reduce code size and
reuse it for the |gridbox| dom element.

Bug: https://github.com/ArthurSonzogni/FTXUI/issues/114
This commit is contained in:
Arthur Sonzogni 2021-08-10 14:20:03 +02:00 committed by GitHub
parent 4450cca31a
commit 71299daea4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 142 additions and 167 deletions

View File

@ -46,6 +46,8 @@ add_library(dom STATIC
src/ftxui/dom/blink.cpp src/ftxui/dom/blink.cpp
src/ftxui/dom/bold.cpp src/ftxui/dom/bold.cpp
src/ftxui/dom/border.cpp src/ftxui/dom/border.cpp
src/ftxui/dom/box_helper.cpp
src/ftxui/dom/box_helper.hpp
src/ftxui/dom/clear_under.cpp src/ftxui/dom/clear_under.cpp
src/ftxui/dom/color.cpp src/ftxui/dom/color.cpp
src/ftxui/dom/composite_decorator.cpp src/ftxui/dom/composite_decorator.cpp

View File

@ -0,0 +1,85 @@
#include "ftxui/dom/box_helper.hpp"
namespace ftxui {
namespace box_helper {
namespace {
// Called when the size allowed is greater than the requested size. This
// distributes the extra spaces toward the flexible elements, in relative
// proportions.
void ComputeGrow(std::vector<Element>* elements,
int extra_space,
int flex_grow_sum) {
for (Element& element : *elements) {
int added_space =
extra_space * element.flex_grow / std::max(flex_grow_sum, 1);
extra_space -= added_space;
flex_grow_sum -= element.flex_grow;
element.size = element.min_size + added_space;
}
}
// Called when the size allowed is lower than the requested size, and the
// shrinkable element can absorbe the (negative) extra_space. This distribute
// the extra_space toward those.
void ComputeShrinkEasy(std::vector<Element>* elements,
int extra_space,
int flex_shrink_sum) {
for (Element& element : *elements) {
int added_space = extra_space * element.min_size * element.flex_shrink /
std::max(flex_shrink_sum, 1);
extra_space -= added_space;
flex_shrink_sum -= element.flex_shrink * element.min_size;
element.size = element.min_size + added_space;
}
}
// Called when the size allowed is lower than the requested size, and the
// shrinkable element can not absorbe the (negative) extra_space. This assign
// zero to shrinkable elements and distribute the remaining (negative)
// extra_space toward the other non shrinkable elements.
void ComputeShrinkHard(std::vector<Element>* elements,
int extra_space,
int size) {
for (Element& element : *elements) {
if (element.flex_shrink) {
element.size = 0;
continue;
}
int added_space = extra_space * element.min_size / std::max(1, size);
extra_space -= added_space;
size -= element.min_size;
element.size = element.min_size + added_space;
}
}
} // namespace
void Compute(std::vector<Element>* elements, int target_size) {
int size = 0;
int flex_grow_sum = 0;
int flex_shrink_sum = 0;
int flex_shrink_size = 0;
for (auto& element : *elements) {
flex_grow_sum += element.flex_grow;
flex_shrink_sum += element.min_size * element.flex_shrink;
if (element.flex_shrink)
flex_shrink_size += element.min_size;
size += element.min_size;
}
int extra_space = target_size - size;
if (extra_space >= 0)
ComputeGrow(elements, extra_space, flex_grow_sum);
else if (flex_shrink_size + extra_space >= 0)
ComputeShrinkEasy(elements, extra_space, flex_shrink_sum);
else
ComputeShrinkHard(elements, extra_space + flex_shrink_size,
size - flex_shrink_size);
}
} // namespace box_helper
} // namespace ftxui

View File

@ -0,0 +1,24 @@
#ifndef FTXUI_DOM_BOX_HELPER_HPP
#define FTXUI_DOM_BOX_HELPER_HPP
#include <vector>
namespace ftxui {
namespace box_helper {
struct Element {
// Input:
int min_size = 0;
int flex_grow = 0;
int flex_shrink = 0;
// Output;
int size = 0;
};
void Compute(std::vector<Element>* elements, int target_size);
} // namespace box_helper
} // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_BOX_HELPER_HPP */

View File

@ -3,6 +3,7 @@
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/dom/box_helper.hpp" // for Box
#include "ftxui/dom/elements.hpp" // for Element, Elements, hbox #include "ftxui/dom/elements.hpp" // for Element, Elements, hbox
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
@ -38,92 +39,23 @@ class HBox : public Node {
void SetBox(Box box) override { void SetBox(Box box) override {
Node::SetBox(box); Node::SetBox(box);
int space = box.x_max - box.x_min + 1; std::vector<box_helper::Element> elements(children_.size());
int extra_space = space - requirement_.min_x; for (size_t i = 0; i < children_.size(); ++i) {
auto& element = elements[i];
int size = 0; const auto& requirement = children_[i]->requirement();
int flex_grow_sum = 0; element.min_size = requirement.min_x;
int flex_shrink_sum = 0; element.flex_grow = requirement.flex_grow_x;
int flex_shrink_size = 0; element.flex_shrink = requirement.flex_shrink_x;
for (auto& child : children_) {
const Requirement& r = child->requirement();
flex_grow_sum += r.flex_grow_x;
flex_shrink_sum += r.min_x * r.flex_shrink_x;
if (r.flex_shrink_x) {
flex_shrink_size += r.min_x;
}
size += r.min_x;
} }
int target_size = box.x_max - box.x_min + 1;
box_helper::Compute(&elements, target_size);
if (extra_space >= 0)
SetBoxGrow(box, extra_space, flex_grow_sum);
else if (flex_shrink_size + extra_space >= 0)
SetBoxShrinkEasy(box, extra_space, flex_shrink_sum);
else
SetBoxShrinkHard(box, extra_space + flex_shrink_size,
size - flex_shrink_size);
}
void SetBoxGrow(Box box, int extra_space, int flex_grow_sum) {
int x = box.x_min; int x = box.x_min;
for (auto& child : children_) { for (size_t i = 0; i < children_.size(); ++i) {
Box child_box = box; box.x_min = x;
const Requirement& r = child->requirement(); box.x_max = x + elements[i].size - 1;
children_[i]->SetBox(box);
int added_space = x = box.x_max + 1;
extra_space * r.flex_grow_x / std::max(flex_grow_sum, 1);
extra_space -= added_space;
flex_grow_sum -= r.flex_grow_x;
child_box.x_min = x;
child_box.x_max = x + r.min_x + added_space - 1;
child->SetBox(child_box);
x = child_box.x_max + 1;
}
}
void SetBoxShrinkEasy(Box box, int extra_space, int flex_shrink_sum) {
int x = box.x_min;
for (auto& child : children_) {
Box child_box = box;
const Requirement& r = child->requirement();
int added_space = extra_space * r.min_x * r.flex_shrink_x /
std::max(flex_shrink_sum, 1);
extra_space -= added_space;
flex_shrink_sum -= r.flex_shrink_x * r.min_x;
child_box.x_min = x;
child_box.x_max = x + r.min_x + added_space - 1;
child->SetBox(child_box);
x = child_box.x_max + 1;
}
}
void SetBoxShrinkHard(Box box, int extra_space, int size) {
int x = box.x_min;
for (auto& child : children_) {
Box child_box = box;
const Requirement& r = child->requirement();
if (r.flex_shrink_x) {
child_box.x_min = x;
child_box.x_max = x - 1;
child->SetBox(child_box);
continue;
}
int added_space = extra_space * r.min_x / std::max(1, size);
extra_space -= added_space;
size -= r.min_x;
child_box.x_min = x;
child_box.x_max = x + r.min_x + added_space - 1;
child->SetBox(child_box);
x = child_box.x_max + 1;
} }
} }
}; };

View File

@ -3,6 +3,7 @@
#include <utility> // for move #include <utility> // for move
#include <vector> // for vector #include <vector> // for vector
#include "ftxui/dom/box_helper.hpp" // for Box
#include "ftxui/dom/elements.hpp" // for Element, Elements, vbox #include "ftxui/dom/elements.hpp" // for Element, Elements, vbox
#include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" // for Requirement #include "ftxui/dom/requirement.hpp" // for Requirement
@ -38,92 +39,23 @@ class VBox : public Node {
void SetBox(Box box) override { void SetBox(Box box) override {
Node::SetBox(box); Node::SetBox(box);
int space = box.y_max - box.y_min + 1; std::vector<box_helper::Element> elements(children_.size());
int extra_space = space - requirement_.min_y; for (size_t i = 0; i < children_.size(); ++i) {
auto& element = elements[i];
int size = 0; const auto& requirement = children_[i]->requirement();
int flex_grow_sum = 0; element.min_size = requirement.min_y;
int flex_shrink_sum = 0; element.flex_grow = requirement.flex_grow_y;
int flex_shrink_size = 0; element.flex_shrink = requirement.flex_shrink_y;
for (auto& child : children_) {
const Requirement& r = child->requirement();
flex_grow_sum += r.flex_grow_y;
flex_shrink_sum += r.min_y * r.flex_shrink_y;
if (r.flex_shrink_y) {
flex_shrink_size += r.min_y;
}
size += r.min_y;
} }
int target_size = box.y_max - box.y_min + 1;
box_helper::Compute(&elements, target_size);
if (extra_space >= 0) int x = box.x_min;
SetBoxGrow(box, extra_space, flex_grow_sum); for (size_t i = 0; i < children_.size(); ++i) {
else if (flex_shrink_size + extra_space >= 0) box.y_min = x;
SetBoxShrinkEasy(box, extra_space, flex_shrink_sum); box.y_max = x + elements[i].size - 1;
else children_[i]->SetBox(box);
SetBoxShrinkHard(box, extra_space + flex_shrink_size, x = box.y_max + 1;
size - flex_shrink_size);
}
void SetBoxGrow(Box box, int extra_space, int flex_grow_sum) {
int y = box.y_min;
for (auto& child : children_) {
Box child_box = box;
const Requirement& r = child->requirement();
int added_space =
extra_space * r.flex_grow_y / std::max(flex_grow_sum, 1);
extra_space -= added_space;
flex_grow_sum -= r.flex_grow_y;
child_box.y_min = y;
child_box.y_max = y + r.min_y + added_space - 1;
child->SetBox(child_box);
y = child_box.y_max + 1;
}
}
void SetBoxShrinkEasy(Box box, int extra_space, int flex_shrink_sum) {
int y = box.y_min;
for (auto& child : children_) {
Box child_box = box;
const Requirement& r = child->requirement();
int added_space = extra_space * r.min_y * r.flex_shrink_y /
std::max(flex_shrink_sum, 1);
extra_space -= added_space;
flex_shrink_sum -= r.flex_shrink_y * r.min_y;
child_box.y_min = y;
child_box.y_max = y + r.min_y + added_space - 1;
child->SetBox(child_box);
y = child_box.y_max + 1;
}
}
void SetBoxShrinkHard(Box box, int extra_space, int size) {
int y = box.y_min;
for (auto& child : children_) {
Box child_box = box;
const Requirement& r = child->requirement();
if (r.flex_shrink_y) {
child_box.y_min = y;
child_box.y_max = y - 1;
child->SetBox(child_box);
continue;
}
int added_space = extra_space * r.min_y / std::max(1, size);
extra_space -= added_space;
size -= r.min_y;
child_box.y_min = y;
child_box.y_max = y + r.min_y + added_space - 1;
child->SetBox(child_box);
y = child_box.y_max + 1;
} }
} }
}; };