From b3ba747d82fcca046b2b11676ba3ac05c87e6f23 Mon Sep 17 00:00:00 2001 From: Arthur Sonzogni Date: Tue, 30 Aug 2022 18:52:33 +0200 Subject: [PATCH] Feature: Slider in any directions. (#468) Add the `SliderOption` option supporting: ```cpp { Ref value; ConstRef min = T(0); ConstRef max = T(100); ConstRef increment = (max() - min()) / 20; GaugeDirection direction = GaugeDirection::Right; Color color_active = Color::White; Color color_inactive = Color::GrayDark; }; ``` In particular, this supports multiple direction. This resolves: https://github.com/ArthurSonzogni/FTXUI/issues/467 This one do not support adding a label. The old constructors can still be used to have a label. --- CHANGELOG.md | 4 + CMakeLists.txt | 4 - cmake/ftxui_set_options.cmake | 6 +- cmake/ftxui_test.cmake | 1 + examples/CMakeLists.txt | 3 + examples/component/CMakeLists.txt | 1 + examples/component/slider_direction.cpp | 37 +++ include/ftxui/component/component.hpp | 5 +- include/ftxui/component/component_options.hpp | 23 +- include/ftxui/screen/screen.hpp | 2 +- src/ftxui/component/animation.cpp | 1 + src/ftxui/component/checkbox.cpp | 9 +- src/ftxui/component/input.cpp | 5 +- src/ftxui/component/menu.cpp | 2 +- src/ftxui/component/radiobox.cpp | 3 +- src/ftxui/component/screen_interactive.cpp | 21 +- src/ftxui/component/slider.cpp | 264 +++++++++++++++--- src/ftxui/component/slider_test.cpp | 136 +++++++++ src/ftxui/screen/screen.cpp | 2 +- 19 files changed, 452 insertions(+), 77 deletions(-) create mode 100644 examples/component/slider_direction.cpp create mode 100644 src/ftxui/component/slider_test.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 0464533..d766685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ current (development) ### Component: - Feature: Add the `Modal` component. - Feature: `Slider` supports taking references for all its arguments. +- Feature: `Slider` supports `SliderOption`. It supports: + - multiple directions. + - multiple colors. + - various values (value, min, max, increment). - Improvement: The `Menu` keeps the focus when an entry is selected with the mouse. diff --git a/CMakeLists.txt b/CMakeLists.txt index b48bdec..2a88435 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,10 +132,6 @@ target_link_libraries(component PUBLIC Threads::Threads ) -set_target_properties(screen PROPERTIES VERSION ${PROJECT_VERSION}) -set_target_properties(dom PROPERTIES VERSION ${PROJECT_VERSION}) -set_target_properties(component PROPERTIES VERSION ${PROJECT_VERSION}) - include(cmake/ftxui_set_options.cmake) ftxui_set_options(screen) ftxui_set_options(dom) diff --git a/cmake/ftxui_set_options.cmake b/cmake/ftxui_set_options.cmake index 1cefd5c..6fd7baf 100644 --- a/cmake/ftxui_set_options.cmake +++ b/cmake/ftxui_set_options.cmake @@ -7,7 +7,11 @@ endif() function(ftxui_set_options library) - set_target_properties(${library} PROPERTIES OUTPUT_NAME "ftxui-${library}") + set_target_properties(${library} PROPERTIES + VERSION ${PROJECT_VERSION} + CXX_STANDARD 20 + OUTPUT_NAME "ftxui-${library}" + ) if(CLANG_TIDY_EXE AND FTXUI_CLANG_TIDY) set_target_properties(${library} diff --git a/cmake/ftxui_test.cmake b/cmake/ftxui_test.cmake index 7f4e237..5aabb2c 100644 --- a/cmake/ftxui_test.cmake +++ b/cmake/ftxui_test.cmake @@ -34,6 +34,7 @@ add_executable(tests src/ftxui/component/modal_test.cpp src/ftxui/component/radiobox_test.cpp src/ftxui/component/receiver_test.cpp + src/ftxui/component/slider_test.cpp src/ftxui/component/resizable_split_test.cpp src/ftxui/component/screen_interactive_test.cpp src/ftxui/component/terminal_input_parser_test.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7b02d17..016aa73 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,6 +4,9 @@ function(example name) target_link_libraries(ftxui_example_${name} PUBLIC ${DIRECTORY_LIB}) file(RELATIVE_PATH dir ${EXAMPLES_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) set_property(GLOBAL APPEND PROPERTY FTXUI::EXAMPLES ${dir}/${name}) + set_target_properties(ftxui_example_${name} PROPERTIES + CXX_STANDARD 20 + ) endfunction(example) add_subdirectory(component) diff --git a/examples/component/CMakeLists.txt b/examples/component/CMakeLists.txt index 3a5ef70..e891110 100644 --- a/examples/component/CMakeLists.txt +++ b/examples/component/CMakeLists.txt @@ -33,6 +33,7 @@ example(radiobox_in_frame) example(renderer) example(resizable_split) example(slider) +example(slider_direction) example(slider_rgb) example(tab_horizontal) example(tab_vertical) diff --git a/examples/component/slider_direction.cpp b/examples/component/slider_direction.cpp new file mode 100644 index 0000000..1a0a390 --- /dev/null +++ b/examples/component/slider_direction.cpp @@ -0,0 +1,37 @@ +#include // for array +#include // for sin +#include // for ComponentBase +#include // for size, GaugeDirection, GaugeDirection::Up, GREATER_THAN, HEIGHT +#include // for shared_ptr, __shared_ptr_access + +#include "ftxui/component/captured_mouse.hpp" // for ftxui +#include "ftxui/component/component.hpp" // for Horizontal, Slider, operator|= +#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive + +using namespace ftxui; + +int main(int argc, const char* argv[]) { + auto screen = ScreenInteractive::TerminalOutput(); + std::array values; + for (int i = 0; i < values.size(); ++i) { + values[i] = 50 + 20 * std::sin(i * 0.3); + } + + auto layout_horizontal = Container::Horizontal({}); + for (int i = 0; i < values.size(); ++i) { + layout_horizontal->Add(Slider({ + .value = &values[i], + .max = 100, + .increment = 5, + .direction = GaugeDirection::Up, + })); + } + + layout_horizontal |= size(HEIGHT, GREATER_THAN, 20); + + screen.Loop(layout_horizontal); +} + +// 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/include/ftxui/component/component.hpp b/include/ftxui/component/component.hpp index fb8729d..80f48ed 100644 --- a/include/ftxui/component/component.hpp +++ b/include/ftxui/component/component.hpp @@ -10,7 +10,7 @@ #include "ftxui/component/component_base.hpp" // for Component, Components #include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, MenuOption #include "ftxui/dom/elements.hpp" // for Element -#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef, ConstStringListRef, StringRef +#include "ftxui/util/ref.hpp" // for ConstRef, Ref, ConstStringRef, ConstStringListRef, StringRef namespace ftxui { struct ButtonOption; @@ -82,6 +82,9 @@ Component Slider(ConstStringRef label, ConstRef min = 0l, ConstRef max = 100l, ConstRef increment = 5l); +// General slider type without support for a `label`. +template // T = {int, float, long} +Component Slider(SliderOption options = {}); Component ResizableSplitLeft(Component main, Component back, int* main_size); Component ResizableSplitRight(Component main, Component back, int* main_size); diff --git a/include/ftxui/component/component_options.hpp b/include/ftxui/component/component_options.hpp index 776d351..8863ff6 100644 --- a/include/ftxui/component/component_options.hpp +++ b/include/ftxui/component/component_options.hpp @@ -3,11 +3,11 @@ #include // for milliseconds #include // for Duration, QuadraticInOut, Function -#include // for Element -#include // for Ref -#include // for function -#include // for optional -#include // for string +#include // for Element, GaugeDirection, GaugeDirection::Right +#include // for Ref, ConstRef +#include // for function +#include // for optional +#include // for string #include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White @@ -164,6 +164,19 @@ struct RadioboxOption { Ref focused_entry = 0; }; +// @brief Option for the `Slider` component. +// @ingroup component +template +struct SliderOption { + Ref value; + ConstRef min = T(0); + ConstRef max = T(100); + ConstRef increment = (max() - min()) / 20; + GaugeDirection direction = GaugeDirection::Right; + Color color_active = Color::White; + Color color_inactive = Color::GrayDark; +}; + } // namespace ftxui #endif /* end of include guard: FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP */ diff --git a/include/ftxui/screen/screen.hpp b/include/ftxui/screen/screen.hpp index 21eb377..6ca7e5a 100644 --- a/include/ftxui/screen/screen.hpp +++ b/include/ftxui/screen/screen.hpp @@ -2,7 +2,7 @@ #define FTXUI_SCREEN_SCREEN_HPP #include -#include // for string, allocator, basic_string +#include // for allocator, string, basic_string #include // for vector #include "ftxui/screen/box.hpp" // for Box diff --git a/src/ftxui/component/animation.cpp b/src/ftxui/component/animation.cpp index bba5783..db904ba 100644 --- a/src/ftxui/component/animation.cpp +++ b/src/ftxui/component/animation.cpp @@ -1,4 +1,5 @@ #include // IWYU pragma: keep +#include // for operator<=, operator>=, partial_ordering #include // for ratio #include // for move diff --git a/src/ftxui/component/checkbox.cpp b/src/ftxui/component/checkbox.cpp index 9e7c354..39a2366 100644 --- a/src/ftxui/component/checkbox.cpp +++ b/src/ftxui/component/checkbox.cpp @@ -1,14 +1,13 @@ #include // for function -#include // for shared_ptr #include // for move #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse -#include "ftxui/component/component.hpp" // for Make, Component, Checkbox -#include "ftxui/component/component_base.hpp" // for ComponentBase -#include "ftxui/component/component_options.hpp" // for CheckboxOption +#include "ftxui/component/component.hpp" // for Make, Checkbox +#include "ftxui/component/component_base.hpp" // for Component, ComponentBase +#include "ftxui/component/component_options.hpp" // for CheckboxOption, EntryState #include "ftxui/component/event.hpp" // for Event, Event::Return #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed -#include "ftxui/dom/elements.hpp" // for operator|, text, Element, hbox, reflect, focus, nothing, select +#include "ftxui/dom/elements.hpp" // for operator|, Element, reflect, focus, nothing, select #include "ftxui/screen/box.hpp" // for Box #include "ftxui/util/ref.hpp" // for Ref, ConstStringRef diff --git a/src/ftxui/component/input.cpp b/src/ftxui/component/input.cpp index 6883dc1..307cb6c 100644 --- a/src/ftxui/component/input.cpp +++ b/src/ftxui/component/input.cpp @@ -1,8 +1,7 @@ #include // for max, min #include // for size_t #include // for function -#include // for shared_ptr, allocator -#include // for string, wstring +#include // for string, allocator #include // for move #include // for vector @@ -15,7 +14,7 @@ #include "ftxui/component/screen_interactive.hpp" // for Component #include "ftxui/dom/elements.hpp" // for operator|, text, Element, reflect, inverted, Decorator, flex, focus, hbox, size, bold, dim, frame, select, EQUAL, HEIGHT #include "ftxui/screen/box.hpp" // for Box -#include "ftxui/screen/string.hpp" // for GlyphPosition, GlyphCount, to_string, CellToGlyphIndex, to_wstring +#include "ftxui/screen/string.hpp" // for GlyphPosition, GlyphCount, CellToGlyphIndex #include "ftxui/screen/util.hpp" // for clamp #include "ftxui/util/ref.hpp" // for StringRef, Ref, ConstStringRef diff --git a/src/ftxui/component/menu.cpp b/src/ftxui/component/menu.cpp index df9d211..62cea4f 100644 --- a/src/ftxui/component/menu.cpp +++ b/src/ftxui/component/menu.cpp @@ -464,7 +464,7 @@ class MenuBase : public ComponentBase { ConstStringListRef entries_; int* selected_; int selected_previous_ = *selected_; - int selected_focus_= *selected_; + int selected_focus_ = *selected_; Ref option_; std::vector boxes_; diff --git a/src/ftxui/component/radiobox.cpp b/src/ftxui/component/radiobox.cpp index 41da88b..b7b0ff7 100644 --- a/src/ftxui/component/radiobox.cpp +++ b/src/ftxui/component/radiobox.cpp @@ -1,6 +1,5 @@ -#include // for max #include // for function -#include // for shared_ptr, allocator_traits<>::value_type +#include // for allocator_traits<>::value_type #include // for move #include // for vector diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index d836ac2..e506f13 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -1,6 +1,7 @@ -#include // for copy, max, min +#include // for min #include // for array -#include // for operator-, milliseconds, duration, operator>=, time_point, common_type<>::type +#include // for operator-, milliseconds, duration, operator<=>, time_point, common_type<>::type +#include // for operator>=, strong_ordering #include // for signal, raise, SIGTSTP, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGWINCH #include // for fileno, size_t, stdin #include // for Task, Closure, AnimationTask @@ -136,14 +137,14 @@ void EventListener(std::atomic* quit, Sender out) { } extern "C" { - EMSCRIPTEN_KEEPALIVE - void ftxui_on_resize(int columns, int rows) { - Terminal::SetFallbackSize({ - columns, - rows, - }); - std::raise(SIGWINCH); - } +EMSCRIPTEN_KEEPALIVE +void ftxui_on_resize(int columns, int rows) { + Terminal::SetFallbackSize({ + columns, + rows, + }); + std::raise(SIGWINCH); +} } #else diff --git a/src/ftxui/component/slider.cpp b/src/ftxui/component/slider.cpp index de07e2c..5312751 100644 --- a/src/ftxui/component/slider.cpp +++ b/src/ftxui/component/slider.cpp @@ -1,46 +1,108 @@ -#include // for allocator -#include // for move +#include // for max, min +#include // for SliderOption +#include // for allocator #include "ftxui/component/captured_mouse.hpp" // for CapturedMouse #include "ftxui/component/component.hpp" // for Make, Slider #include "ftxui/component/component_base.hpp" // for ComponentBase -#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight +#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp #include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released #include "ftxui/component/screen_interactive.hpp" // for Component -#include "ftxui/dom/elements.hpp" // for operator|, text, Element, reflect, xflex, gauge, hbox, underlined, color, dim, vcenter +#include "ftxui/dom/elements.hpp" // for operator|, text, GaugeDirection, Element, xflex, hbox, color, underlined, GaugeDirection::Down, GaugeDirection::Left, GaugeDirection::Right, GaugeDirection::Up, reflect, Decorator, dim, vcenter, yflex, gaugeDirection #include "ftxui/screen/box.hpp" // for Box -#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::GrayLight -#include "ftxui/util/ref.hpp" // for StringRef +#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White +#include "ftxui/screen/util.hpp" // for clamp +#include "ftxui/util/ref.hpp" // for ConstRef, ConstStringRef, Ref namespace ftxui { +namespace { +Decorator flexDirection(GaugeDirection direction) { + switch (direction) { + case GaugeDirection::Up: + case GaugeDirection::Down: + return yflex; + case GaugeDirection::Left: + case GaugeDirection::Right: + return xflex; + } + return xflex; // NOT_REACHED() +} +} // namespace + template class SliderBase : public ComponentBase { public: - SliderBase(ConstStringRef label, - Ref value, - ConstRef min, - ConstRef max, - ConstRef increment) - : label_(std::move(label)), - value_(value), - min_(min), - max_(max), - increment_(increment) {} + SliderBase(Ref> options) + : value_(options->value), + min_(options->min), + max_(options->max), + increment_(options->increment), + options_(options) {} Element Render() override { - auto gauge_color = - Focused() ? color(Color::GrayLight) : color(Color::GrayDark); + auto gauge_color = Focused() ? color(options_->color_active) + : color(options_->color_inactive); float percent = float(value_() - min_()) / float(max_() - min_()); - return hbox({ - text(label_()) | dim | vcenter, - hbox({ - text("["), - gauge(percent) | underlined | xflex | reflect(gauge_box_), - text("]"), - }) | xflex, - }) | - gauge_color | xflex | reflect(box_); + return gaugeDirection(percent, options_->direction) | + flexDirection(options_->direction) | reflect(gauge_box_) | + gauge_color; + } + + void OnLeft() { + switch (options_->direction) { + case GaugeDirection::Right: + value_() -= increment_(); + break; + case GaugeDirection::Left: + value_() += increment_(); + break; + case GaugeDirection::Up: + case GaugeDirection::Down: + break; + } + } + + void OnRight() { + switch (options_->direction) { + case GaugeDirection::Right: + value_() += increment_(); + break; + case GaugeDirection::Left: + value_() -= increment_(); + break; + case GaugeDirection::Up: + case GaugeDirection::Down: + break; + } + } + + void OnUp() { + switch (options_->direction) { + case GaugeDirection::Up: + value_() -= increment_(); + break; + case GaugeDirection::Down: + value_() += increment_(); + break; + case GaugeDirection::Left: + case GaugeDirection::Right: + break; + } + } + + void OnDown() { + switch (options_->direction) { + case GaugeDirection::Down: + value_() -= increment_(); + break; + case GaugeDirection::Up: + value_() += increment_(); + break; + case GaugeDirection::Left: + case GaugeDirection::Right: + break; + } } bool OnEvent(Event event) final { @@ -48,17 +110,23 @@ class SliderBase : public ComponentBase { return OnMouseEvent(event); } + T old_value = value_(); if (event == Event::ArrowLeft || event == Event::Character('h')) { - value_() -= increment_(); - value_() = std::max(value_(), min_()); - return true; + OnLeft(); + } + if (event == Event::ArrowRight || event == Event::Character('l')) { + OnRight(); + } + if (event == Event::ArrowUp || event == Event::Character('k')) { + OnDown(); + } + if (event == Event::ArrowDown || event == Event::Character('j')) { + OnUp(); } - if (event == Event::ArrowRight || event == Event::Character('l')) { - value_() += increment_(); - value_() = std::min(*value_, max_()); + value_() = util::clamp(value_(), min_(), max_()); + if (old_value != value_()) return true; - } return ComponentBase::OnEvent(event); } @@ -69,7 +137,8 @@ class SliderBase : public ComponentBase { return true; } - if (box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event)) { + if (gauge_box_.Contain(event.mouse().x, event.mouse().y) && + CaptureMouse(event)) { TakeFocus(); } @@ -81,9 +150,32 @@ class SliderBase : public ComponentBase { } if (captured_mouse_) { - value_() = min_() + (event.mouse().x - gauge_box_.x_min) * - (max_() - min_()) / - (gauge_box_.x_max - gauge_box_.x_min); + switch (options_->direction) { + case GaugeDirection::Right: { + value_() = min_() + (event.mouse().x - gauge_box_.x_min) * + (max_() - min_()) / + (gauge_box_.x_max - gauge_box_.x_min); + break; + } + case GaugeDirection::Left: { + value_() = max_() - (event.mouse().x - gauge_box_.x_min) * + (max_() - min_()) / + (gauge_box_.x_max - gauge_box_.x_min); + break; + } + case GaugeDirection::Down: { + value_() = min_() + (event.mouse().y - gauge_box_.y_min) * + (max_() - min_()) / + (gauge_box_.y_max - gauge_box_.y_min); + break; + } + case GaugeDirection::Up: { + value_() = max_() - (event.mouse().y - gauge_box_.y_min) * + (max_() - min_()) / + (gauge_box_.y_max - gauge_box_.y_min); + break; + } + } value_() = std::max(min_(), std::min(max_(), value_())); return true; } @@ -93,16 +185,60 @@ class SliderBase : public ComponentBase { bool Focusable() const final { return true; } private: - ConstStringRef label_; Ref value_; ConstRef min_; ConstRef max_; ConstRef increment_; - Box box_; + Ref> options_; Box gauge_box_; CapturedMouse captured_mouse_; }; +class SliderWithLabel : public ComponentBase { + public: + SliderWithLabel(ConstStringRef label, Component inner) : label_(label) { + Add(inner); + SetActiveChild(inner); + } + + private: + bool OnEvent(Event event) final { + if (ComponentBase::OnEvent(event)) + return true; + + if (!event.is_mouse()) { + return false; + } + + if (!box_.Contain(event.mouse().x, event.mouse().y)) { + return false; + } + + if (!CaptureMouse(event)) { + return false; + } + + TakeFocus(); + return true; + } + + Element Render() override { + auto gauge_color = Focused() ? color(Color::White) : color(Color::GrayDark); + return hbox({ + text(label_()) | dim | vcenter, + hbox({ + text("["), + ComponentBase::Render() | underlined, + text("]"), + }) | xflex, + }) | + gauge_color | xflex | reflect(box_); + } + + ConstStringRef label_; + Box box_; +}; + /// @brief An horizontal slider. /// @param label The name of the slider. /// @param value The current value of the slider. @@ -130,23 +266,65 @@ Component Slider(ConstStringRef label, ConstRef min, ConstRef max, ConstRef increment) { - return Make>(std::move(label), value, min, max, increment); + auto slider = Make>(SliderOption({ + .value = value, + .min = min, + .max = max, + .increment = increment, + })); + return Make(label, slider); } + Component Slider(ConstStringRef label, Ref value, ConstRef min, ConstRef max, ConstRef increment) { - return Make>(std::move(label), value, min, max, increment); + auto slider = Make>(SliderOption({ + .value = value, + .min = min, + .max = max, + .increment = increment, + })); + return Make(label, slider); } Component Slider(ConstStringRef label, Ref value, ConstRef min, ConstRef max, ConstRef increment) { - return Make>(std::move(label), value, min, max, increment); + auto slider = Make>(SliderOption({ + .value = value, + .min = min, + .max = max, + .increment = increment, + })); + return Make(label, slider); } +/// @brief A slider in any direction. +/// @param option The options +/// ### Example +/// +/// ```cpp +/// auto screen = ScreenInteractive::TerminalOutput(); +/// int value = 50; +/// auto slider = Slider({ +/// .value = &value, +/// .min = 0, +/// .max = 100, +/// .increment= 20, +/// }); +/// screen.Loop(slider); +/// ``` +template +Component Slider(SliderOption options) { + return Make>(options); +} +template Component Slider(SliderOption options); +template Component Slider(SliderOption options); +template Component Slider(SliderOption options); + } // namespace ftxui // Copyright 2020 Arthur Sonzogni. All rights reserved. diff --git a/src/ftxui/component/slider_test.cpp b/src/ftxui/component/slider_test.cpp new file mode 100644 index 0000000..ecaf3ed --- /dev/null +++ b/src/ftxui/component/slider_test.cpp @@ -0,0 +1,136 @@ +#include // for AssertionResult, Message, TestPartResult, Test, EXPECT_TRUE, EXPECT_EQ, SuiteApiResolver, TestInfo (ptr only), EXPECT_FALSE, TEST, TestFactoryImpl +#include // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released +#include // for GaugeDirection, GaugeDirection::Down, GaugeDirection::Left, GaugeDirection::Right, GaugeDirection::Up +#include // for __shared_ptr_access, shared_ptr, allocator + +#include "ftxui/component/component.hpp" // for Slider +#include "ftxui/component/component_base.hpp" // for ComponentBase +#include "ftxui/component/event.hpp" // for Event +#include "ftxui/dom/node.hpp" // for Render +#include "ftxui/screen/screen.hpp" // for Screen + +namespace ftxui { + +namespace { +Event MousePressed(int x, int y) { + Mouse mouse; + mouse.button = Mouse::Left; + mouse.motion = Mouse::Pressed; + mouse.shift = false; + mouse.meta = false; + mouse.control = false; + mouse.x = x; + mouse.y = y; + return Event::Mouse("jjj", mouse); +} + +Event MouseReleased(int x, int y) { + Mouse mouse; + mouse.button = Mouse::Left; + mouse.motion = Mouse::Released; + mouse.shift = false; + mouse.meta = false; + mouse.control = false; + mouse.x = x; + mouse.y = y; + return Event::Mouse("jjj", mouse); +} +} // namespace + +TEST(SliderTest, Right) { + int value = 50; + auto slider = Slider({ + .value = &value, + .min = 0, + .max = 100, + .increment = 10, + .direction = GaugeDirection::Right, + }); + Screen screen(11, 1); + Render(screen, slider->Render()); + EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0))); + EXPECT_EQ(value, 30); + EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0))); + EXPECT_EQ(value, 90); + EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2))); + EXPECT_EQ(value, 90); + EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2))); + EXPECT_EQ(value, 50); + EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2))); + EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2))); +} + +TEST(SliderTest, Left) { + int value = 50; + auto slider = Slider({ + .value = &value, + .min = 0, + .max = 100, + .increment = 10, + .direction = GaugeDirection::Left, + }); + Screen screen(11, 1); + Render(screen, slider->Render()); + EXPECT_TRUE(slider->OnEvent(MousePressed(3, 0))); + EXPECT_EQ(value, 70); + EXPECT_TRUE(slider->OnEvent(MousePressed(9, 0))); + EXPECT_EQ(value, 10); + EXPECT_TRUE(slider->OnEvent(MousePressed(9, 2))); + EXPECT_EQ(value, 10); + EXPECT_TRUE(slider->OnEvent(MousePressed(5, 2))); + EXPECT_EQ(value, 50); + EXPECT_TRUE(slider->OnEvent(MouseReleased(5, 2))); + EXPECT_FALSE(slider->OnEvent(MousePressed(5, 2))); +} + +TEST(SliderTest, Down) { + int value = 50; + auto slider = Slider({ + .value = &value, + .min = 0, + .max = 100, + .increment = 10, + .direction = GaugeDirection::Down, + }); + Screen screen(1, 11); + Render(screen, slider->Render()); + EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3))); + EXPECT_EQ(value, 30); + EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9))); + EXPECT_EQ(value, 90); + EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9))); + EXPECT_EQ(value, 90); + EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5))); + EXPECT_EQ(value, 50); + EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5))); + EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5))); +} + +TEST(SliderTest, Up) { + int value = 50; + auto slider = Slider({ + .value = &value, + .min = 0, + .max = 100, + .increment = 10, + .direction = GaugeDirection::Up, + }); + Screen screen(1, 11); + Render(screen, slider->Render()); + EXPECT_TRUE(slider->OnEvent(MousePressed(0, 3))); + EXPECT_EQ(value, 70); + EXPECT_TRUE(slider->OnEvent(MousePressed(0, 9))); + EXPECT_EQ(value, 10); + EXPECT_TRUE(slider->OnEvent(MousePressed(2, 9))); + EXPECT_EQ(value, 10); + EXPECT_TRUE(slider->OnEvent(MousePressed(2, 5))); + EXPECT_EQ(value, 50); + EXPECT_TRUE(slider->OnEvent(MouseReleased(2, 5))); + EXPECT_FALSE(slider->OnEvent(MousePressed(2, 5))); +} + +} // namespace ftxui + +// Copyright 2022 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/screen/screen.cpp b/src/ftxui/screen/screen.cpp index dc30d51..ed73831 100644 --- a/src/ftxui/screen/screen.cpp +++ b/src/ftxui/screen/screen.cpp @@ -55,7 +55,7 @@ void UpdatePixelStyle(std::stringstream& ss, if (next == previous) { return; } - + if ((!next.bold && previous.bold) || // (!next.dim && previous.dim)) { ss << "\x1B[22m"; // BOLD_RESET and DIM_RESET