From 71299daea46f8f3c6ab61fb072d186748c3da632 Mon Sep 17 00:00:00 2001 From: Arthur Sonzogni Date: Tue, 10 Aug 2021 14:20:03 +0200 Subject: [PATCH] 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 --- CMakeLists.txt | 2 + src/ftxui/dom/box_helper.cpp | 85 +++++++++++++++++++++++++++++ src/ftxui/dom/box_helper.hpp | 24 +++++++++ src/ftxui/dom/hbox.cpp | 98 ++++++---------------------------- src/ftxui/dom/vbox.cpp | 100 ++++++----------------------------- 5 files changed, 142 insertions(+), 167 deletions(-) create mode 100644 src/ftxui/dom/box_helper.cpp create mode 100644 src/ftxui/dom/box_helper.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 67a3bd8..d2ad461 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,8 @@ add_library(dom STATIC src/ftxui/dom/blink.cpp src/ftxui/dom/bold.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/color.cpp src/ftxui/dom/composite_decorator.cpp diff --git a/src/ftxui/dom/box_helper.cpp b/src/ftxui/dom/box_helper.cpp new file mode 100644 index 0000000..d90da2a --- /dev/null +++ b/src/ftxui/dom/box_helper.cpp @@ -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* 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* 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* 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* 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 diff --git a/src/ftxui/dom/box_helper.hpp b/src/ftxui/dom/box_helper.hpp new file mode 100644 index 0000000..9e0ebda --- /dev/null +++ b/src/ftxui/dom/box_helper.hpp @@ -0,0 +1,24 @@ +#ifndef FTXUI_DOM_BOX_HELPER_HPP +#define FTXUI_DOM_BOX_HELPER_HPP + +#include + +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* elements, int target_size); + +} // namespace box_helper +} // namespace ftxui + +#endif /* end of include guard: FTXUI_DOM_BOX_HELPER_HPP */ diff --git a/src/ftxui/dom/hbox.cpp b/src/ftxui/dom/hbox.cpp index 092e38f..55bc115 100644 --- a/src/ftxui/dom/hbox.cpp +++ b/src/ftxui/dom/hbox.cpp @@ -3,6 +3,7 @@ #include // for move #include // for vector +#include "ftxui/dom/box_helper.hpp" // for Box #include "ftxui/dom/elements.hpp" // for Element, Elements, hbox #include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/requirement.hpp" // for Requirement @@ -38,92 +39,23 @@ class HBox : public Node { void SetBox(Box box) override { Node::SetBox(box); - int space = box.x_max - box.x_min + 1; - int extra_space = space - requirement_.min_x; - - int size = 0; - int flex_grow_sum = 0; - int flex_shrink_sum = 0; - int flex_shrink_size = 0; - 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; + std::vector elements(children_.size()); + for (size_t i = 0; i < children_.size(); ++i) { + auto& element = elements[i]; + const auto& requirement = children_[i]->requirement(); + element.min_size = requirement.min_x; + element.flex_grow = requirement.flex_grow_x; + element.flex_shrink = requirement.flex_shrink_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; - for (auto& child : children_) { - Box child_box = box; - const Requirement& r = child->requirement(); - - int added_space = - 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; + for (size_t i = 0; i < children_.size(); ++i) { + box.x_min = x; + box.x_max = x + elements[i].size - 1; + children_[i]->SetBox(box); + x = box.x_max + 1; } } }; diff --git a/src/ftxui/dom/vbox.cpp b/src/ftxui/dom/vbox.cpp index 1ed02d8..6dd26e7 100644 --- a/src/ftxui/dom/vbox.cpp +++ b/src/ftxui/dom/vbox.cpp @@ -3,6 +3,7 @@ #include // for move #include // for vector +#include "ftxui/dom/box_helper.hpp" // for Box #include "ftxui/dom/elements.hpp" // for Element, Elements, vbox #include "ftxui/dom/node.hpp" // for Node #include "ftxui/dom/requirement.hpp" // for Requirement @@ -38,92 +39,23 @@ class VBox : public Node { void SetBox(Box box) override { Node::SetBox(box); - int space = box.y_max - box.y_min + 1; - int extra_space = space - requirement_.min_y; - - int size = 0; - int flex_grow_sum = 0; - int flex_shrink_sum = 0; - int flex_shrink_size = 0; - 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; + std::vector elements(children_.size()); + for (size_t i = 0; i < children_.size(); ++i) { + auto& element = elements[i]; + const auto& requirement = children_[i]->requirement(); + element.min_size = requirement.min_y; + element.flex_grow = requirement.flex_grow_y; + element.flex_shrink = requirement.flex_shrink_y; } + int target_size = box.y_max - box.y_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 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; + int x = box.x_min; + for (size_t i = 0; i < children_.size(); ++i) { + box.y_min = x; + box.y_max = x + elements[i].size - 1; + children_[i]->SetBox(box); + x = box.y_max + 1; } } };