mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 18:59:59 +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:
parent
f9256fa132
commit
11519ef1c6
@ -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);
|
||||||
|
|
||||||
if (global.blocks.empty()) {
|
// Reset:
|
||||||
|
requirement_.selection = Requirement::Selection::NORMAL;
|
||||||
|
requirement_.selected_box = Box();
|
||||||
requirement_.min_x = 0;
|
requirement_.min_x = 0;
|
||||||
requirement_.min_y = 0;
|
requirement_.min_y = 0;
|
||||||
|
|
||||||
|
if (global.blocks.empty()) {
|
||||||
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__)
|
||||||
|
Loading…
Reference in New Issue
Block a user