Gorgon Game Engine
Slider.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "Common.h"
4 #include "../UI/ComponentStackWidget.h"
5 #include "../UI/Helpers.h"
6 #include "../Property.h"
7 #include "Registry.h"
8 
9 #include <limits>
10 
11 namespace Gorgon { namespace Widgets {
12 
14  namespace internal {
15  enum class SliderInteractivity {
16  None,
17  HandleOnly = 1,
18  DualHandle = 2,
19  Jump = 4,
20  Page = 8,
21  HandleAndJump = HandleOnly | Jump,
22  HandleAndPage = HandleOnly | Page,
23  DualHandleAndJump = DualHandle | Jump,
24  DualHandleAndPage = DualHandle | Page,
25  };
26 
27  enum class SliderValueMapping {
28  OneValue = 1,
29  TwoValues = 2,
30  ValueAndRange = 3
31  };
32 
33  inline bool operator &(SliderInteractivity l, SliderInteractivity r) { return (int(l)&int(r)) != 0; }
34  }
36 
42  template<
43  class T_ = int,
44  float(*DIV_)(T_, T_, T_) = FloatDivider<T_>,
45  T_(*VAL_)(float, T_, T_) = FloatToValue<T_>,
46  template<class C_, class PT_, PT_(C_::*Getter_)() const, void(C_::*Setter_)(const PT_ &)> class P_ = Gorgon::NumericProperty,
47  internal::SliderInteractivity interactive = internal::SliderInteractivity::None,
48  internal::SliderValueMapping valuemapping = internal::SliderValueMapping::OneValue
49 
50  >
51  class SliderBase :
53  public P_<
55  T_,
58  >
59  {
60  public:
61  using Type = T_;
62  using PropType = P_<
64  T_,
67  >;
68 
69  friend class P_<
70  UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive, valuemapping>, T_>,
71  T_,
72  &UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive, valuemapping>, T_>::get_,
73  &UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive, valuemapping>, T_>::set_
74  >;
75 
76  template<
77  class T1_,
78  float(*DIV1_)(T1_, T1_, T1_),
79  T1_(*VAL1_)(float, T1_, T1_),
80  template<
81  class C_,
82  class PT_,
83  PT_(C_::*Getter_)() const,
84  void(C_::*Setter_)(const PT_&)
85  > class P1_,
86  internal::SliderInteractivity I2_,
87  internal::SliderValueMapping V2_
88  >
89  friend class SliderBase;
90 
91  friend struct UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive, valuemapping>, T_>;
92 
93  SliderBase(const SliderBase &) = delete;
94 
95  explicit SliderBase(T_ cur, T_ max, Registry::TemplateType type = Registry::Progress_Regular) :
96  SliderBase(Registry::Active()[type], cur, max)
97  {
98  }
99 
101  SliderBase(Registry::Active()[type], cur)
102  {
103  }
104 
105  explicit SliderBase(const UI::Template &temp, T_ cur = T_{}) : SliderBase(temp, cur, T_{100}) { }
106 
107  SliderBase(const UI::Template &temp, T_ cur, T_ max) :
108  ComponentStackWidget(temp),
109  PropType(&helper),
110  Maximum(this),
111  Minimum(this),
112  Range(this),
113  SmallChange(this),
114  LargeChange(this),
115  value(cur), max(max)
116  {
117  refreshvalue();
118  stack.SetValueTransitionSpeed({changespeed, 0, 0, 0});
119 
120  setupinteractivity();
121  }
122 
123  protected: //these methods are here to be elevated to public if necessary.
124 
127  void SetValue(const T_ &value, bool instant = false) {
128  setval(value, instant);
129  }
130 
132  T_ GetValue() const {
133  return this->operator T_();
134  }
135 
138  void SetMaximum(const T_ &value) {
139  max = value;
140 
141  if(valuemapping == internal::SliderValueMapping::ValueAndRange) {
142  SetSmoothChangeSpeedRatio(changespeed);
143  }
144 
145  if(!setval(this->value)) //if returns true, refresh is already called
146  refreshvalue();
147  }
148 
150  T_ GetMaximum() const {
151  return max;
152  }
153 
156  void SetMinimum(const T_ &value) {
157  min = value;
158 
159  if(valuemapping == internal::SliderValueMapping::ValueAndRange) {
160  SetSmoothChangeSpeedRatio(changespeed);
161  }
162 
163  if(!setval(this->value)) //if returns true, refresh is already called
164  refreshvalue();
165  }
166 
173  void SetLimits(T_ min, T_ max, bool exchange = true) {
174  if(exchange && min > max) {
175  using std::swap;
176 
177  swap(min, max);
178  }
179 
180  this->min = min;
181  this->max = max;
182 
183  if(valuemapping == internal::SliderValueMapping::ValueAndRange) {
184  SetSmoothChangeSpeedRatio(changespeed);
185  }
186 
187  if(setval(this->value)) //if returns true, refresh is already called
188  refreshvalue();
189  }
190 
192  T_ GetMinimum() const {
193  return min;
194  }
195 
197  SliderBase &operator =(T_ value) {
198  set(value);
199 
200  return *this;
201  }
202 
205  void SetRange(const T_ &value) {
206  auto r = DIV_(value, min, max);
207 
208  if(r < 0)
209  range = this->min;
210  else if(r > 1)
211  range = this->max;
212  else
213  range = value;
214 
215  if(valuemapping == internal::SliderValueMapping::ValueAndRange) {
216  SetSmoothChangeSpeedRatio(changespeed);
217  }
218 
219  this->refreshvalue();
220  }
221 
224  T_ GetRange() const {
225  return range;
226  }
227 
230  void SetSmallChange(const T_ &value) {
231  smallchange = value;
232 
233  this->refreshvalue();
234  }
235 
238  T_ GetSmallChange() const {
239  return smallchange;
240  }
241 
244  void SetLargeChange(const T_ &value) {
245  largechange = value;
246 
247  this->refreshvalue();
248  }
249 
252  T_ GetLargeChange() const {
253  return largechange;
254  }
255 
257  operator T_() const {
258  return get();
259  }
260 
261  virtual bool Activate() override {
262  return Focus();
263  }
264 
267  SetSmoothChangeSpeed(0);
268  }
269 
272  void SetSmoothChangeSpeed(T_ value) {
273  SetSmoothChangeSpeedRatio(DIV_(value, min, max));
274  }
275 
278  void SetSmoothChangeSpeedRatio(float value) {
279  changespeed = value;
280  if(valuemapping == internal::SliderValueMapping::OneValue) {
281  stack.SetValueTransitionSpeed({value, 0, 0, 0});
282  }
283  else if(valuemapping == internal::SliderValueMapping::ValueAndRange) {
284  auto r = DIV_(range, min, max);
285  auto m = DIV_(max, min, max);
286  if(m == 0 || r == 1) {
287  stack.SetValueTransitionSpeed({value, 0, 0, 0});
288  }
289  else {
290  stack.SetValueTransitionSpeed({value / (1 - r), 0, 0, 0});
291  }
292  }
293  }
294 
297  T_ GetSmoothChangeSpeed() const {
298  return FloatToValue(changespeed, min, max);
299  }
300 
304  return changespeed;
305  }
306 
308  bool IsSmoothChangeEnabled() const {
309  return changespeed != 0;
310  }
311 
314 
317 
320 
324 
328 
329 
330  protected:
331  virtual bool allowfocus() const override { return false; }
332 
334  T_ get() const {
335  return value;
336  }
337 
339  void set(const T_ &val) {
340  setval(val);
341  }
342 
343  template<internal::SliderValueMapping vm = valuemapping>
344  typename std::enable_if<vm == internal::SliderValueMapping::ValueAndRange, T_>::type
346  return max - range;
347  }
348 
349  template<internal::SliderValueMapping vm = valuemapping>
350  typename std::enable_if<vm != internal::SliderValueMapping::ValueAndRange, T_>::type
352  return max;
353  }
354 
355  bool setval(T_ val, bool instant = false) {
356  auto v = DIV_(val, min, actualmax());
357  if(v < 0)
358  val = min;
359  else if(v > 1)
360  val = actualmax();
361  else if(!(v <= 1)) { // in case of nan
362  val = 0;
363  }
364 
365  if(value != val) {
366  value = val;
367 
368  valuechanged(value);
369 
370  refreshvalue(instant);
371 
372  return true;
373  }
374 
375  return false;
376  }
377 
378  virtual void refreshvalue(bool instant = false) {
379  refreshme(instant);
380  }
381 
382  T_ value = T_{};
383  T_ min = T_{};
384  T_ max = T_{};
385 
386  //TODO move these out
387  T_ smallchange = T_{1};
388  T_ largechange = T_{10};
389 
390  //this is range that is covered by the scrollbar
391  T_ range = T_{};
392 
393  float changespeed = 1;
394 
395  internal::SliderInteractivity grab = internal::SliderInteractivity::None;
396  Geometry::Point downlocation = {0, 0};
397  float downvalue = 0;
398 
399  virtual void valuechanged(T_) = 0;
400 
401  private:
402 
403  template<internal::SliderValueMapping vm = valuemapping>
404  typename std::enable_if<vm == internal::SliderValueMapping::OneValue, void>::type
405  refreshme(bool instant = false) {
406  float val = DIV_(this->value, min, max);
407 
408  stack.SetValue(val, 0, 0, instant);
409  }
410 
411  template<internal::SliderValueMapping vm = valuemapping>
412  typename std::enable_if<vm == internal::SliderValueMapping::ValueAndRange, void>::type
413  refreshme(bool instant = false) {
414  float val = DIV_(this->value, this->min, this->max-this->range);
415  float v1 = DIV_(this->value, this->min, this->max);
416  float v2 = DIV_(this->value+this->range, this->min, this->max);
417  float r = DIV_(this->range, this->min, this->max);
418 
419  if(DIV_(max, min, max) == 0) {
420  r = 1;
421  }
422 
423  stack.SetValue(val, v1, v2, r, instant);
424  }
425 
426  template<internal::SliderInteractivity si = interactive>
427  typename std::enable_if<si == internal::SliderInteractivity::None, void>::type
428  setupinteractivity() {
429  }
430 
431  //TODO distribute according to actual interactivity
432  template<internal::SliderInteractivity si = interactive>
433  typename std::enable_if<si != internal::SliderInteractivity::None, void>::type
434  setupinteractivity() {
435  stack.HandleMouse();
436 
437  stack.SetClickEvent([this](auto tag, auto location, auto btn) {
438  if(tag == UI::ComponentTemplate::NoTag) {
439  auto ind = stack.ComponentAt(location);
440  if(ind != -1)
441  tag = stack.GetTemplate(ind).GetTag();
442  }
443 
444  if(btn == Input::Mouse::Button::Left) {
445  if((interactive & internal::SliderInteractivity::Jump) && tag == UI::ComponentTemplate::DragBarTag) {
446  auto val = stack.CoordinateToValue(UI::ComponentTemplate::DragTag, location)[0];
447 
448  if(val == std::numeric_limits<float>::infinity())
449  return;
450 
451  val = Clamp(val, 0.f, 1.f);
452 
453  setval(VAL_(val, this->min, this->max));
454  }
455  if((interactive & internal::SliderInteractivity::Page)) {
457  auto val = stack.CoordinateToValue(UI::ComponentTemplate::DragTag, location)[0];
458 
459  auto curval = DIV_(value, this->min, this->max);
460 
461  if(val > curval) {
462  SetValue(GetValue() + largechange);
463  }
464  else if(val < curval) {
465  SetValue(GetValue() - largechange);
466  }
467  }
468  else if(tag == UI::ComponentTemplate::IncrementTag) {
469  SetValue(GetValue() + largechange);
470  }
471  else if(tag == UI::ComponentTemplate::DecrementTag) {
472  SetValue(GetValue() - largechange);
473  }
474  }
475  }
476  });
477 
478  stack.SetMouseDownEvent([this](auto tag, auto location, auto btn) {
479  if(tag == UI::ComponentTemplate::NoTag) {
480  auto ind = stack.ComponentAt(location);
481  if(ind != -1)
482  tag = stack.GetTemplate(ind).GetTag();
483  }
484 
485  if((interactive & internal::SliderInteractivity::HandleOnly)) {
486  if(tag == UI::ComponentTemplate::DragTag) {
487  if(btn == Input::Mouse::Button::Left) {
488  downlocation = location;
489  downvalue = stack.GetValue()[0];
490  grab = internal::SliderInteractivity::HandleOnly;
491  }
492  }
493  }
494  });
495 
496  stack.SetMouseUpEvent([this](auto, auto, auto btn) {
497  if(btn == Input::Mouse::Button::Left) {
499  }
500  });
501 
502  stack.SetMouseMoveEvent([this](UI::ComponentTemplate::Tag, Geometry::Point location) {
503  if((interactive & internal::SliderInteractivity::HandleOnly) && grab == internal::SliderInteractivity::HandleOnly) {
504  auto val = stack.CoordinateToValue(UI::ComponentTemplate::DragTag, location - downlocation, true)[0];
505  if(val == std::numeric_limits<float>::infinity())
506  return;
507 
508  val += downvalue;
509 
510  val = Clamp(val, 0.f, 1.f);
511 
512  setval(VAL_(val, this->min, this->max));
513  }
514  });
515 
516  stack.SetOtherMouseEvent([this](UI::ComponentTemplate::Tag, Input::Mouse::EventType type, Geometry::Point, float amount) {
518  SetValue(T_(GetValue() - amount * smallchange));
519 
520  return true;
521  }
522 
523  return false;
524  });
525  }
526 
527  struct UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive, valuemapping>, T_> helper = UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive, valuemapping>, T_>(this);
528 
529  };
530 
531 
532 } }
Gorgon::Input::Mouse::None
@ None
Definition: Mouse.h:32
Gorgon::swap
void swap(Event< Source_, Args_... > &l, Event< Source_, Args_... > &r)
Swaps two events.
Definition: Event.h:351
Gorgon::Widgets::SliderBase::valuechanged
virtual void valuechanged(T_)=0
Gorgon::Widgets::SliderBase::SetMaximum
void SetMaximum(const T_ &value)
Sets the maximum value that this slider reaches up to.
Definition: Slider.h:138
Gorgon::NumericProperty
Supports arithmetic operators including +, * ..., +=, ...
Definition: Property.h:108
Gorgon::Widgets::SliderBase::SetSmallChange
void SetSmallChange(const T_ &value)
Sets the amount of change on a small change action.
Definition: Slider.h:230
Gorgon::Widgets::SliderBase
This is an internal basis for slider.
Definition: Slider.h:59
Gorgon::Input::Mouse::Scroll_Hor
@ Scroll_Hor
Definition: Mouse.h:25
Gorgon::Widgets::SliderBase::SetMinimum
void SetMinimum(const T_ &value)
Sets the minimum value that this slider reaches up to.
Definition: Slider.h:156
Gorgon::Widgets::SliderBase::SetSmoothChangeSpeed
void SetSmoothChangeSpeed(T_ value)
Adjusts the smooth change speed.
Definition: Slider.h:272
Gorgon::UI::Template
This class stores visual information about a widget template.
Definition: Template.h:392
Common.h
Gorgon::Widgets::SliderBase::get
T_ get() const
Returns the value in the box.
Definition: Slider.h:334
Gorgon::Widgets::SliderBase::set
void set(const T_ &val)
Changes the value in the box.
Definition: Slider.h:339
Gorgon::Widgets::SliderBase::Range
NumericProperty< SliderBase, T_, &SliderBase::GetRange, &SliderBase::SetRange > Range
This is used to show how much more the scroller can be scrolled.
Definition: Slider.h:319
Gorgon::Widgets::SliderBase::GetLargeChange
T_ GetLargeChange() const
Returns the amount of change on a large change action.
Definition: Slider.h:252
Gorgon::Widgets::SliderBase::SetSmoothChangeSpeedRatio
void SetSmoothChangeSpeedRatio(float value)
Adjusts the smooth change speed.
Definition: Slider.h:278
Gorgon::Widgets::SliderBase::GetValue
T_ GetValue() const
Gets the current value of the slider.
Definition: Slider.h:132
Gorgon::UI::ComponentTemplate::DecrementTag
@ DecrementTag
Definition: Template.h:865
Gorgon::Widgets::SliderBase::GetRange
T_ GetRange() const
Returns the range the container can display.
Definition: Slider.h:224
Gorgon::Widgets::Registry::Active
static Registry & Active()
Definition: Registry.h:73
Gorgon::Widgets::SliderBase::DisableSmoothChange
void DisableSmoothChange()
Disables smooth change.
Definition: Slider.h:266
Gorgon::Widgets::SliderBase::SetRange
void SetRange(const T_ &value)
Sets the range the container can display.
Definition: Slider.h:205
Gorgon::Widgets::Registry::TemplateType
TemplateType
This enum lists all possible template types.
Definition: Registry.h:18
Gorgon
Root namespace for Gorgon Game Engine.
Definition: Any.h:19
Gorgon::Input::Mouse::Scroll_Vert
@ Scroll_Vert
Definition: Mouse.h:24
Gorgon::Widgets::SliderBase::GetSmoothChangeSpeedRatio
float GetSmoothChangeSpeedRatio() const
Returns the smooth change in ratio to the maximum.
Definition: Slider.h:303
Gorgon::Widgets::SliderBase::SliderBase
SliderBase(T_ cur=T_{}, Registry::TemplateType type=Registry::Progress_Regular)
Definition: Slider.h:100
Gorgon::Widgets::SliderBase::GetSmallChange
T_ GetSmallChange() const
Returns the amount of change on a small change action.
Definition: Slider.h:238
Gorgon::UI::ComponentTemplate::DragTag
@ DragTag
Definition: Template.h:858
Gorgon::Geometry::Point
basic_Point< int > Point
Definition: Point.h:598
Gorgon::Widgets::SliderBase::allowfocus
virtual bool allowfocus() const override
Should return true if the widget can be focused.
Definition: Slider.h:331
Gorgon::Range
This class represents a range of values.
Definition: Types.h:22
Gorgon::UI::ComponentTemplate::DragBarTag
@ DragBarTag
Definition: Template.h:860
Gorgon::Widgets::SliderBase::SliderBase
SliderBase(const UI::Template &temp, T_ cur=T_{})
Definition: Slider.h:105
Gorgon::Widgets::SliderBase::SetLimits
void SetLimits(T_ min, T_ max, bool exchange=true)
Sets minimum and maximum limits.
Definition: Slider.h:173
Gorgon::Widgets::SliderBase::refreshvalue
virtual void refreshvalue(bool instant=false)
Definition: Slider.h:378
Gorgon::Widgets::SliderBase::setval
bool setval(T_ val, bool instant=false)
Definition: Slider.h:355
Gorgon::Widgets::Registry::Progress_Regular
@ Progress_Regular
Definition: Registry.h:39
Gorgon::Widgets::SliderBase::Activate
virtual bool Activate() override
Activates the widget.
Definition: Slider.h:261
Gorgon::Widgets::SliderBase::GetSmoothChangeSpeed
T_ GetSmoothChangeSpeed() const
Returns the smooth change speed.
Definition: Slider.h:297
Gorgon::UI::internal::prophelper
Definition: Helpers.h:7
Gorgon::Geometry::basic_Point
This class represents a 2D point.
Definition: Point.h:32
Gorgon::Clamp
T_ Clamp(T_ v, T_ min, T_ max)
Performs clamping on the given value and returns the result.
Definition: Types.h:99
Gorgon::Widgets::SliderBase::SmallChange
NumericProperty< SliderBase, T_, &SliderBase::GetSmallChange, &SliderBase::SetSmallChange > SmallChange
Controls the amount of change on a small change action.
Definition: Slider.h:323
Gorgon::Widgets::SliderBase::actualmax
std::enable_if< vm !=internal::SliderValueMapping::ValueAndRange, T_ >::type actualmax()
Definition: Slider.h:351
Registry.h
Gorgon::Widgets::SliderBase::SetValue
void SetValue(const T_ &value, bool instant=false)
Sets the current value of the slider.
Definition: Slider.h:127
Gorgon::Input::Mouse::EventType
EventType
The type of a mouse event.
Definition: Mouse.h:14
Gorgon::Widgets::SliderBase::SetLargeChange
void SetLargeChange(const T_ &value)
Sets the amount of change on a large change action.
Definition: Slider.h:244
Gorgon::Widgets::SliderBase::SliderBase
SliderBase(const UI::Template &temp, T_ cur, T_ max)
Definition: Slider.h:107
Gorgon::UI::ComponentTemplate::NoTag
@ NoTag
Definition: Template.h:851
Gorgon::Property< UI::internal::prophelper< SliderBase< int, DIV_, VAL_, Gorgon::NumericProperty, interactive, valuemapping >, int >, int, Getter_, Setter_ >::Type
int Type
Definition: Property.h:32
Gorgon::Widgets::SliderBase::IsSmoothChangeEnabled
bool IsSmoothChangeEnabled() const
Returns if the smooth change is enabled.
Definition: Slider.h:308
Gorgon::Widgets::SliderBase::GetMaximum
T_ GetMaximum() const
Returns the current maximum value.
Definition: Slider.h:150
Gorgon::Widgets::SliderBase::LargeChange
NumericProperty< SliderBase, T_, &SliderBase::GetLargeChange, &SliderBase::SetLargeChange > LargeChange
Controls the amount of change on a large change action.
Definition: Slider.h:327
Gorgon::Widgets::SliderBase::Minimum
NumericProperty< SliderBase, T_, &SliderBase::GetMinimum, &SliderBase::SetMinimum > Minimum
This property controls the minimum value that the slider can have.
Definition: Slider.h:316
Gorgon::UI::ComponentTemplate::IncrementTag
@ IncrementTag
Definition: Template.h:864
Gorgon::Widgets::SliderBase::Maximum
NumericProperty< SliderBase, T_, &SliderBase::GetMaximum, &SliderBase::SetMaximum > Maximum
This property controls the maximum value that the slider can have.
Definition: Slider.h:313
Gorgon::UI::ComponentStackWidget
This class acts as a widget base that uses component stack to handle rendering, resizing and other op...
Definition: ComponentStackWidget.h:14
Gorgon::Input::Mouse::operator&
Button operator&(Button l, Button r)
Definition: Mouse.h:49
Gorgon::UI::ComponentTemplate::Tag
Tag
Tags mark a component to be modified in a way meaningful to specific widgets.
Definition: Template.h:850
Gorgon::Input::Keyboard::Keycodes::Left
constexpr Key Left
Definition: Keyboard.h:62
Gorgon::Widgets::SliderBase::actualmax
std::enable_if< vm==internal::SliderValueMapping::ValueAndRange, T_ >::type actualmax()
Definition: Slider.h:345
Gorgon::Widgets::SliderBase::GetMinimum
T_ GetMinimum() const
Returns the current minimum value.
Definition: Slider.h:192