diff --git a/examples/component/menu.cpp b/examples/component/menu.cpp index f68e265..00d2f75 100644 --- a/examples/component/menu.cpp +++ b/examples/component/menu.cpp @@ -19,8 +19,9 @@ int main(int argc, const char* argv[]) { }; int selected = 0; - auto menu = Menu(&entries, &selected); - MenuBase::From(menu)->on_enter = screen.ExitLoopClosure(); + MenuOption option; + option.on_enter = screen.ExitLoopClosure(); + auto menu = Menu(&entries, &selected, &option); screen.Loop(menu); diff --git a/examples/component/menu2.cpp b/examples/component/menu2.cpp index 82f7121..af79010 100644 --- a/examples/component/menu2.cpp +++ b/examples/component/menu2.cpp @@ -13,6 +13,7 @@ int main(int argc, const char* argv[]) { using namespace ftxui; + auto screen = ScreenInteractive::TerminalOutput(); std::vector left_menu_entries = { L"0%", L"10%", L"20%", L"30%", L"40%", @@ -22,10 +23,16 @@ int main(int argc, const char* argv[]) { L"0%", L"1%", L"2%", L"3%", L"4%", L"5%", L"6%", L"7%", L"8%", L"9%", L"10%", }; + + auto menu_option = MenuOption(); + menu_option.on_enter = screen.ExitLoopClosure(); + int left_menu_selected = 0; int right_menu_selected = 0; - Component left_menu_ = Menu(&left_menu_entries, &left_menu_selected); - Component right_menu_ = Menu(&right_menu_entries, &right_menu_selected); + Component left_menu_ = + Menu(&left_menu_entries, &left_menu_selected, &menu_option); + Component right_menu_ = + Menu(&right_menu_entries, &right_menu_selected, &menu_option); Component container = Container::Horizontal({ left_menu_, @@ -68,9 +75,6 @@ int main(int argc, const char* argv[]) { border; }); - auto screen = ScreenInteractive::TerminalOutput(); - MenuBase::From(left_menu_)->on_enter = screen.ExitLoopClosure(); - MenuBase::From(right_menu_)->on_enter = screen.ExitLoopClosure(); screen.Loop(renderer); } diff --git a/examples/component/menu_style.cpp b/examples/component/menu_style.cpp index 4be8f08..9b6a7fc 100644 --- a/examples/component/menu_style.cpp +++ b/examples/component/menu_style.cpp @@ -14,6 +14,8 @@ int main(int argc, const char* argv[]) { using namespace ftxui; + auto screen = ScreenInteractive::TerminalOutput(); + std::vector entries = { L"Monkey", L"Dog", L"Cat", L"Bird", L"Elephant", }; @@ -23,12 +25,51 @@ int main(int argc, const char* argv[]) { int menu_4_selected_ = 0; int menu_5_selected_ = 0; int menu_6_selected_ = 0; - auto menu_1_ = Menu(&entries, &menu_1_selected_); - auto menu_2_ = Menu(&entries, &menu_2_selected_); - auto menu_3_ = Menu(&entries, &menu_3_selected_); - auto menu_4_ = Menu(&entries, &menu_4_selected_); - auto menu_5_ = Menu(&entries, &menu_5_selected_); - auto menu_6_ = Menu(&entries, &menu_6_selected_); + + MenuOption option_1; + option_1.focused_style = bold | color(Color::Blue); + option_1.selected_style = color(Color::Blue); + option_1.selected_focused_style = bold | color(Color::Blue); + option_1.on_enter = screen.ExitLoopClosure(); + auto menu_1_ = Menu(&entries, &menu_1_selected_, &option_1); + + MenuOption option_2; + option_2.focused_style = bold | color(Color::Blue); + option_2.selected_style = color(Color::Blue); + option_2.selected_focused_style = bold | color(Color::Blue); + option_2.on_enter = screen.ExitLoopClosure(); + auto menu_2_ = Menu(&entries, &menu_2_selected_, &option_2); + + MenuOption option_3; + option_3.selected_style = color(Color::Blue); + option_3.focused_style = bgcolor(Color::Blue); + option_3.selected_focused_style = bgcolor(Color::Blue); + option_3.on_enter = screen.ExitLoopClosure(); + auto menu_3_ = Menu(&entries, &menu_3_selected_, &option_3); + + MenuOption option_4; + option_4.selected_style = bgcolor(Color::Blue); + option_4.focused_style = bgcolor(Color::BlueLight); + option_4.selected_focused_style = bgcolor(Color::BlueLight); + option_4.on_enter = screen.ExitLoopClosure(); + auto menu_4_ = Menu(&entries, &menu_4_selected_, &option_4); + + MenuOption option_5; + option_5.normal_style = bgcolor(Color::Blue); + option_5.selected_style = bgcolor(Color::Yellow); + option_5.focused_style = bgcolor(Color::Red); + option_5.selected_focused_style = bgcolor(Color::Red); + option_5.on_enter = screen.ExitLoopClosure(); + auto menu_5_ = Menu(&entries, &menu_5_selected_, &option_5); + + MenuOption option_6; + option_6.normal_style = dim | color(Color::Blue); + option_6.selected_style = color(Color::Blue); + option_6.focused_style = bold | color(Color::Blue); + option_6.selected_focused_style = bold | color(Color::Blue); + option_6.on_enter = screen.ExitLoopClosure(); + auto menu_6_ = Menu(&entries, &menu_6_selected_, &option_6); + auto container = Container::Horizontal({ menu_1_, menu_2_, @@ -51,31 +92,6 @@ int main(int argc, const char* argv[]) { }) | border; }); // clang-format on - auto screen = ScreenInteractive::TerminalOutput(); - for (Component menu : {menu_1_, menu_2_, menu_3_, menu_4_, menu_5_, menu_6_}) - MenuBase::From(menu)->on_enter = screen.ExitLoopClosure(); - - MenuBase::From(menu_2_)->focused_style = bold | color(Color::Blue); - MenuBase::From(menu_2_)->selected_style = color(Color::Blue); - MenuBase::From(menu_2_)->selected_focused_style = bold | color(Color::Blue); - - MenuBase::From(menu_3_)->selected_style = color(Color::Blue); - MenuBase::From(menu_3_)->focused_style = bgcolor(Color::Blue); - MenuBase::From(menu_3_)->selected_focused_style = bgcolor(Color::Blue); - - MenuBase::From(menu_4_)->selected_style = bgcolor(Color::Blue); - MenuBase::From(menu_4_)->focused_style = bgcolor(Color::BlueLight); - MenuBase::From(menu_4_)->selected_focused_style = bgcolor(Color::BlueLight); - - MenuBase::From(menu_5_)->normal_style = bgcolor(Color::Blue); - MenuBase::From(menu_5_)->selected_style = bgcolor(Color::Yellow); - MenuBase::From(menu_5_)->focused_style = bgcolor(Color::Red); - MenuBase::From(menu_5_)->selected_focused_style = bgcolor(Color::Red); - - MenuBase::From(menu_6_)->normal_style = dim | color(Color::Blue); - MenuBase::From(menu_6_)->selected_style = color(Color::Blue); - MenuBase::From(menu_6_)->focused_style = bold | color(Color::Blue); - MenuBase::From(menu_6_)->selected_focused_style = bold | color(Color::Blue); screen.Loop(renderer); } diff --git a/include/ftxui/component/component.hpp b/include/ftxui/component/component.hpp index c524fde..573b2ad 100644 --- a/include/ftxui/component/component.hpp +++ b/include/ftxui/component/component.hpp @@ -7,8 +7,9 @@ #include // for vector #include "ftxui/component/component_base.hpp" +#include "ftxui/component/component_options.hpp" #include "ftxui/dom/elements.hpp" // for Element -#include "ftxui/screen/string.hpp" // for ConstStringRef, StringRef +#include "ftxui/util/ref.hpp" // for ConstStringRef, StringRef namespace ftxui { @@ -28,7 +29,9 @@ Component Button(ConstStringRef label, bool border = true); Component Checkbox(ConstStringRef label, bool* checked); Component Input(StringRef content, ConstStringRef placeholder); -Component Menu(const std::vector* entries, int* selected_); +Component Menu(const std::vector* entries, + int* selected_, + ConstRef = {}); Component Radiobox(const std::vector* entries, int* selected_); Component Toggle(const std::vector* entries, int* selected); template // T = {int, float, long} diff --git a/include/ftxui/component/component_options.hpp b/include/ftxui/component/component_options.hpp new file mode 100644 index 0000000..9bdb329 --- /dev/null +++ b/include/ftxui/component/component_options.hpp @@ -0,0 +1,19 @@ +#ifndef FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP +#define FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP + +namespace ftxui { + +struct MenuOption { + Decorator normal_style = nothing; + Decorator focused_style = inverted; + Decorator selected_style = bold; + Decorator selected_focused_style = focused_style | selected_style; + + // State update callback. + std::function on_change = []() {}; + std::function on_enter = []() {}; +}; + +}; // namespace ftxui + +#endif /* end of include guard: FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP */ diff --git a/include/ftxui/component/menu.hpp b/include/ftxui/component/menu.hpp index 3f30839..cb16314 100644 --- a/include/ftxui/component/menu.hpp +++ b/include/ftxui/component/menu.hpp @@ -5,8 +5,9 @@ #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.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 @@ -21,21 +22,14 @@ class MenuBase : public ComponentBase { static MenuBase* From(Component component); // Constructor. - MenuBase(const std::vector* entries, int* selected_); + MenuBase(const std::vector* entries, + int* selected_, + ConstRef option = {}); ~MenuBase() override = default; // State. int focused = 0; - Decorator normal_style = nothing; - Decorator focused_style = inverted; - Decorator selected_style = bold; - Decorator selected_focused_style = focused_style | selected_style; - - // State update callback. - std::function on_change = []() {}; - std::function on_enter = []() {}; - // Component implementation. Element Render() override; bool OnEvent(Event) override; @@ -43,6 +37,7 @@ class MenuBase : public ComponentBase { protected: const std::vector* const entries_; int* selected_ = 0; + ConstRef option_; bool OnMouseEvent(Event); diff --git a/include/ftxui/screen/string.hpp b/include/ftxui/screen/string.hpp index ad312b5..8e1f0a4 100644 --- a/include/ftxui/screen/string.hpp +++ b/include/ftxui/screen/string.hpp @@ -19,40 +19,6 @@ int wchar_width_cjk(wchar_t); int wstring_width(const std::wstring&); int wstring_width_cjk(const std::wstring&); -/// @brief For convenience, this class convert multiple mutable string -/// references toward a shared representation. -class StringRef { - public: - StringRef(std::wstring* ref); - StringRef(std::wstring ref); - StringRef(const wchar_t* ref); - StringRef(const char* ref); - - std::wstring& operator*(); - std::wstring* operator->(); - - private: - std::wstring* const borrowed_ = nullptr; - std::wstring owned_; -}; - -/// @brief For convenience, this class convert multiple immutable string -/// references toward shared representation. -class ConstStringRef { - public: - ConstStringRef(const std::wstring* ref); - ConstStringRef(std::wstring ref); - ConstStringRef(const wchar_t* ref); - ConstStringRef(const char* ref); - - const std::wstring& operator*(); - const std::wstring* operator->(); - - private: - const std::wstring* const borrowed_ = nullptr; - const std::wstring owned_; -}; - } // namespace ftxui #endif /* end of include guard: FTXUI_SCREEN_STRING_HPP */ diff --git a/include/ftxui/util/ref.hpp b/include/ftxui/util/ref.hpp new file mode 100644 index 0000000..678c528 --- /dev/null +++ b/include/ftxui/util/ref.hpp @@ -0,0 +1,63 @@ +#ifndef FTXUI_UTIL_REF_HPP +#define FTXUI_UTIL_REF_HPP + +#include +#include + +namespace ftxui { + +// An adapter for a const object referenced or owned. +template +class ConstRef { + public: + ConstRef() {} + ConstRef(T t): owned_(t) {} + ConstRef(const T* t) : address_(t) {} + const T& operator*() { return address_ ? *address_ : owned_; } + const T* operator->() { return address_ ? address_ : &owned_; } + + private: + T owned_; + const T* address_ = nullptr; +}; + +/// @brief For convenience, this class convert multiple mutable string +/// references toward a shared representation. +class StringRef { + public: + StringRef(std::wstring* ref) : address_(ref) {} + StringRef(std::wstring ref) : owned_(std::move(ref)) {} + StringRef(const wchar_t* ref) : StringRef(std::wstring(ref)) {} + StringRef(const char* ref) : StringRef(to_wstring(std::string(ref))) {} + std::wstring& operator*() { return address_ ? *address_ : owned_; } + std::wstring* operator->() { return address_ ? address_ : &owned_; } + + private: + std::wstring owned_; + std::wstring* address_ = nullptr; +}; + +/// @brief For convenience, this class convert multiple immutable string +/// references toward shared representation. +class ConstStringRef { + public: + ConstStringRef(const std::wstring* ref) : address_(ref) {} + ConstStringRef(std::wstring ref) : owned_(std::move(ref)) {} + ConstStringRef(const wchar_t* ref) : ConstStringRef(std::wstring(ref)) {} + ConstStringRef(const char* ref) + : ConstStringRef(to_wstring(std::string(ref))) {} + const std::wstring& operator*() { return address_ ? *address_ : owned_; } + const std::wstring* operator->() { return address_ ? address_ : &owned_; } + + private: + const std::wstring owned_; + const std::wstring* address_ = nullptr; +}; + +} // namespace ftxui + +#endif /* end of include guard: FTXUI_UTIL_REF_HPP */ + +// 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 685c53a..dd4b81f 100644 --- a/src/ftxui/component/menu.cpp +++ b/src/ftxui/component/menu.cpp @@ -38,8 +38,10 @@ namespace ftxui { /// entry 2 /// entry 3 /// ``` -Component Menu(const std::vector* entries, int* selected) { - return Make(entries, selected); +Component Menu(const std::vector* entries, + int* selected, + ConstRef option) { + return Make(entries, selected, std::move(option)); } // static @@ -47,8 +49,10 @@ MenuBase* MenuBase::From(Component component) { return static_cast(component.get()); } -MenuBase::MenuBase(const std::vector* entries, int* selected) - : entries_(entries), selected_(selected) {} +MenuBase::MenuBase(const std::vector* entries, + int* selected, + ConstRef option) + : entries_(entries), selected_(selected), option_(option) {} Element MenuBase::Render() { Elements elements; @@ -58,9 +62,10 @@ Element MenuBase::Render() { bool is_focused = (focused == int(i)) && is_menu_focused; bool is_selected = (*selected_ == int(i)); - auto style = is_selected - ? (is_focused ? selected_focused_style : selected_style) - : (is_focused ? focused_style : normal_style); + auto style = is_selected ? (is_focused ? option_->selected_focused_style + : option_->selected_style) + : (is_focused ? option_->focused_style + : option_->normal_style); auto focus_management = !is_selected ? nothing : is_menu_focused ? focus : select; @@ -94,12 +99,12 @@ bool MenuBase::OnEvent(Event event) { if (*selected_ != old_selected) { focused = *selected_; - on_change(); + option_->on_change(); return true; } if (event == Event::Return) { - on_enter(); + option_->on_enter(); return true; } @@ -119,7 +124,7 @@ bool MenuBase::OnMouseEvent(Event event) { event.mouse().motion == Mouse::Released) { if (*selected_ != i) { *selected_ = i; - on_change(); + option_->on_change(); } return true; } diff --git a/src/ftxui/screen/string.cpp b/src/ftxui/screen/string.cpp index 1323265..1c54c4c 100644 --- a/src/ftxui/screen/string.cpp +++ b/src/ftxui/screen/string.cpp @@ -26,30 +26,6 @@ std::wstring to_wstring(const std::string& s) { #pragma warning(pop) #endif -StringRef::StringRef(std::wstring* ref) : borrowed_(ref) {} -StringRef::StringRef(std::wstring ref) : owned_(std::move(ref)) {} -StringRef::StringRef(const wchar_t* ref) : owned_(ref) {} -StringRef::StringRef(const char* ref) : owned_(to_wstring(std::string(ref))) {} -std::wstring& StringRef::operator*() { - return borrowed_ ? *borrowed_ : owned_; -} -std::wstring* StringRef::operator->() { - return borrowed_ ? borrowed_ : &owned_; -} - -ConstStringRef::ConstStringRef(const std::wstring* ref) : borrowed_(ref) {} -ConstStringRef::ConstStringRef(std::wstring ref) : owned_(std::move(ref)) {} -ConstStringRef::ConstStringRef(const wchar_t* ref) : owned_(ref) {} -ConstStringRef::ConstStringRef(const char* ref) - : owned_(to_wstring(std::string(ref))) {} - -const std::wstring& ConstStringRef::operator*() { - return borrowed_ ? *borrowed_ : owned_; -} -const std::wstring* ConstStringRef::operator->() { - return borrowed_ ? borrowed_ : &owned_; -} - } // namespace ftxui // Copyright 2020 Arthur Sonzogni. All rights reserved.