mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-11-04 05:28:15 +08:00 
			
		
		
		
	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:
		@@ -105,7 +105,11 @@ bool ComponentBase::OnEvent(Event event) {
 | 
			
		||||
/// @return the currently Active child.
 | 
			
		||||
/// @ingroup component
 | 
			
		||||
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.
 | 
			
		||||
@@ -128,14 +132,15 @@ bool ComponentBase::Active() const {
 | 
			
		||||
 | 
			
		||||
/// @brief Returns if the elements if focused by the user.
 | 
			
		||||
/// 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
 | 
			
		||||
bool ComponentBase::Focused() const {
 | 
			
		||||
  auto current = this;
 | 
			
		||||
  while (current && current->Active()) {
 | 
			
		||||
    current = current->parent_;
 | 
			
		||||
  }
 | 
			
		||||
  return !current;
 | 
			
		||||
  return !current && Focusable();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @brief Make the |child| to be the "active" one.
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include <memory>  // for shared_ptr, __shared_ptr_access, allocator, make_shared
 | 
			
		||||
 | 
			
		||||
#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 "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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
// Use of this source code is governed by the MIT license that can be found in
 | 
			
		||||
// the LICENSE file.
 | 
			
		||||
 
 | 
			
		||||
@@ -190,6 +190,12 @@ class TabContainer : public ContainerBase {
 | 
			
		||||
    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 {
 | 
			
		||||
    return ActiveChild()->OnEvent(event);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -305,6 +305,34 @@ TEST(ContainerTest, TakeFocus) {
 | 
			
		||||
  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.
 | 
			
		||||
// Use of this source code is governed by the MIT license that can be found in
 | 
			
		||||
// the LICENSE file.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user