diff --git a/CHANGELOG.md b/CHANGELOG.md index a0afeb2..611187d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,10 @@ current (development) - Bugfix: Fix cursor position in when in the last column. See #831. - Bugfix: Fix `ResizeableSplit` keyboard navigation. Fixed by #842. - Bugfix: Fix `Menu` focus. See #841 +- Feature: Add `ComponentBase::Index()`. This allows to get the index of a + component in its parent. See #932 +- Feature: Add `EntryState::index`. This allows to get the index of a menu entry. + See #932 ### Dom - Feature: Add `hscroll_indicator`. It display an horizontal indicator diff --git a/include/ftxui/component/component_base.hpp b/include/ftxui/component/component_base.hpp index 622af0a..ef1b751 100644 --- a/include/ftxui/component/component_base.hpp +++ b/include/ftxui/component/component_base.hpp @@ -44,6 +44,7 @@ class ComponentBase { ComponentBase* Parent() const; Component& ChildAt(size_t i); size_t ChildCount() const; + int Index() const; void Add(Component children); void Detach(); void DetachAllChildren(); diff --git a/include/ftxui/component/component_options.hpp b/include/ftxui/component/component_options.hpp index b7e771e..a55a4df 100644 --- a/include/ftxui/component/component_options.hpp +++ b/include/ftxui/component/component_options.hpp @@ -25,6 +25,7 @@ struct EntryState { bool state; ///< The state of the button/checkbox/radiobox bool active; ///< Whether the entry is the active one. bool focused; ///< Whether the entry is one focused by the user. + int index; ///< Index of the entry when applicable or -1. }; struct UnderlineOption { diff --git a/src/ftxui/component/button.cpp b/src/ftxui/component/button.cpp index e2be2af..844dd39 100644 --- a/src/ftxui/component/button.cpp +++ b/src/ftxui/component/button.cpp @@ -48,11 +48,8 @@ class ButtonBase : public ComponentBase, public ButtonOption { } auto focus_management = focused ? focus : active ? select : nothing; - const EntryState state = { - *label, - false, - active, - focused_or_hover, + const EntryState state{ + *label, false, active, focused_or_hover, Index(), }; auto element = (transform ? transform : DefaultTransform) // diff --git a/src/ftxui/component/checkbox.cpp b/src/ftxui/component/checkbox.cpp index c2a10f1..b4b7f15 100644 --- a/src/ftxui/component/checkbox.cpp +++ b/src/ftxui/component/checkbox.cpp @@ -28,10 +28,7 @@ class CheckboxBase : public ComponentBase, public CheckboxOption { const bool is_active = Active(); auto focus_management = is_focused ? focus : is_active ? select : nothing; auto entry_state = EntryState{ - *label, - *checked, - is_active, - is_focused || hovered_, + *label, *checked, is_active, is_focused || hovered_, -1, }; auto element = (transform ? transform : CheckboxOption::Simple().transform)( entry_state); diff --git a/src/ftxui/component/component.cpp b/src/ftxui/component/component.cpp index 6ed4cb2..e0c38dd 100644 --- a/src/ftxui/component/component.cpp +++ b/src/ftxui/component/component.cpp @@ -51,6 +51,22 @@ size_t ComponentBase::ChildCount() const { return children_.size(); } +/// @brief Return index of the component in its parent. -1 if no parent. +/// @ingroup component +int ComponentBase::Index() const { + if (parent_ == nullptr) { + return -1; + } + int index = 0; + for (const Component& child : parent_->children_) { + if (child.get() == this) { + return index; + } + index++; + } + return -1; // Not reached. +} + /// @brief Add a child. /// @@param child The child to be attached. /// @ingroup component diff --git a/src/ftxui/component/menu.cpp b/src/ftxui/component/menu.cpp index 4fc4d0b..0f8b0fc 100644 --- a/src/ftxui/component/menu.cpp +++ b/src/ftxui/component/menu.cpp @@ -123,10 +123,7 @@ class MenuBase : public ComponentBase, public MenuOption { const bool is_selected = (selected() == i); const EntryState state = { - entries[i], - false, - is_selected, - is_focused, + entries[i], false, is_selected, is_focused, i, }; auto focus_management = (selected_focus_ != i) ? nothing @@ -625,11 +622,8 @@ Component MenuEntry(MenuEntryOption option) { const bool focused = Focused(); UpdateAnimationTarget(); - const EntryState state = { - label(), - false, - hovered_, - focused, + const EntryState state{ + label(), false, hovered_, focused, Index(), }; const Element element = diff --git a/src/ftxui/component/menu_test.cpp b/src/ftxui/component/menu_test.cpp index b5465eb..ca09946 100644 --- a/src/ftxui/component/menu_test.cpp +++ b/src/ftxui/component/menu_test.cpp @@ -226,5 +226,50 @@ TEST(MenuTest, AnimationsVertical) { } } +TEST(MenuTest, EntryIndex) { + int selected = 0; + std::vector entries = {"0", "1", "2"}; + + auto option = MenuOption::Vertical(); + option.entries = &entries; + option.selected = &selected; + option.entries_option.transform = [&](const EntryState& state) { + int curidx = std::stoi(state.label); + EXPECT_EQ(state.index, curidx); + return text(state.label); + }; + auto menu = Menu(option); + menu->OnEvent(Event::ArrowDown); + menu->OnEvent(Event::ArrowDown); + menu->OnEvent(Event::Return); + entries.resize(2); + (void)menu->Render(); +} + +TEST(MenuTest, MenuEntryIndex) { + int selected = 0; + + MenuEntryOption option; + option.transform = [&](const EntryState& state) { + int curidx = std::stoi(state.label); + EXPECT_EQ(state.index, curidx); + return text(state.label); + }; + auto menu = Container::Vertical( + { + MenuEntry("0", option), + MenuEntry("1", option), + MenuEntry("2", option), + }, + &selected); + + menu->OnEvent(Event::ArrowDown); + menu->OnEvent(Event::ArrowDown); + menu->OnEvent(Event::Return); + for (int index = 0; index < menu->ChildCount(); index++) { + EXPECT_EQ(menu->ChildAt(index)->Index(), index); + } +} + } // namespace ftxui // NOLINTEND diff --git a/src/ftxui/component/radiobox.cpp b/src/ftxui/component/radiobox.cpp index bb77500..4c823de 100644 --- a/src/ftxui/component/radiobox.cpp +++ b/src/ftxui/component/radiobox.cpp @@ -40,10 +40,7 @@ class RadioboxBase : public ComponentBase, public RadioboxOption { : is_menu_focused ? focus : select; auto state = EntryState{ - entries[i], - selected() == i, - is_selected, - is_focused, + entries[i], selected() == i, is_selected, is_focused, i, }; auto element = (transform ? transform : RadioboxOption::Simple().transform)(state);