mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-11-04 13:38:14 +08:00 
			
		
		
		
	Add TakeFocus and SetActiveChild.
This allows developers to set child children component must be the currently active/focused one. This can be used to "control" where the focus is, without user interactions.
This commit is contained in:
		
				
					committed by
					
						
						Arthur Sonzogni
					
				
			
			
				
	
			
			
			
						parent
						
							114ab4ae2a
						
					
				
				
					commit
					81d79d311d
				
			@@ -58,9 +58,16 @@ Component* Component::ActiveChild() {
 | 
			
		||||
  return children_.empty() ? nullptr : children_.front();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @brief Returns if the element if the currently active child of its parent.
 | 
			
		||||
/// @ingroup component
 | 
			
		||||
bool Component::Active() {
 | 
			
		||||
  return !parent_ || parent_->ActiveChild() == this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @brief Returns if the elements if focused by the user.
 | 
			
		||||
/// True when the Component is focused by the user. An element is Focused when
 | 
			
		||||
/// it is with all its ancestors the ActiveChild() of their parents.
 | 
			
		||||
/// @ingroup component
 | 
			
		||||
bool Component::Focused() {
 | 
			
		||||
  Component* current = this;
 | 
			
		||||
  for (;;) {
 | 
			
		||||
@@ -73,6 +80,23 @@ bool Component::Focused() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @brief Make the |child| to be the "active" one.
 | 
			
		||||
/// @argument child the child to become active.
 | 
			
		||||
/// @ingroup component
 | 
			
		||||
void Component::SetActiveChild(Component*) {}
 | 
			
		||||
 | 
			
		||||
/// @brief Configure all the ancestors to give focus to this component.
 | 
			
		||||
/// @ingroup component
 | 
			
		||||
void Component::TakeFocus() {
 | 
			
		||||
  Component* child = this;
 | 
			
		||||
  Component* parent = parent_;
 | 
			
		||||
  while (parent) {
 | 
			
		||||
    parent->SetActiveChild(child);
 | 
			
		||||
    child = parent;
 | 
			
		||||
    parent = parent->parent_;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @brief Detach this children from its parent.
 | 
			
		||||
/// @see Attach
 | 
			
		||||
/// @see Detach
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,15 @@ Component* Container::ActiveChild() {
 | 
			
		||||
  return children_[selected % children_.size()];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Container::SetActiveChild(Component* child) {
 | 
			
		||||
  for(size_t i = 0; i < children_.size(); ++i) {
 | 
			
		||||
    if (children_[i] == child) {
 | 
			
		||||
      (selector_ ? *selector_ : selected_) = i;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Container::VerticalEvent(Event event) {
 | 
			
		||||
  int old_selected = selected_;
 | 
			
		||||
  if (event == Event::ArrowUp || event == Event::Character('k'))
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,211 @@ TEST(ContainerTest, HorizontalEvent) {
 | 
			
		||||
  container.OnEvent(Event::TabReverse);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(ContainerTest, VerticalEvent) {
 | 
			
		||||
  auto container = Container::Vertical();
 | 
			
		||||
  Component c0, c1, c2;
 | 
			
		||||
  container.Add(&c0);
 | 
			
		||||
  container.Add(&c1);
 | 
			
		||||
  container.Add(&c2);
 | 
			
		||||
 | 
			
		||||
  // With arrow key.
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  container.OnEvent(Event::ArrowDown);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  container.OnEvent(Event::ArrowDown);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c2);
 | 
			
		||||
  container.OnEvent(Event::ArrowDown);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c2);
 | 
			
		||||
  container.OnEvent(Event::ArrowUp);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  container.OnEvent(Event::ArrowUp);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  container.OnEvent(Event::ArrowUp);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
 | 
			
		||||
  // With arrow key in the wrong dimension.
 | 
			
		||||
  container.OnEvent(Event::ArrowLeft);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  container.OnEvent(Event::ArrowRight);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
 | 
			
		||||
  // With vim like characters.
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  container.OnEvent(Event::Character('j'));
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  container.OnEvent(Event::Character('j'));
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c2);
 | 
			
		||||
  container.OnEvent(Event::Character('j'));
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c2);
 | 
			
		||||
  container.OnEvent(Event::Character('k'));
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  container.OnEvent(Event::Character('k'));
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  container.OnEvent(Event::Character('k'));
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
 | 
			
		||||
  // With vim like characters in the wrong direction.
 | 
			
		||||
  container.OnEvent(Event::Character('h'));
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  container.OnEvent(Event::Character('l'));
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
 | 
			
		||||
  // With tab characters.
 | 
			
		||||
  container.OnEvent(Event::Tab);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  container.OnEvent(Event::Tab);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c2);
 | 
			
		||||
  container.OnEvent(Event::Tab);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  container.OnEvent(Event::Tab);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  container.OnEvent(Event::Tab);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c2);
 | 
			
		||||
  container.OnEvent(Event::TabReverse);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  container.OnEvent(Event::TabReverse);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  container.OnEvent(Event::TabReverse);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c2);
 | 
			
		||||
  container.OnEvent(Event::TabReverse);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  container.OnEvent(Event::TabReverse);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(ContainerTest, SetActiveChild) {
 | 
			
		||||
  auto container = Container::Horizontal();
 | 
			
		||||
  Component c0, c1, c2;
 | 
			
		||||
  container.Add(&c0);
 | 
			
		||||
  container.Add(&c1);
 | 
			
		||||
  container.Add(&c2);
 | 
			
		||||
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  EXPECT_TRUE(c0.Focused());
 | 
			
		||||
  EXPECT_TRUE(c0.Active());
 | 
			
		||||
  EXPECT_FALSE(c1.Focused());
 | 
			
		||||
  EXPECT_FALSE(c1.Active());
 | 
			
		||||
  EXPECT_FALSE(c2.Focused());
 | 
			
		||||
  EXPECT_FALSE(c2.Active());
 | 
			
		||||
 | 
			
		||||
  container.SetActiveChild(&c0);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  EXPECT_TRUE(c0.Focused());
 | 
			
		||||
  EXPECT_TRUE(c0.Active());
 | 
			
		||||
  EXPECT_FALSE(c1.Focused());
 | 
			
		||||
  EXPECT_FALSE(c1.Active());
 | 
			
		||||
  EXPECT_FALSE(c2.Focused());
 | 
			
		||||
  EXPECT_FALSE(c2.Active());
 | 
			
		||||
 | 
			
		||||
  container.SetActiveChild(&c1);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c1);
 | 
			
		||||
  EXPECT_FALSE(c0.Focused());
 | 
			
		||||
  EXPECT_FALSE(c0.Active());
 | 
			
		||||
  EXPECT_TRUE(c1.Focused());
 | 
			
		||||
  EXPECT_TRUE(c1.Active());
 | 
			
		||||
  EXPECT_FALSE(c2.Focused());
 | 
			
		||||
  EXPECT_FALSE(c2.Active());
 | 
			
		||||
 | 
			
		||||
  container.SetActiveChild(&c2);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c2);
 | 
			
		||||
  EXPECT_FALSE(c0.Focused());
 | 
			
		||||
  EXPECT_FALSE(c0.Active());
 | 
			
		||||
  EXPECT_FALSE(c1.Focused());
 | 
			
		||||
  EXPECT_FALSE(c1.Active());
 | 
			
		||||
  EXPECT_TRUE(c2.Focused());
 | 
			
		||||
  EXPECT_TRUE(c2.Active());
 | 
			
		||||
 | 
			
		||||
  container.SetActiveChild(&c0);
 | 
			
		||||
  EXPECT_EQ(container.ActiveChild(), &c0);
 | 
			
		||||
  EXPECT_TRUE(c0.Focused());
 | 
			
		||||
  EXPECT_TRUE(c0.Active());
 | 
			
		||||
  EXPECT_FALSE(c1.Focused());
 | 
			
		||||
  EXPECT_FALSE(c1.Active());
 | 
			
		||||
  EXPECT_FALSE(c2.Focused());
 | 
			
		||||
  EXPECT_FALSE(c2.Active());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(ContainerTest, TakeFocus) {
 | 
			
		||||
  auto c= Container::Horizontal();
 | 
			
		||||
  auto c1 = Container::Vertical();
 | 
			
		||||
  auto c2 = Container::Vertical();
 | 
			
		||||
  auto c3 = Container::Vertical();
 | 
			
		||||
  auto c11 = Container::Horizontal();
 | 
			
		||||
  auto c12 = Container::Horizontal();
 | 
			
		||||
  auto c13 = Container::Horizontal();
 | 
			
		||||
  auto c21 = Container::Horizontal();
 | 
			
		||||
  auto c22 = Container::Horizontal();
 | 
			
		||||
  auto c23 = Container::Horizontal();
 | 
			
		||||
 | 
			
		||||
  c.Add(&c1);
 | 
			
		||||
  c.Add(&c2);
 | 
			
		||||
  c.Add(&c3);
 | 
			
		||||
  c1.Add(&c11);
 | 
			
		||||
  c1.Add(&c12);
 | 
			
		||||
  c1.Add(&c13);
 | 
			
		||||
  c2.Add(&c21);
 | 
			
		||||
  c2.Add(&c22);
 | 
			
		||||
  c2.Add(&c23);
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(c.Focused());
 | 
			
		||||
  EXPECT_TRUE(c1.Focused());
 | 
			
		||||
  EXPECT_FALSE(c2.Focused());
 | 
			
		||||
  EXPECT_TRUE(c11.Focused());
 | 
			
		||||
  EXPECT_FALSE(c12.Focused());
 | 
			
		||||
  EXPECT_FALSE(c13.Focused());
 | 
			
		||||
  EXPECT_FALSE(c21.Focused());
 | 
			
		||||
  EXPECT_FALSE(c22.Focused());
 | 
			
		||||
  EXPECT_FALSE(c23.Focused());
 | 
			
		||||
  EXPECT_TRUE(c.Active());
 | 
			
		||||
  EXPECT_TRUE(c1.Active());
 | 
			
		||||
  EXPECT_FALSE(c2.Active());
 | 
			
		||||
  EXPECT_TRUE(c11.Active());
 | 
			
		||||
  EXPECT_FALSE(c12.Active());
 | 
			
		||||
  EXPECT_FALSE(c13.Active());
 | 
			
		||||
  EXPECT_TRUE(c21.Active());
 | 
			
		||||
  EXPECT_FALSE(c22.Active());
 | 
			
		||||
  EXPECT_FALSE(c23.Active());
 | 
			
		||||
 | 
			
		||||
  c22.TakeFocus();
 | 
			
		||||
  EXPECT_TRUE(c.Focused());
 | 
			
		||||
  EXPECT_FALSE(c1.Focused());
 | 
			
		||||
  EXPECT_TRUE(c2.Focused());
 | 
			
		||||
  EXPECT_FALSE(c11.Focused());
 | 
			
		||||
  EXPECT_FALSE(c12.Focused());
 | 
			
		||||
  EXPECT_FALSE(c13.Focused());
 | 
			
		||||
  EXPECT_FALSE(c21.Focused());
 | 
			
		||||
  EXPECT_TRUE(c22.Focused());
 | 
			
		||||
  EXPECT_FALSE(c23.Focused());
 | 
			
		||||
  EXPECT_TRUE(c.Active());
 | 
			
		||||
  EXPECT_FALSE(c1.Active());
 | 
			
		||||
  EXPECT_TRUE(c2.Active());
 | 
			
		||||
  EXPECT_TRUE(c11.Active());
 | 
			
		||||
  EXPECT_FALSE(c12.Active());
 | 
			
		||||
  EXPECT_FALSE(c13.Active());
 | 
			
		||||
  EXPECT_FALSE(c21.Active());
 | 
			
		||||
  EXPECT_TRUE(c22.Active());
 | 
			
		||||
  EXPECT_FALSE(c23.Active());
 | 
			
		||||
 | 
			
		||||
  c1.TakeFocus();
 | 
			
		||||
  EXPECT_TRUE(c.Focused());
 | 
			
		||||
  EXPECT_TRUE(c1.Focused());
 | 
			
		||||
  EXPECT_FALSE(c2.Focused());
 | 
			
		||||
  EXPECT_TRUE(c11.Focused());
 | 
			
		||||
  EXPECT_FALSE(c12.Focused());
 | 
			
		||||
  EXPECT_FALSE(c13.Focused());
 | 
			
		||||
  EXPECT_FALSE(c21.Focused());
 | 
			
		||||
  EXPECT_FALSE(c22.Focused());
 | 
			
		||||
  EXPECT_FALSE(c23.Focused());
 | 
			
		||||
  EXPECT_TRUE(c.Active());
 | 
			
		||||
  EXPECT_TRUE(c1.Active());
 | 
			
		||||
  EXPECT_FALSE(c2.Active());
 | 
			
		||||
  EXPECT_TRUE(c11.Active());
 | 
			
		||||
  EXPECT_FALSE(c12.Active());
 | 
			
		||||
  EXPECT_FALSE(c13.Active());
 | 
			
		||||
  EXPECT_FALSE(c21.Active());
 | 
			
		||||
  EXPECT_TRUE(c22.Active());
 | 
			
		||||
  EXPECT_FALSE(c23.Active());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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