diff --git a/include/ftxui/component/component_base.hpp b/include/ftxui/component/component_base.hpp index 622af0a..97f0585 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 IndexOf(ComponentBase* child) 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..37ef884 100644 --- a/src/ftxui/component/button.cpp +++ b/src/ftxui/component/button.cpp @@ -53,6 +53,7 @@ class ButtonBase : public ComponentBase, public ButtonOption { false, active, focused_or_hover, + -1, }; auto element = (transform ? transform : DefaultTransform) // diff --git a/src/ftxui/component/checkbox.cpp b/src/ftxui/component/checkbox.cpp index c2a10f1..5ac7a7a 100644 --- a/src/ftxui/component/checkbox.cpp +++ b/src/ftxui/component/checkbox.cpp @@ -32,6 +32,7 @@ class CheckboxBase : public ComponentBase, public CheckboxOption { *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..0b373a0 100644 --- a/src/ftxui/component/component.cpp +++ b/src/ftxui/component/component.cpp @@ -51,6 +51,18 @@ size_t ComponentBase::ChildCount() const { return children_.size(); } +/// @brief Return index of child or -1 if not found. +/// @ingroup component +int ComponentBase::IndexOf(ComponentBase* child) const { + int index = 0; + for (Component c : children_) { + if (&(*c) == child) + return index; + index++; + } + return -1; +} + /// @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..667d999 100644 --- a/src/ftxui/component/menu.cpp +++ b/src/ftxui/component/menu.cpp @@ -127,6 +127,7 @@ class MenuBase : public ComponentBase, public MenuOption { false, is_selected, is_focused, + i, }; auto focus_management = (selected_focus_ != i) ? nothing @@ -630,6 +631,7 @@ Component MenuEntry(MenuEntryOption option) { false, hovered_, focused, + Parent()->IndexOf(this), }; const Element element = diff --git a/src/ftxui/component/menu_test.cpp b/src/ftxui/component/menu_test.cpp index b5465eb..c78a293 100644 --- a/src/ftxui/component/menu_test.cpp +++ b/src/ftxui/component/menu_test.cpp @@ -226,5 +226,52 @@ 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 idx = 0; idx < menu->ChildCount(); idx++) + { + auto child = menu->ChildAt(idx); + EXPECT_EQ(menu->IndexOf(&(*child)), idx); + } +} + } // namespace ftxui // NOLINTEND diff --git a/src/ftxui/component/radiobox.cpp b/src/ftxui/component/radiobox.cpp index bb77500..17b7858 100644 --- a/src/ftxui/component/radiobox.cpp +++ b/src/ftxui/component/radiobox.cpp @@ -44,6 +44,7 @@ class RadioboxBase : public ComponentBase, public RadioboxOption { selected() == i, is_selected, is_focused, + i, }; auto element = (transform ? transform : RadioboxOption::Simple().transform)(state);