mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-11-01 02:58:12 +08:00 
			
		
		
		
	Fix focus vs flexbox interaction. (#405)
- Fix focus in flexbox. This required resetting the focus state at the beginning of the ComputeRequirement(), because it can now run several times. This resolves:https://github.com/ArthurSonzogni/FTXUI/issues/399 - Add Box::Union. - Add a preliminary implementation of forwarding selected_box from within the flexbox.
This commit is contained in:
		| @@ -4,6 +4,13 @@ Changelog | |||||||
| current (development)  | current (development)  | ||||||
| --------------------- | --------------------- | ||||||
|  |  | ||||||
|  | ### DOM | ||||||
|  | - Bugfix: Fix `focus`/`select` when the `vbox`/`hbox`/`dbox` contains a | ||||||
|  |   `flexbox` | ||||||
|  |  | ||||||
|  | ### Screen | ||||||
|  | - Feature: add `Box::Union(a,b) -> Box` | ||||||
|  |  | ||||||
| 3.0.0 | 3.0.0 | ||||||
| ----- | ----- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ struct Box { | |||||||
|   int y_max = 0; |   int y_max = 0; | ||||||
|  |  | ||||||
|   static auto Intersection(Box a, Box b) -> Box; |   static auto Intersection(Box a, Box b) -> Box; | ||||||
|  |   static auto Union(Box a, Box b) -> Box; | ||||||
|   bool Contain(int x, int y) const; |   bool Contain(int x, int y) const; | ||||||
|   bool operator==(const Box& other) const; |   bool operator==(const Box& other) const; | ||||||
|   bool operator!=(const Box& other) const; |   bool operator!=(const Box& other) const; | ||||||
|   | |||||||
| @@ -297,8 +297,9 @@ ScreenInteractive ScreenInteractive::FitComponent() { | |||||||
| void ScreenInteractive::Post(Task task) { | void ScreenInteractive::Post(Task task) { | ||||||
|   // Task/Events sent toward inactive screen or screen waiting to become |   // Task/Events sent toward inactive screen or screen waiting to become | ||||||
|   // inactive are dropped. |   // inactive are dropped. | ||||||
|   if (!task_sender_) |   if (!task_sender_) { | ||||||
|     return; |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   task_sender_->Send(std::move(task)); |   task_sender_->Send(std::move(task)); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ class DBox : public Node { | |||||||
|     requirement_.flex_grow_y = 0; |     requirement_.flex_grow_y = 0; | ||||||
|     requirement_.flex_shrink_x = 0; |     requirement_.flex_shrink_x = 0; | ||||||
|     requirement_.flex_shrink_y = 0; |     requirement_.flex_shrink_y = 0; | ||||||
|  |     requirement_.selection = Requirement::NORMAL; | ||||||
|     for (auto& child : children_) { |     for (auto& child : children_) { | ||||||
|       child->ComputeRequirement(); |       child->ComputeRequirement(); | ||||||
|       requirement_.min_x = |       requirement_.min_x = | ||||||
|   | |||||||
| @@ -99,41 +99,63 @@ class Flexbox : public Node { | |||||||
|     } |     } | ||||||
|     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()) { | ||||||
|       requirement_.min_x = 0; |  | ||||||
|       requirement_.min_y = 0; |  | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Compute the union of all the blocks: | ||||||
|     Box box; |     Box box; | ||||||
|     box.x_min = global.blocks[0].x; |     box.x_min = global.blocks[0].x; | ||||||
|     box.y_min = global.blocks[0].y; |     box.y_min = global.blocks[0].y; | ||||||
|     box.x_max = global.blocks[0].x + global.blocks[0].dim_x; |     box.x_max = global.blocks[0].x + global.blocks[0].dim_x; | ||||||
|     box.y_max = global.blocks[0].y + global.blocks[0].dim_y; |     box.y_max = global.blocks[0].y + global.blocks[0].dim_y; | ||||||
|  |  | ||||||
|     for (auto& b : global.blocks) { |     for (auto& b : global.blocks) { | ||||||
|       box.x_min = std::min(box.x_min, b.x); |       box.x_min = std::min(box.x_min, b.x); | ||||||
|       box.y_min = std::min(box.y_min, b.y); |       box.y_min = std::min(box.y_min, b.y); | ||||||
|       box.x_max = std::max(box.x_max, b.x + b.dim_x); |       box.x_max = std::max(box.x_max, b.x + b.dim_x); | ||||||
|       box.y_max = std::max(box.y_max, b.y + b.dim_y); |       box.y_max = std::max(box.y_max, b.y + b.dim_y); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     requirement_.min_x = box.x_max - box.x_min; |     requirement_.min_x = box.x_max - box.x_min; | ||||||
|     requirement_.min_y = box.y_max - box.y_min; |     requirement_.min_y = box.y_max - box.y_min; | ||||||
|  |  | ||||||
|  |     // Find the selection: | ||||||
|  |     for (size_t i = 0; i < children_.size(); ++i) { | ||||||
|  |       if (requirement_.selection >= children_[i]->requirement().selection) { | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       requirement_.selection = children_[i]->requirement().selection; | ||||||
|  |       Box selected_box = children_[i]->requirement().selected_box; | ||||||
|  |  | ||||||
|  |       // Shift |selected_box| according to its position inside this component: | ||||||
|  |       auto& b = global.blocks[i]; | ||||||
|  |       selected_box.x_min += b.x; | ||||||
|  |       selected_box.y_min += b.y; | ||||||
|  |       selected_box.x_max += b.x; | ||||||
|  |       selected_box.y_max += b.y; | ||||||
|  |       requirement_.selected_box = Box::Intersection(selected_box, box); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void SetBox(Box box) override { |   void SetBox(Box box) override { | ||||||
|     Node::SetBox(box); |     Node::SetBox(box); | ||||||
|  |  | ||||||
|  |     int asked_previous = asked_; | ||||||
|     asked_ = std::min(asked_, IsColumnOriented() ? box.y_max - box.y_min + 1 |     asked_ = std::min(asked_, IsColumnOriented() ? box.y_max - box.y_min + 1 | ||||||
|                                                  : box.x_max - box.x_min + 1); |                                                  : box.x_max - box.x_min + 1); | ||||||
|  |     need_iteration_ = (asked_ != asked_previous); | ||||||
|  |  | ||||||
|     flexbox_helper::Global global; |     flexbox_helper::Global global; | ||||||
|     global.config = config_; |     global.config = config_; | ||||||
|     global.size_x = box.x_max - box.x_min + 1; |     global.size_x = box.x_max - box.x_min + 1; | ||||||
|     global.size_y = box.y_max - box.y_min + 1; |     global.size_y = box.y_max - box.y_min + 1; | ||||||
|     Layout(global); |     Layout(global); | ||||||
|  |  | ||||||
|     need_iteration_ = false; |  | ||||||
|     for (size_t i = 0; i < children_.size(); ++i) { |     for (size_t i = 0; i < children_.size(); ++i) { | ||||||
|       auto& child = children_[i]; |       auto& child = children_[i]; | ||||||
|       auto& b = global.blocks[i]; |       auto& b = global.blocks[i]; | ||||||
|   | |||||||
| @@ -432,6 +432,29 @@ TEST(FlexboxTest, GapY) { | |||||||
|             "       "); |             "       "); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | TEST(FlexboxTest, Focus) { | ||||||
|  |   auto document = vbox({ | ||||||
|  |     paragraph("0 -"), | ||||||
|  |     paragraph("1 -"), | ||||||
|  |     paragraph("2 -"), | ||||||
|  |     paragraph("3 -"), | ||||||
|  |     paragraph("4 -"), | ||||||
|  |     paragraph("5 -"), | ||||||
|  |     paragraph("6 -"), | ||||||
|  |     paragraph("7 -") | focus, | ||||||
|  |     paragraph("8 -"), | ||||||
|  |     paragraph("9 -"), | ||||||
|  |   }) | yframe | flex; | ||||||
|  |  | ||||||
|  |   Screen screen(1, 3); | ||||||
|  |   Render(screen, document); | ||||||
|  |   EXPECT_EQ(screen.ToString(), | ||||||
|  |             "7\r\n" | ||||||
|  |             "-\r\n" | ||||||
|  |             "8" | ||||||
|  |             ); | ||||||
|  | } | ||||||
|  |  | ||||||
| }  // namespace ftxui | }  // namespace ftxui | ||||||
|  |  | ||||||
| // Copyright 2021 Arthur Sonzogni. All rights reserved. | // Copyright 2021 Arthur Sonzogni. All rights reserved. | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ class HBox : public Node { | |||||||
|     requirement_.flex_grow_y = 0; |     requirement_.flex_grow_y = 0; | ||||||
|     requirement_.flex_shrink_x = 0; |     requirement_.flex_shrink_x = 0; | ||||||
|     requirement_.flex_shrink_y = 0; |     requirement_.flex_shrink_y = 0; | ||||||
|  |     requirement_.selection = Requirement::NORMAL; | ||||||
|     for (auto& child : children_) { |     for (auto& child : children_) { | ||||||
|       child->ComputeRequirement(); |       child->ComputeRequirement(); | ||||||
|       if (requirement_.selection < child->requirement().selection) { |       if (requirement_.selection < child->requirement().selection) { | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ class VBox : public Node { | |||||||
|     requirement_.flex_grow_y = 0; |     requirement_.flex_grow_y = 0; | ||||||
|     requirement_.flex_shrink_x = 0; |     requirement_.flex_shrink_x = 0; | ||||||
|     requirement_.flex_shrink_y = 0; |     requirement_.flex_shrink_y = 0; | ||||||
|  |     requirement_.selection = Requirement::NORMAL; | ||||||
|     for (auto& child : children_) { |     for (auto& child : children_) { | ||||||
|       child->ComputeRequirement(); |       child->ComputeRequirement(); | ||||||
|       if (requirement_.selection < child->requirement().selection) { |       if (requirement_.selection < child->requirement().selection) { | ||||||
|   | |||||||
| @@ -15,6 +15,18 @@ Box Box::Intersection(Box a, Box b) { | |||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// @return the smallest Box containing both |a| and |b|. | ||||||
|  | /// @ingroup screen | ||||||
|  | // static | ||||||
|  | Box Box::Union(Box a, Box b) { | ||||||
|  |   return Box{ | ||||||
|  |       std::min(a.x_min, b.x_min), | ||||||
|  |       std::max(a.x_max, b.x_max), | ||||||
|  |       std::min(a.y_min, b.y_min), | ||||||
|  |       std::max(a.y_max, b.y_max), | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
| /// @return whether (x,y) is contained inside the box. | /// @return whether (x,y) is contained inside the box. | ||||||
| /// @ingroup screen | /// @ingroup screen | ||||||
| bool Box::Contain(int x, int y) const { | bool Box::Contain(int x, int y) const { | ||||||
|   | |||||||
| @@ -20,8 +20,8 @@ namespace ftxui { | |||||||
|  |  | ||||||
| namespace { | namespace { | ||||||
|  |  | ||||||
| bool g_cached = false; | bool g_cached = false;                     // NOLINT | ||||||
| Terminal::Color g_cached_supported_color; | Terminal::Color g_cached_supported_color;  // NOLINT | ||||||
|  |  | ||||||
| Dimensions& FallbackSize() { | Dimensions& FallbackSize() { | ||||||
| #if defined(__EMSCRIPTEN__) | #if defined(__EMSCRIPTEN__) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Arthur Sonzogni
					Arthur Sonzogni