diff --git a/CMakeLists.txt b/CMakeLists.txt index b9ee7a7..b6e5d8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ add_library(component STATIC include/ftxui/component/component.hpp include/ftxui/component/component_base.hpp include/ftxui/component/event.hpp - include/ftxui/component/menu.hpp include/ftxui/component/mouse.hpp include/ftxui/component/radiobox.hpp include/ftxui/component/receiver.hpp diff --git a/examples/component/menu.cpp b/examples/component/menu.cpp index 00d2f75..fa71547 100644 --- a/examples/component/menu.cpp +++ b/examples/component/menu.cpp @@ -5,7 +5,6 @@ #include "ftxui/component/captured_mouse.hpp" // for ftxui #include "ftxui/component/component.hpp" // for Menu -#include "ftxui/component/menu.hpp" // for MenuBase #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive int main(int argc, const char* argv[]) { diff --git a/examples/component/menu2.cpp b/examples/component/menu2.cpp index af79010..dbf4122 100644 --- a/examples/component/menu2.cpp +++ b/examples/component/menu2.cpp @@ -6,7 +6,6 @@ #include "ftxui/component/captured_mouse.hpp" // for ftxui #include "ftxui/component/component.hpp" // for Menu, Horizontal, Renderer #include "ftxui/component/component_base.hpp" // for ComponentBase -#include "ftxui/component/menu.hpp" // for MenuBase #include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive #include "ftxui/dom/elements.hpp" // for text, separator, bold, hcenter, vbox, hbox, gauge, Element, operator|, border #include "ftxui/screen/string.hpp" // for to_wstring diff --git a/examples/component/menu_style.cpp b/examples/component/menu_style.cpp index 1eb2bee..24cf2cf 100644 --- a/examples/component/menu_style.cpp +++ b/examples/component/menu_style.cpp @@ -7,7 +7,6 @@ #include "ftxui/component/captured_mouse.hpp" // for ftxui #include "ftxui/component/component.hpp" // for Menu, Horizontal, Renderer #include "ftxui/component/component_base.hpp" // for ComponentBase -#include "ftxui/component/menu.hpp" // for MenuBase #include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive, Component #include "ftxui/dom/elements.hpp" // for operator|, Element, separator, bgcolor, color, flex, Decorator, bold, hbox, border, dim #include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::BlueLight, Color::Red, Color::Yellow diff --git a/include/ftxui/component/component_options.hpp b/include/ftxui/component/component_options.hpp index d9d0a5a..61cdcb6 100644 --- a/include/ftxui/component/component_options.hpp +++ b/include/ftxui/component/component_options.hpp @@ -18,6 +18,8 @@ struct MenuOption { std::function on_change = [] {}; /// Called when the user presses enter. std::function on_enter = [] {}; + + Ref focused_entry = 0; }; /// @brief Option for the Button component. diff --git a/include/ftxui/component/menu.hpp b/include/ftxui/component/menu.hpp deleted file mode 100644 index 0b56f05..0000000 --- a/include/ftxui/component/menu.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef FTXUI_COMPONENT_MENU -#define FTXUI_COMPONENT_MENU - -#include // for function -#include // for wstring -#include // for vector - -#include "ftxui/component/component.hpp" // for Component -#include "ftxui/component/component_base.hpp" // for ComponentBase -#include "ftxui/component/component_options.hpp" // for Component -#include "ftxui/dom/elements.hpp" // for Element, Decorator, operator|, bold, inverted, nothing -#include "ftxui/screen/box.hpp" // for Box - -namespace ftxui { -struct Event; - -/// @brief A list of items. The user can navigate through them. -/// @ingroup component -class MenuBase : public ComponentBase { - public: - // Access this interface from a Component - static MenuBase* From(Component component); - - // Constructor. - MenuBase(const std::vector* entries, - int* selected_, - Ref option = {}); - ~MenuBase() override = default; - - // State. - int focused = 0; - - // Component implementation. - Element Render() override; - bool OnEvent(Event) override; - - protected: - const std::vector* const entries_; - int* selected_ = 0; - Ref option_; - - bool OnMouseEvent(Event); - - std::vector boxes_; -}; - -} // namespace ftxui - -#endif /* end of include guard: FTXUI_COMPONENT_MENU */ - -// 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. diff --git a/src/ftxui/component/menu.cpp b/src/ftxui/component/menu.cpp index e621350..c4b0495 100644 --- a/src/ftxui/component/menu.cpp +++ b/src/ftxui/component/menu.cpp @@ -5,13 +5,110 @@ #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/component.hpp" // for CapturedMouse +#include "ftxui/component/component.hpp" // for Component #include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Return, Event::Tab, Event::TabReverse -#include "ftxui/component/menu.hpp" #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released #include "ftxui/component/screen_interactive.hpp" // for Component namespace ftxui { +/// @brief A list of items. The user can navigate through them. +/// @ingroup component +class MenuBase : public ComponentBase { + public: + MenuBase(const std::vector* entries, + int* selected, + Ref option) + : entries_(entries), selected_(selected), option_(option) {} + + Element Render() { + Elements elements; + bool is_menu_focused = Focused(); + boxes_.resize(entries_->size()); + for (size_t i = 0; i < entries_->size(); ++i) { + bool is_focused = (focused_entry() == int(i)) && is_menu_focused; + bool is_selected = (*selected_ == int(i)); + + auto style = is_selected ? (is_focused ? option_->style_selected_focused + : option_->style_selected) + : (is_focused ? option_->style_focused + : option_->style_normal); + auto focus_management = !is_selected ? nothing + : is_menu_focused ? focus + : select; + auto icon = is_selected ? L"> " : L" "; + elements.push_back(text(icon + entries_->at(i)) | style | + focus_management | reflect(boxes_[i])); + } + return vbox(std::move(elements)); + } + + bool OnEvent(Event event) { + if (!CaptureMouse(event)) + return false; + if (event.is_mouse()) + return OnMouseEvent(event); + + if (!Focused()) + return false; + + int old_selected = *selected_; + if (event == Event::ArrowUp || event == Event::Character('k')) + (*selected_)--; + if (event == Event::ArrowDown || event == Event::Character('j')) + (*selected_)++; + if (event == Event::Tab && entries_->size()) + *selected_ = (*selected_ + 1) % entries_->size(); + if (event == Event::TabReverse && entries_->size()) + *selected_ = (*selected_ + entries_->size() - 1) % entries_->size(); + + *selected_ = std::max(0, std::min(int(entries_->size()) - 1, *selected_)); + + if (*selected_ != old_selected) { + focused_entry() = *selected_; + option_->on_change(); + return true; + } + + if (event == Event::Return) { + option_->on_enter(); + return true; + } + + return false; + } + + bool OnMouseEvent(Event event) { + if (!CaptureMouse(event)) + return false; + for (int i = 0; i < int(boxes_.size()); ++i) { + if (!boxes_[i].Contain(event.mouse().x, event.mouse().y)) + continue; + + TakeFocus(); + focused_entry() = i; + if (event.mouse().button == Mouse::Left && + event.mouse().motion == Mouse::Released) { + if (*selected_ != i) { + *selected_ = i; + option_->on_change(); + } + return true; + } + } + return false; + } + + int& focused_entry() { return option_->focused_entry(); } + + protected: + const std::vector* const entries_; + int* selected_ = 0; + Ref option_; + + std::vector boxes_; +}; + /// @brief A list of text. The focused element is selected. /// @param entries The list of entries in the menu. /// @param selected The index of the currently selected element. @@ -45,94 +142,6 @@ Component Menu(const std::vector* entries, return Make(entries, selected, std::move(option)); } -// static -MenuBase* MenuBase::From(Component component) { - return static_cast(component.get()); -} - -MenuBase::MenuBase(const std::vector* entries, - int* selected, - Ref option) - : entries_(entries), selected_(selected), option_(option) {} - -Element MenuBase::Render() { - Elements elements; - bool is_menu_focused = Focused(); - boxes_.resize(entries_->size()); - for (size_t i = 0; i < entries_->size(); ++i) { - bool is_focused = (focused == int(i)) && is_menu_focused; - bool is_selected = (*selected_ == int(i)); - - auto style = is_selected ? (is_focused ? option_->style_selected_focused - : option_->style_selected) - : (is_focused ? option_->style_focused - : option_->style_normal); - auto focus_management = !is_selected ? nothing - : is_menu_focused ? focus - : select; - auto icon = is_selected ? L"> " : L" "; - elements.push_back(text(icon + entries_->at(i)) | style | focus_management | - reflect(boxes_[i])); - } - return vbox(std::move(elements)); -} - -bool MenuBase::OnEvent(Event event) { - if (!CaptureMouse(event)) - return false; - if (event.is_mouse()) - return OnMouseEvent(event); - - if (!Focused()) - return false; - - int old_selected = *selected_; - if (event == Event::ArrowUp || event == Event::Character('k')) - (*selected_)--; - if (event == Event::ArrowDown || event == Event::Character('j')) - (*selected_)++; - if (event == Event::Tab && entries_->size()) - *selected_ = (*selected_ + 1) % entries_->size(); - if (event == Event::TabReverse && entries_->size()) - *selected_ = (*selected_ + entries_->size() - 1) % entries_->size(); - - *selected_ = std::max(0, std::min(int(entries_->size()) - 1, *selected_)); - - if (*selected_ != old_selected) { - focused = *selected_; - option_->on_change(); - return true; - } - - if (event == Event::Return) { - option_->on_enter(); - return true; - } - - return false; -} - -bool MenuBase::OnMouseEvent(Event event) { - if (!CaptureMouse(event)) - return false; - for (int i = 0; i < int(boxes_.size()); ++i) { - if (!boxes_[i].Contain(event.mouse().x, event.mouse().y)) - continue; - - TakeFocus(); - focused = i; - if (event.mouse().button == Mouse::Left && - event.mouse().motion == Mouse::Released) { - if (*selected_ != i) { - *selected_ = i; - option_->on_change(); - } - return true; - } - } - return false; -} - } // namespace ftxui // Copyright 2020 Arthur Sonzogni. All rights reserved.