From c34494ce265940054a0c288efa8f85576341abf3 Mon Sep 17 00:00:00 2001 From: Felix Heitmann <75343504+felixjulianheitmann@users.noreply.github.com> Date: Thu, 15 Jul 2021 15:29:33 +0200 Subject: [PATCH] Allows components to remove a child or access to children in general (#152) Allows components to remove a child or access to children in general. Co-authored-by: Felix Heitmann Co-authored-by: ArthurSonzogni --- include/ftxui/component/component_base.hpp | 7 +- src/ftxui/component/component.cpp | 58 ++++++--- src/ftxui/component/component_test.cpp | 129 ++++++++++++++++++++- 3 files changed, 171 insertions(+), 23 deletions(-) diff --git a/include/ftxui/component/component_base.hpp b/include/ftxui/component/component_base.hpp index 4e8fd63..a8b68a5 100644 --- a/include/ftxui/component/component_base.hpp +++ b/include/ftxui/component/component_base.hpp @@ -26,9 +26,13 @@ class ComponentBase { ComponentBase() = default; virtual ~ComponentBase(); - // ComponentBase hierarchy. + // Component hierarchy: ComponentBase* Parent(); + Component& ChildAt(size_t i); + size_t ChildCount() const; void Add(Component children); + void Detach(); + void DetachAllChildren(); // Renders the component. virtual Element Render(); @@ -67,7 +71,6 @@ class ComponentBase { private: ComponentBase* parent_ = nullptr; - void Detach(); }; using Component = std::shared_ptr; diff --git a/src/ftxui/component/component.cpp b/src/ftxui/component/component.cpp index e603c7f..bc90775 100644 --- a/src/ftxui/component/component.cpp +++ b/src/ftxui/component/component.cpp @@ -1,4 +1,6 @@ +#include // for size_t #include // for find_if, max +#include // for assert #include // for begin, end #include // for move @@ -24,7 +26,6 @@ ComponentBase::~ComponentBase() { } /// @brief Return the parent ComponentBase, or nul if any. -/// @see Attach /// @see Detach /// @see Parent /// @ingroup component @@ -32,7 +33,20 @@ ComponentBase* ComponentBase::Parent() { return parent_; } -/// @brief Add a children. +/// @brief Access the child at index `i`. +/// @ingroup component +Component& ComponentBase::ChildAt(size_t i) { + assert(i < ChildCount()); + return children_[i]; +} + +/// @brief Returns the number of children. +/// @ingroup component +size_t ComponentBase::ChildCount() const { + return children_.size(); +} + +/// @brief Add a child. /// @@param child The child to be attached. /// @ingroup component void ComponentBase::Add(Component child) { @@ -41,6 +55,29 @@ void ComponentBase::Add(Component child) { children_.push_back(std::move(child)); } +/// @brief Detach this child from its parent. +/// @see Detach +/// @see Parent +/// @ingroup component +void ComponentBase::Detach() { + if (!parent_) + return; + auto it = std::find_if(std::begin(parent_->children_), // + std::end(parent_->children_), // + [this](const Component& that) { // + return this == that.get(); + }); + parent_->children_.erase(it); + parent_ = nullptr; +} + +/// @brief Remove all children. +/// @ingroup component +void ComponentBase::DetachAllChildren() { + while (!children_.empty()) + children_[0]->Detach(); +} + /// @brief Draw the component. /// Build a ftxui::Element to be drawn on the ftxi::Screen representing this /// ftxui::ComponentBase. @@ -129,23 +166,6 @@ CapturedMouse ComponentBase::CaptureMouse(const Event& event) { return event.screen_->CaptureMouse(); } -/// @brief Detach this children from its parent. -/// @see Attach -/// @see Detach -/// @see Parent -/// @ingroup component -void ComponentBase::Detach() { - if (!parent_) - return; - auto it = std::find_if(std::begin(parent_->children_), // - std::end(parent_->children_), // - [this](const Component& that) { // - return this == that.get(); - }); - parent_->children_.erase(it); - parent_ = nullptr; -} - } // namespace ftxui // Copyright 2020 Arthur Sonzogni. All rights reserved. diff --git a/src/ftxui/component/component_test.cpp b/src/ftxui/component/component_test.cpp index 5e1813c..4cef0e7 100644 --- a/src/ftxui/component/component_test.cpp +++ b/src/ftxui/component/component_test.cpp @@ -1,8 +1,10 @@ -#include // for shared_ptr, allocator, make_shared, __shared_ptr_access +#include // for Message +#include // for TestPartResult +#include // for shared_ptr, __shared_ptr_access, allocator, make_shared #include "ftxui/component/captured_mouse.hpp" // for ftxui #include "ftxui/component/component_base.hpp" // for ComponentBase, Component -#include "gtest/gtest_pred_impl.h" // for Test, SuiteApiResolver, TEST, TestFactoryImpl +#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, SuiteApiResolver, TEST, TestFactoryImpl using namespace ftxui; @@ -30,6 +32,129 @@ TEST(ContainerTest, DeleteChildFirst) { parent.reset(); } +TEST(ContainerTest, Detach) { + auto parent = Make(); + auto child_1 = Make(); + auto child_2 = Make(); + auto child_3 = Make(); + parent->Add(child_1); + parent->Add(child_2); + parent->Add(child_3); + + EXPECT_EQ(parent->ChildCount(), 3u); + EXPECT_EQ(child_1->Parent(), parent.get()); + EXPECT_EQ(child_2->Parent(), parent.get()); + EXPECT_EQ(child_3->Parent(), parent.get()); + + child_2->Detach(); + + EXPECT_EQ(parent->ChildCount(), 2u); + EXPECT_EQ(child_1->Parent(), parent.get()); + EXPECT_EQ(child_2->Parent(), nullptr); + EXPECT_EQ(child_3->Parent(), parent.get()); + + child_2->Detach(); + + EXPECT_EQ(parent->ChildCount(), 2u); + EXPECT_EQ(child_1->Parent(), parent.get()); + EXPECT_EQ(child_2->Parent(), nullptr); + EXPECT_EQ(child_3->Parent(), parent.get()); + + child_1->Detach(); + + EXPECT_EQ(parent->ChildCount(), 1u); + EXPECT_EQ(child_1->Parent(), nullptr); + EXPECT_EQ(child_2->Parent(), nullptr); + EXPECT_EQ(child_3->Parent(), parent.get()); + + child_3->Detach(); + + EXPECT_EQ(parent->ChildCount(), 0u); + EXPECT_EQ(child_1->Parent(), nullptr); + EXPECT_EQ(child_2->Parent(), nullptr); + EXPECT_EQ(child_3->Parent(), nullptr); +} + +TEST(ContainerTest, DetachAllChild) { + auto parent = Make(); + auto child_1 = Make(); + auto child_2 = Make(); + auto child_3 = Make(); + parent->Add(child_1); + parent->Add(child_2); + parent->Add(child_3); + + EXPECT_EQ(parent->ChildCount(), 3u); + EXPECT_EQ(child_1->Parent(), parent.get()); + EXPECT_EQ(child_2->Parent(), parent.get()); + EXPECT_EQ(child_3->Parent(), parent.get()); + + parent->DetachAllChild(); + + EXPECT_EQ(parent->ChildCount(), 0u); + EXPECT_EQ(child_1->Parent(), nullptr); + EXPECT_EQ(child_2->Parent(), nullptr); + EXPECT_EQ(child_3->Parent(), nullptr); +} + +TEST(ContainerTest, Add) { + auto parent = Make(); + auto child_1 = Make(); + auto child_2 = Make(); + + EXPECT_EQ(parent->ChildCount(), 0u); + EXPECT_EQ(child_1->Parent(), nullptr); + EXPECT_EQ(child_2->Parent(), nullptr); + + parent->Add(child_1); + + EXPECT_EQ(parent->ChildCount(), 1u); + EXPECT_EQ(child_1->Parent(), parent.get()); + EXPECT_EQ(child_2->Parent(), nullptr); + + parent->Add(child_1); + + EXPECT_EQ(parent->ChildCount(), 1u); + EXPECT_EQ(child_1->Parent(), parent.get()); + EXPECT_EQ(child_2->Parent(), nullptr); + + parent->Add(child_2); + + EXPECT_EQ(parent->ChildCount(), 2u); + EXPECT_EQ(child_1->Parent(), parent.get()); + EXPECT_EQ(child_2->Parent(), parent.get()); +} + +TEST(ContainerTest, ChildAt) { + auto parent = Make(); + auto child_1 = Make(); + auto child_2 = Make(); + + EXPECT_EQ(parent->ChildCount(), 0u); + + parent->Add(child_1); + + EXPECT_EQ(parent->ChildCount(), 1u); + EXPECT_EQ(parent->ChildAt(0u), child_1); + + parent->Add(child_2); + + EXPECT_EQ(parent->ChildCount(), 2u); + EXPECT_EQ(parent->ChildAt(0u), child_1); + EXPECT_EQ(parent->ChildAt(1u), child_2); + + parent->Add(child_1); + + EXPECT_EQ(parent->ChildCount(), 2u); + EXPECT_EQ(parent->ChildAt(0u), child_2); + EXPECT_EQ(parent->ChildAt(1u), child_1); + + child_1->Detach(); + + EXPECT_EQ(parent->ChildCount(), 1u); + EXPECT_EQ(parent->ChildAt(0u), child_2); +} + // 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.