Improve ComponentBase and Container::Tab Focusable implementations (#341)

- Provide better defaults for ComponentBase `Focusable()` and
  `ActiveChild()` methods. This resolves:
  https://github.com/ArthurSonzogni/FTXUI/issues/335

- Implement `Container::Tab` 's  `Focusable()` methods. This prevents
  the users to navigate into a tab with no interactivity.
This commit is contained in:
Arthur Sonzogni 2022-02-19 11:49:12 +01:00 committed by GitHub
parent f95ed885bb
commit 20f16b3984
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 3 deletions

View File

@ -27,6 +27,9 @@ Element gaugeDirection(float ratio, GaugeDirection);
### Component ### Component
- Support SIGTSTP. (ctrl+z). - Support SIGTSTP. (ctrl+z).
- Support task posting. `ScreenInteractive::Post(Task)`. - Support task posting. `ScreenInteractive::Post(Task)`.
- **bugfix** Container::Tab implements `Focusable()`.
- **bugfix** Improved default implementations of ComponentBase `Focusable()` and
`ActiveChild()` methods.
2.0.0 2.0.0
----- -----

View File

@ -105,7 +105,11 @@ bool ComponentBase::OnEvent(Event event) {
/// @return the currently Active child. /// @return the currently Active child.
/// @ingroup component /// @ingroup component
Component ComponentBase::ActiveChild() { Component ComponentBase::ActiveChild() {
return children_.empty() ? nullptr : children_.front(); for (auto& child : children_) {
if (child->Focusable())
return child;
}
return nullptr;
} }
/// @brief Return true when the component contains focusable elements. /// @brief Return true when the component contains focusable elements.
@ -128,14 +132,15 @@ bool ComponentBase::Active() const {
/// @brief Returns if the elements if focused by the user. /// @brief Returns if the elements if focused by the user.
/// True when the ComponentBase is focused by the user. An element is Focused /// True when the ComponentBase is focused by the user. An element is Focused
/// when it is with all its ancestors the ActiveChild() of their parents. /// when it is with all its ancestors the ActiveChild() of their parents, and it
/// Focusable().
/// @ingroup component /// @ingroup component
bool ComponentBase::Focused() const { bool ComponentBase::Focused() const {
auto current = this; auto current = this;
while (current && current->Active()) { while (current && current->Active()) {
current = current->parent_; current = current->parent_;
} }
return !current; return !current && Focusable();
} }
/// @brief Make the |child| to be the "active" one. /// @brief Make the |child| to be the "active" one.

View File

@ -3,6 +3,7 @@
#include <memory> // for shared_ptr, __shared_ptr_access, allocator, make_shared #include <memory> // for shared_ptr, __shared_ptr_access, allocator, make_shared
#include "ftxui/component/captured_mouse.hpp" // for ftxui #include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Make
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component #include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, SuiteApiResolver, TEST, TestFactoryImpl #include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, SuiteApiResolver, TEST, TestFactoryImpl
@ -155,6 +156,22 @@ TEST(ContainerTest, ChildAt) {
EXPECT_EQ(parent->ChildAt(0u), child_2); EXPECT_EQ(parent->ChildAt(0u), child_2);
} }
TEST(ComponentTest, NonFocusableAreNotFocused) {
class NonFocusable : public ComponentBase {
bool Focusable() const override { return false; }
};
auto root = Make<NonFocusable>();
EXPECT_FALSE(root->Focused());
EXPECT_EQ(root->ActiveChild(), nullptr);
auto child = Make<NonFocusable>();
root->Add(child);
EXPECT_FALSE(root->Focused());
EXPECT_FALSE(child->Focused());
EXPECT_EQ(root->ActiveChild(), nullptr);
EXPECT_EQ(child->ActiveChild(), nullptr);
}
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.

View File

@ -190,6 +190,12 @@ class TabContainer : public ContainerBase {
return text("Empty container"); return text("Empty container");
} }
bool Focusable() const override {
if (children_.size() == 0)
return false;
return children_[*selector_ % children_.size()]->Focusable();
}
bool OnMouseEvent(Event event) override { bool OnMouseEvent(Event event) override {
return ActiveChild()->OnEvent(event); return ActiveChild()->OnEvent(event);
} }

View File

@ -305,6 +305,34 @@ TEST(ContainerTest, TakeFocus) {
EXPECT_FALSE(c23->Active()); EXPECT_FALSE(c23->Active());
} }
TEST(ContainerTest, TabFocusable) {
int selected = 0;
auto c = Container::Tab(
{
Focusable(),
NonFocusable(),
Focusable(),
NonFocusable(),
},
&selected);
selected = 0;
EXPECT_TRUE(c->Focusable());
EXPECT_TRUE(c->Focused());
selected = 1;
EXPECT_FALSE(c->Focusable());
EXPECT_FALSE(c->Focused());
selected = 2;
EXPECT_TRUE(c->Focusable());
EXPECT_TRUE(c->Focused());
selected = 3;
EXPECT_FALSE(c->Focusable());
EXPECT_FALSE(c->Focused());
}
// Copyright 2020 Arthur Sonzogni. All rights reserved. // Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in // Use of this source code is governed by the MIT license that can be found in
// the LICENSE file. // the LICENSE file.