From ed5b4cec4952a71475747e4cf42d90dc78449fd9 Mon Sep 17 00:00:00 2001 From: Arthur Sonzogni Date: Sat, 28 May 2022 22:35:52 +0200 Subject: [PATCH] Fix: Forward `gridbox` selected box. (#408) This was discovered in: https://github.com/ArthurSonzogni/FTXUI/issues/407 --- CHANGELOG.md | 3 ++ src/ftxui/dom/flexbox_test.cpp | 4 +- src/ftxui/dom/frame.cpp | 4 +- src/ftxui/dom/gridbox.cpp | 67 ++++++++++++++++++++++------------ src/ftxui/dom/gridbox_test.cpp | 19 ++++++++++ 5 files changed, 69 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55a255e..c4a7983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ current (development) ### DOM - Bugfix: Fix `focus`/`select` when the `vbox`/`hbox`/`dbox` contains a `flexbox` +- Bugfix: Fix the selected/focused area. It used to be 1 cell larger/longer than + requested +- Bugfix: Forward the selected/focused area from the child in gridbox. ### Screen - Feature: add `Box::Union(a,b) -> Box` diff --git a/src/ftxui/dom/flexbox_test.cpp b/src/ftxui/dom/flexbox_test.cpp index b5175f6..dca5913 100644 --- a/src/ftxui/dom/flexbox_test.cpp +++ b/src/ftxui/dom/flexbox_test.cpp @@ -449,9 +449,9 @@ TEST(FlexboxTest, Focus) { Screen screen(1, 3); Render(screen, document); EXPECT_EQ(screen.ToString(), - "7\r\n" "-\r\n" - "8" + "7\r\n" + "-" ); } diff --git a/src/ftxui/dom/frame.cpp b/src/ftxui/dom/frame.cpp index b67c7bb..575bbc8 100644 --- a/src/ftxui/dom/frame.cpp +++ b/src/ftxui/dom/frame.cpp @@ -24,8 +24,8 @@ class Select : public Node { auto& selected_box = requirement_.selected_box; selected_box.x_min = 0; selected_box.y_min = 0; - selected_box.x_max = requirement_.min_x; - selected_box.y_max = requirement_.min_y; + selected_box.x_max = requirement_.min_x - 1; + selected_box.y_max = requirement_.min_y - 1; requirement_.selection = Requirement::SELECTED; }; diff --git a/src/ftxui/dom/gridbox.cpp b/src/ftxui/dom/gridbox.cpp index 9bc2197..abc12d2 100644 --- a/src/ftxui/dom/gridbox.cpp +++ b/src/ftxui/dom/gridbox.cpp @@ -13,6 +13,23 @@ namespace ftxui { class Screen; +namespace { + +// Accumulate the values of a list U[n] into v[n]. So that: +// V[0] = 0; +// V[n+1] = v[n] + U[n] +// return the sum of U[n]. +int Integrate(std::vector& elements) { + int accu = 0; + for (auto& i : elements) { + int old_accu = accu; + accu += i; + i = old_accu; + } + return accu; +} +} + class GridBox : public Node { public: explicit GridBox(std::vector lines) : lines_(std::move(lines)) { @@ -38,35 +55,37 @@ class GridBox : public Node { for (auto& line : lines_) { for (auto& cell : line) { cell->ComputeRequirement(); + } + } - // Determine focus based on the focused child. - if (requirement_.selection >= cell->requirement().selection) { + // Compute the size of each columns/row. + std::vector size_x(x_size, 0); + std::vector size_y(y_size, 0); + for (int x = 0; x < x_size; ++x) { + for (int y = 0; y < y_size; ++y) { + size_x[x] = std::max(size_x[x], lines_[y][x]->requirement().min_x); + size_y[y] = std::max(size_y[y], lines_[y][x]->requirement().min_y); + } + } + + requirement_.min_x = Integrate(size_x); + requirement_.min_y = Integrate(size_y); + + // Forward the selected/focused child state: + requirement_.selection = Requirement::NORMAL; + for (int x = 0; x < x_size; ++x) { + for (int y = 0; y < y_size; ++y) { + if (requirement_.selection >= lines_[y][x]->requirement().selection) { continue; } - requirement_.selection = cell->requirement().selection; - requirement_.selected_box = cell->requirement().selected_box; - requirement_.selected_box.x_min += requirement_.min_x; - requirement_.selected_box.x_max += requirement_.min_x; + requirement_.selection = lines_[y][x]->requirement().selection; + requirement_.selected_box = lines_[y][x]->requirement().selected_box; + requirement_.selected_box.x_min += size_x[x]; + requirement_.selected_box.x_max += size_x[x]; + requirement_.selected_box.y_min += size_y[y]; + requirement_.selected_box.y_max += size_y[y]; } } - - // Work on the x-axis. - for (int x = 0; x < x_size; ++x) { - int min_x = 0; - for (int y = 0; y < y_size; ++y) { - min_x = std::max(min_x, lines_[y][x]->requirement().min_x); - } - requirement_.min_x += min_x; - } - - // Work on the y-axis. - for (int y = 0; y < y_size; ++y) { - int min_y = 0; - for (int x = 0; x < x_size; ++x) { - min_y = std::max(min_y, lines_[y][x]->requirement().min_y); - } - requirement_.min_y += min_y; - } } void SetBox(Box box) override { diff --git a/src/ftxui/dom/gridbox_test.cpp b/src/ftxui/dom/gridbox_test.cpp index 19a8f8b..f358d4f 100644 --- a/src/ftxui/dom/gridbox_test.cpp +++ b/src/ftxui/dom/gridbox_test.cpp @@ -596,6 +596,25 @@ TEST(GridboxTest, MissingCells) { " "); } +TEST(GridboxTest, Focus) { + auto root = gridbox({ + {cell("1"), cell("2"), cell("3"), cell("4")}, + {cell("5"), cell("6"), cell("7"), cell("8")}, + {cell("9"), cell("10"), cell("11"), cell("12")}, + {cell("13"), cell("14") | focus, cell("15"), cell("16")}, + {cell("17"), cell("18"), cell("19"), cell("20")}, + }); + + root |= frame; + + Screen screen(4, 3); + Render(screen, root); + EXPECT_EQ(screen.ToString(), + "╭──╮\r\n" + "│14│\r\n" + "╰──╯"); +} + } // namespace ftxui // Copyright 2020 Arthur Sonzogni. All rights reserved.