mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-10-31 18:48:11 +08:00 
			
		
		
		
	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:
		| @@ -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 | ||||
|   | ||||
							
								
								
									
										85
									
								
								src/ftxui/dom/box_helper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/ftxui/dom/box_helper.cpp
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										24
									
								
								src/ftxui/dom/box_helper.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/ftxui/dom/box_helper.hpp
									
									
									
									
									
										Normal 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 */ | ||||
| @@ -3,6 +3,7 @@ | ||||
| #include <utility>    // for move | ||||
| #include <vector>     // 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<box_helper::Element> 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; | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| #include <utility>    // for move | ||||
| #include <vector>     // 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<box_helper::Element> 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; | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Arthur Sonzogni
					Arthur Sonzogni