mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-11-04 13:38:14 +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:
		@@ -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
 | 
				
			||||||
-----
 | 
					-----
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user