diff --git a/CMakeLists.txt b/CMakeLists.txt index 00d70fc..8feca7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,6 @@ add_library(component STATIC include/ftxui/component/mouse.hpp include/ftxui/component/receiver.hpp include/ftxui/component/screen_interactive.hpp - include/ftxui/component/toggle.hpp src/ftxui/component/button.cpp src/ftxui/component/catch_event.cpp src/ftxui/component/checkbox.cpp diff --git a/include/ftxui/component/component_options.hpp b/include/ftxui/component/component_options.hpp index c88fbbc..c71327b 100644 --- a/include/ftxui/component/component_options.hpp +++ b/include/ftxui/component/component_options.hpp @@ -74,6 +74,8 @@ struct ToggleOption { std::function on_change = [] {}; /// Called when the user presses enter. std::function on_enter = [] {}; + + Ref focused_entry = 0; }; }; // namespace ftxui diff --git a/include/ftxui/component/toggle.hpp b/include/ftxui/component/toggle.hpp deleted file mode 100644 index d5530da..0000000 --- a/include/ftxui/component/toggle.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef FTXUI_COMPONENT_TOGGLE_H_ -#define FTXUI_COMPONENT_TOGGLE_H_ - -#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/dom/elements.hpp" // for Element, Decorator, operator|, bold, dim, inverted -#include "ftxui/screen/box.hpp" // for Box - -namespace ftxui { -struct Event; - -/// @brief An horizontal list of elements. The user can navigate through them. -/// @ingroup component -class ToggleBase : public ComponentBase { - public: - // Access this interface from a Component - static ToggleBase* From(Component component); - - // Constructor. - ToggleBase(const std::vector* entries, - int* selected, - Ref option = {}); - ~ToggleBase() override = default; - - // State. - int focused = 0; - - // Component implementation. - Element Render() override; - bool OnEvent(Event) override; - - protected: - const std::vector* const entries_; - int* selected_ = 0; - - bool OnMouseEvent(Event event); - std::vector boxes_; - Ref option_; -}; - -} // namespace ftxui - -#endif /* end of include guard: FTXUI_COMPONENT_TOGGLE_H_ */ - -// 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/toggle.cpp b/src/ftxui/component/toggle.cpp index 4d55128..8a26ba4 100644 --- a/src/ftxui/component/toggle.cpp +++ b/src/ftxui/component/toggle.cpp @@ -4,105 +4,122 @@ #include // for move #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse +#include "ftxui/component/component.hpp" // for Mouse, Mouse::Left, Mouse::Pressed #include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed -#include "ftxui/component/toggle.hpp" namespace ftxui { +namespace { + +/// @brief An horizontal list of elements. The user can navigate through them. +/// @ingroup component +class ToggleBase : public ComponentBase { + public: + ToggleBase(const std::vector* entries, + int* selected, + Ref option) + : entries_(entries), selected_(selected), option_(std::move(option)) {} + + ~ToggleBase() override = default; + + private: + Element Render() override { + Elements children; + bool is_toggle_focused = Focused(); + boxes_.resize(entries_->size()); + for (size_t i = 0; i < entries_->size(); ++i) { + // Separator. + if (i != 0) + children.push_back(separator()); + + bool is_focused = (focused_entry() == int(i)) && is_toggle_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_toggle_focused ? focus + : select; + children.push_back(text(entries_->at(i)) | style | focus_management | + reflect(boxes_[i])); + } + return hbox(std::move(children)); + } + + bool OnEvent(Event event) override{ + if (event.is_mouse()) + return OnMouseEvent(event); + + int old_selected = *selected_; + if (event == Event::ArrowLeft || event == Event::Character('h')) + (*selected_)--; + if (event == Event::ArrowRight || event == Event::Character('l')) + (*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 (old_selected != *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::Pressed) { + TakeFocus(); + if (*selected_ != i) { + *selected_ = i; + option_->on_change(); + } + return true; + } + } + return false; + } + + int& focused_entry() { return option_->focused_entry(); } + + const std::vector* const entries_; + int* selected_ = 0; + + std::vector boxes_; + Ref option_; +}; + +} // namespace + +/// @brief An horizontal list of elements. The user can navigate through them. +/// @ingroup component Component Toggle(const std::vector* entries, int* selected, Ref option) { return Make(entries, selected, std::move(option)); } -// static -ToggleBase* ToggleBase::From(Component component) { - return static_cast(component.get()); -} - -ToggleBase::ToggleBase(const std::vector* entries, - int* selected, - Ref option) - : entries_(entries), selected_(selected), option_(std::move(option)) {} - -Element ToggleBase::Render() { - Elements children; - bool is_toggle_focused = Focused(); - boxes_.resize(entries_->size()); - for (size_t i = 0; i < entries_->size(); ++i) { - // Separator. - if (i != 0) - children.push_back(separator()); - - bool is_focused = (focused == int(i)) && is_toggle_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_toggle_focused ? focus - : select; - children.push_back(text(entries_->at(i)) | style | focus_management | - reflect(boxes_[i])); - } - return hbox(std::move(children)); -} - -bool ToggleBase::OnEvent(Event event) { - if (event.is_mouse()) - return OnMouseEvent(event); - - int old_selected = *selected_; - if (event == Event::ArrowLeft || event == Event::Character('h')) - (*selected_)--; - if (event == Event::ArrowRight || event == Event::Character('l')) - (*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 (old_selected != *selected_) { - focused = *selected_; - option_->on_change(); - return true; - } - - if (event == Event::Return) { - option_->on_enter(); - return true; - } - - return false; -} - -bool ToggleBase::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::Pressed) { - TakeFocus(); - if (*selected_ != i) { - *selected_ = i; - option_->on_change(); - } - return true; - } - } - return false; -} - } // namespace ftxui // Copyright 2020 Arthur Sonzogni. All rights reserved. diff --git a/src/ftxui/component/toggle_test.cpp b/src/ftxui/component/toggle_test.cpp index d4752f3..a88bd38 100644 --- a/src/ftxui/component/toggle_test.cpp +++ b/src/ftxui/component/toggle_test.cpp @@ -2,9 +2,9 @@ #include // for TestPartResult, SuiteApiResolver, TestFactoryImpl #include // for __shared_ptr_access, shared_ptr +#include "ftxui/component/component.hpp" #include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse #include "ftxui/component/mouse.hpp" // for ftxui -#include "ftxui/component/toggle.hpp" #include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, Test, EXPECT_TRUE, EXPECT_FALSE, TEST using namespace ftxui;