mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-11-04 13:38:14 +08:00 
			
		
		
		
	Feature: Slider in any directions. (#468)
Add the `SliderOption` option supporting:
```cpp
{
  Ref<T> value;
  ConstRef<T> min = T(0);
  ConstRef<T> max = T(100);
  ConstRef<T> 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.
			
			
This commit is contained in:
		@@ -1,4 +1,5 @@
 | 
			
		||||
#include <cmath>    // IWYU pragma: keep
 | 
			
		||||
#include <compare>  // for operator<=, operator>=, partial_ordering
 | 
			
		||||
#include <ratio>    // for ratio
 | 
			
		||||
#include <utility>  // for move
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,13 @@
 | 
			
		||||
#include <functional>  // for function
 | 
			
		||||
#include <memory>      // for shared_ptr
 | 
			
		||||
#include <utility>     // 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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
#include <algorithm>   // for max, min
 | 
			
		||||
#include <cstddef>     // for size_t
 | 
			
		||||
#include <functional>  // for function
 | 
			
		||||
#include <memory>      // for shared_ptr, allocator
 | 
			
		||||
#include <string>      // for string, wstring
 | 
			
		||||
#include <string>      // for string, allocator
 | 
			
		||||
#include <utility>     // for move
 | 
			
		||||
#include <vector>      // 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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<MenuOption> option_;
 | 
			
		||||
 | 
			
		||||
  std::vector<Box> boxes_;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
#include <algorithm>   // for max
 | 
			
		||||
#include <functional>  // for function
 | 
			
		||||
#include <memory>      // for shared_ptr, allocator_traits<>::value_type
 | 
			
		||||
#include <memory>      // for allocator_traits<>::value_type
 | 
			
		||||
#include <utility>     // for move
 | 
			
		||||
#include <vector>      // for vector
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#include <algorithm>  // for copy, max, min
 | 
			
		||||
#include <algorithm>  // for min
 | 
			
		||||
#include <array>      // for array
 | 
			
		||||
#include <chrono>  // for operator-, milliseconds, duration, operator>=, time_point, common_type<>::type
 | 
			
		||||
#include <chrono>  // for operator-, milliseconds, duration, operator<=>, time_point, common_type<>::type
 | 
			
		||||
#include <compare>  // for operator>=, strong_ordering
 | 
			
		||||
#include <csignal>  // for signal, raise, SIGTSTP, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGWINCH
 | 
			
		||||
#include <cstdio>   // for fileno, size_t, stdin
 | 
			
		||||
#include <ftxui/component/task.hpp>  // for Task, Closure, AnimationTask
 | 
			
		||||
@@ -136,14 +137,14 @@ void EventListener(std::atomic<bool>* quit, Sender<Task> 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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,46 +1,108 @@
 | 
			
		||||
#include <string>   // for allocator
 | 
			
		||||
#include <utility>  // for move
 | 
			
		||||
#include <algorithm>                              // for max, min
 | 
			
		||||
#include <ftxui/component/component_options.hpp>  // for SliderOption
 | 
			
		||||
#include <string>                                 // 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 T>
 | 
			
		||||
class SliderBase : public ComponentBase {
 | 
			
		||||
 public:
 | 
			
		||||
  SliderBase(ConstStringRef label,
 | 
			
		||||
             Ref<T> value,
 | 
			
		||||
             ConstRef<T> min,
 | 
			
		||||
             ConstRef<T> max,
 | 
			
		||||
             ConstRef<T> increment)
 | 
			
		||||
      : label_(std::move(label)),
 | 
			
		||||
        value_(value),
 | 
			
		||||
        min_(min),
 | 
			
		||||
        max_(max),
 | 
			
		||||
        increment_(increment) {}
 | 
			
		||||
  SliderBase(Ref<SliderOption<T>> 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<T> value_;
 | 
			
		||||
  ConstRef<T> min_;
 | 
			
		||||
  ConstRef<T> max_;
 | 
			
		||||
  ConstRef<T> increment_;
 | 
			
		||||
  Box box_;
 | 
			
		||||
  Ref<SliderOption<T>> 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<int> min,
 | 
			
		||||
                 ConstRef<int> max,
 | 
			
		||||
                 ConstRef<int> increment) {
 | 
			
		||||
  return Make<SliderBase<int>>(std::move(label), value, min, max, increment);
 | 
			
		||||
  auto slider = Make<SliderBase<int>>(SliderOption<int>({
 | 
			
		||||
      .value = value,
 | 
			
		||||
      .min = min,
 | 
			
		||||
      .max = max,
 | 
			
		||||
      .increment = increment,
 | 
			
		||||
  }));
 | 
			
		||||
  return Make<SliderWithLabel>(label, slider);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component Slider(ConstStringRef label,
 | 
			
		||||
                 Ref<float> value,
 | 
			
		||||
                 ConstRef<float> min,
 | 
			
		||||
                 ConstRef<float> max,
 | 
			
		||||
                 ConstRef<float> increment) {
 | 
			
		||||
  return Make<SliderBase<float>>(std::move(label), value, min, max, increment);
 | 
			
		||||
  auto slider = Make<SliderBase<float>>(SliderOption<float>({
 | 
			
		||||
      .value = value,
 | 
			
		||||
      .min = min,
 | 
			
		||||
      .max = max,
 | 
			
		||||
      .increment = increment,
 | 
			
		||||
  }));
 | 
			
		||||
  return Make<SliderWithLabel>(label, slider);
 | 
			
		||||
}
 | 
			
		||||
Component Slider(ConstStringRef label,
 | 
			
		||||
                 Ref<long> value,
 | 
			
		||||
                 ConstRef<long> min,
 | 
			
		||||
                 ConstRef<long> max,
 | 
			
		||||
                 ConstRef<long> increment) {
 | 
			
		||||
  return Make<SliderBase<long>>(std::move(label), value, min, max, increment);
 | 
			
		||||
  auto slider = Make<SliderBase<long>>(SliderOption<long>({
 | 
			
		||||
      .value = value,
 | 
			
		||||
      .min = min,
 | 
			
		||||
      .max = max,
 | 
			
		||||
      .increment = increment,
 | 
			
		||||
  }));
 | 
			
		||||
  return Make<SliderWithLabel>(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 <typename T>
 | 
			
		||||
Component Slider(SliderOption<T> options) {
 | 
			
		||||
  return Make<SliderBase<T>>(options);
 | 
			
		||||
}
 | 
			
		||||
template Component Slider(SliderOption<int> options);
 | 
			
		||||
template Component Slider(SliderOption<float> options);
 | 
			
		||||
template Component Slider(SliderOption<long> options);
 | 
			
		||||
 | 
			
		||||
}  // namespace ftxui
 | 
			
		||||
 | 
			
		||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										136
									
								
								src/ftxui/component/slider_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/ftxui/component/slider_test.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
#include <gtest/gtest.h>  // for AssertionResult, Message, TestPartResult, Test, EXPECT_TRUE, EXPECT_EQ, SuiteApiResolver, TestInfo (ptr only), EXPECT_FALSE, TEST, TestFactoryImpl
 | 
			
		||||
#include <ftxui/component/mouse.hpp>  // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
 | 
			
		||||
#include <ftxui/dom/elements.hpp>  // for GaugeDirection, GaugeDirection::Down, GaugeDirection::Left, GaugeDirection::Right, GaugeDirection::Up
 | 
			
		||||
#include <memory>  // 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<int>({
 | 
			
		||||
      .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<int>({
 | 
			
		||||
      .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<int>({
 | 
			
		||||
      .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<int>({
 | 
			
		||||
      .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.
 | 
			
		||||
		Reference in New Issue
	
	Block a user