Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Slider.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 
4 using System;
5 
6 using SiliconStudio.Core;
7 using SiliconStudio.Core.Mathematics;
8 using SiliconStudio.Paradox.Input;
9 using SiliconStudio.Paradox.UI.Events;
10 
11 namespace SiliconStudio.Paradox.UI.Controls
12 {
13  /// <summary>
14  /// Represents a slider element.
15  /// </summary>
16  public class Slider : UIElement
17  {
18  private float value;
19 
20  private bool shouldSnapToTicks;
21 
22  private Orientation orientation;
23 
24  /// <summary>
25  /// The key to the TrackBackgroundImage dependency property.
26  /// </summary>
27  public static readonly PropertyKey<UIImage> TrackBackgroundImagePropertyKey = new PropertyKey<UIImage>("TrackBackgroundImageKey", typeof(Slider), DefaultValueMetadata.Static<UIImage>(null), ObjectInvalidationMetadata.New<UIImage>(InvalidateTrackBackground));
28 
29  /// <summary>
30  /// The key to the TrackForegroundImage dependency property.
31  /// </summary>
32  public static readonly PropertyKey<UIImage> TrackForegroundImagePropertyKey = new PropertyKey<UIImage>("TrackForegroundImageKey", typeof(Slider), DefaultValueMetadata.Static<UIImage>(null));
33 
34  /// <summary>
35  /// The key to the ThumbImage dependency property.
36  /// </summary>
37  public static readonly PropertyKey<UIImage> ThumbImagePropertyKey = new PropertyKey<UIImage>("ThumbImageKey", typeof(Slider), DefaultValueMetadata.Static<UIImage>(null));
38 
39  /// <summary>
40  /// The key to the TickImage dependency property.
41  /// </summary>
42  public static readonly PropertyKey<UIImage> TickImagePropertyKey = new PropertyKey<UIImage>("ThickImageKey", typeof(Slider), DefaultValueMetadata.Static<UIImage>(null));
43 
44  /// <summary>
45  /// The key to the MouseOverThumbImage dependency property.
46  /// </summary>
47  public static readonly PropertyKey<UIImage> MouseOverThumbImagePropertyKey = new PropertyKey<UIImage>("MouseOverThumbImageKey", typeof(Slider), DefaultValueMetadata.Static<UIImage>(null));
48 
49  /// <summary>
50  /// The key to the Minimum dependency property.
51  /// </summary>
52  public static readonly PropertyKey<float> MinimumPropertyKey = new PropertyKey<float>("MinimumKey", typeof(Slider), DefaultValueMetadata.Static<float>(0), ObjectInvalidationMetadata.New<float>(ValidateExtremum));
53 
54  /// <summary>
55  /// The key to the Maximum dependency property.
56  /// </summary>
57  public static readonly PropertyKey<float> MaximumPropertyKey = new PropertyKey<float>("MaximumKey", typeof(Slider), DefaultValueMetadata.Static<float>(1), ObjectInvalidationMetadata.New<float>(ValidateExtremum));
58 
59  /// <summary>
60  /// The key to the Step dependency property.
61  /// </summary>
62  public static readonly PropertyKey<float> StepPropertyKey = new PropertyKey<float>("StepKey", typeof(Slider), DefaultValueMetadata.Static(0.1f));
63 
64  /// <summary>
65  /// The key to the TickFrequency dependency property.
66  /// </summary>
67  public static readonly PropertyKey<float> TickFrequencyPropertyKey = new PropertyKey<float>("TickFrequencyKey", typeof(Slider), DefaultValueMetadata.Static(10f), ObjectInvalidationMetadata.New<float>(TickFrequencyInvalidated));
68 
69  /// <summary>
70  /// The key to the TickFrequency dependency property.
71  /// </summary>
72  public static readonly PropertyKey<float> TickOffsetPropertyKey = new PropertyKey<float>("TickOffsetKey", typeof(Slider), DefaultValueMetadata.Static(10f));
73 
74  /// <summary>
75  /// The key to the TrackStartingOffsets dependency property.
76  /// </summary>
77  public static readonly PropertyKey<Vector2> TrackStartingOffsetsrPropertyKey = new PropertyKey<Vector2>("TrackStartingOffsetKey", typeof(Slider), DefaultValueMetadata.Static(new Vector2()));
78 
79  private static void InvalidateTrackBackground(object propertyowner, PropertyKey<UIImage> propertykey, UIImage propertyoldvalue)
80  {
81  var slider = (Slider)propertyowner;
82 
83  slider.InvalidateMeasure();
84 
85  if (propertyoldvalue != null)
86  propertyoldvalue.IdealSizeChanged -= slider.OnIdealSizeChanged;
87 
88  if(slider.TrackBackgroundImage != null)
89  slider.TrackBackgroundImage.IdealSizeChanged += slider.OnIdealSizeChanged;
90  }
91 
92  private void OnIdealSizeChanged(object sender, EventArgs e)
93  {
94  InvalidateMeasure();
95  }
96 
97  private static void ValidateExtremum(object propertyowner, PropertyKey<float> propertykey, float propertyoldvalue)
98  {
99  var slider = (Slider)propertyowner;
100 
101  if (slider.Maximum < slider.Minimum)
102  {
103  slider.DependencyProperties.Set(propertykey, propertyoldvalue);
104 
105  // ReSharper disable once NotResolvedInText
106  throw new ArgumentOutOfRangeException("Maximum should be greater or equal than Minimum.");
107  }
108  }
109 
110  private static void TickFrequencyInvalidated(object propertyowner, PropertyKey<float> propertykey, float propertyoldvalue)
111  {
112  var slider = (Slider)propertyowner;
113 
114  if (slider.TickFrequency < 1)
115  slider.TickFrequency = 1;
116 
117  slider.Value = slider.value; // snap to tick if enabled
118  }
119 
120  static Slider()
121  {
122  EventManager.RegisterClassHandler(typeof(Slider), ValueChangedEvent, ValueChangedClassHandler);
123  }
124 
125  /// <summary>
126  /// Create a new instance of slider.
127  /// </summary>
128  public Slider()
129  {
130  CanBeHitByUser = true;
131  VerticalAlignment = VerticalAlignment.Center;
132  HorizontalAlignment = HorizontalAlignment.Center;
133  DrawLayerNumber += 4; // track background, track foreground, ticks, thumb
134  }
135 
136  /// <summary>
137  /// Gets or sets the image to display as Track background.
138  /// </summary>
139  public UIImage TrackBackgroundImage
140  {
141  get { return DependencyProperties.Get(TrackBackgroundImagePropertyKey); }
142  set { DependencyProperties.Set(TrackBackgroundImagePropertyKey, value); }
143  }
144 
145  /// <summary>
146  /// Gets or sets the image to display as Track foreground.
147  /// </summary>
148  public UIImage TrackForegroundImage
149  {
150  get { return DependencyProperties.Get(TrackForegroundImagePropertyKey); }
151  set { DependencyProperties.Set(TrackForegroundImagePropertyKey, value); }
152  }
153 
154  /// <summary>
155  /// Gets or sets the image to display as slider thumb (button).
156  /// </summary>
157  public UIImage ThumbImage
158  {
159  get { return DependencyProperties.Get(ThumbImagePropertyKey); }
160  set { DependencyProperties.Set(ThumbImagePropertyKey, value); }
161  }
162 
163  /// <summary>
164  /// Gets or sets the image to display as slider thumb (button) when the mouse is over the slider.
165  /// </summary>
166  public UIImage MouseOverThumbImage
167  {
168  get { return DependencyProperties.Get(MouseOverThumbImagePropertyKey); }
169  set { DependencyProperties.Set(MouseOverThumbImagePropertyKey, value); }
170  }
171 
172  /// <summary>
173  /// Gets or sets the image to display as tick.
174  /// </summary>
175  public UIImage TickImage
176  {
177  get { return DependencyProperties.Get(TickImagePropertyKey); }
178  set { DependencyProperties.Set(TickImagePropertyKey, value); }
179  }
180 
181  /// <summary>
182  /// Gets or sets the smallest possible value of the slider.
183  /// </summary>
184  public float Minimum
185  {
186  get { return DependencyProperties.Get(MinimumPropertyKey); }
187  set { DependencyProperties.Set(MinimumPropertyKey, value); }
188  }
189 
190  /// <summary>
191  /// Gets or sets the greatest possible value of the slider.
192  /// </summary>
193  public float Maximum
194  {
195  get { return DependencyProperties.Get(MaximumPropertyKey); }
196  set { DependencyProperties.Set(MaximumPropertyKey, value); }
197  }
198 
199  /// <summary>
200  /// Gets or sets the step of a <see cref="Value"/> change.
201  /// </summary>
202  public float Step
203  {
204  get { return DependencyProperties.Get(StepPropertyKey); }
205  set { DependencyProperties.Set(StepPropertyKey, value); }
206  }
207 
208  /// <summary>
209  /// Gets or sets the current value of the slider.
210  /// </summary>
211  /// <remarks>value is truncated between <see cref="Minimum"/> and <see cref="Maximum"/></remarks>
212  public float Value
213  {
214  get { return value; }
215  set
216  {
217  var oldValue = Value;
218 
219  this.value = Math.Min(Maximum, Math.Max(Minimum, value));
220  if(ShouldSnapToTicks)
221  this.value = CalculateClosestTick(this.value);
222 
223  if(Math.Abs(oldValue - this.value) > MathUtil.ZeroTolerance)
224  RaiseEvent(new RoutedEventArgs(ValueChangedEvent));
225  }
226  }
227 
228  /// <summary>
229  /// Gets or sets the frequency of the ticks on the slider track.
230  /// </summary>
231  /// <remarks>Provided value is truncated to be greater or equal the 1</remarks>
232  public float TickFrequency
233  {
234  get { return DependencyProperties.Get(TickFrequencyPropertyKey); }
235  set { DependencyProperties.Set(TickFrequencyPropertyKey, value); }
236  }
237 
238  /// <summary>
239  /// Gets or sets the offset in virtual pixels between the center of the track and center of the ticks (for an not-stretched slider).
240  /// </summary>
241  public float TickOffset
242  {
243  get { return DependencyProperties.Get(TickOffsetPropertyKey); }
244  set { DependencyProperties.Set(TickOffsetPropertyKey, value); }
245  }
246 
247  /// <summary>
248  /// Gets or sets the left/right offsets specifying where the track region starts.
249  /// </summary>
250  public Vector2 TrackStartingOffsets
251  {
252  get { return DependencyProperties.Get(TrackStartingOffsetsrPropertyKey); }
253  set { DependencyProperties.Set(TrackStartingOffsetsrPropertyKey, value); }
254  }
255 
256  /// <summary>
257  /// Gets or sets the value indicating if the default direction of the slider should reversed or not.
258  /// </summary>
259  public bool IsDirectionReversed { get; set; }
260 
261  /// <summary>
262  /// Gets or sets the value indicating if the ticks should be displayed or not.
263  /// </summary>
264  public bool AreTicksDisplayed { get; set; }
265 
266  /// <summary>
267  /// Gets or sets the value indicating if the slider <see cref="Value"/> should be snapped to the ticks or not.
268  /// </summary>
269  public bool ShouldSnapToTicks
270  {
271  get { return shouldSnapToTicks; }
272  set
273  {
274  shouldSnapToTicks = value;
275  Value = Value; // snap if enabled
276  }
277  }
278 
279  /// <summary>
280  /// Gets or sets the orientation of the slider.
281  /// </summary>
282  public Orientation Orientation
283  {
284  get { return orientation; }
285  set
286  {
287  orientation = value;
288 
289  InvalidateMeasure();
290  }
291  }
292 
293  /// <summary>
294  /// Snap the current <see cref="Value"/> to the closest tick.
295  /// </summary>
296  public void SnapToClosestTick()
297  {
298  Value = CalculateClosestTick(Value);
299  }
300 
301  /// <summary>
302  /// Calculate the value of the closest tick to the provided value.
303  /// </summary>
304  /// <param name="rawValue">The current raw value</param>
305  /// <returns>The value adjusted to the closest tick</returns>
306  protected float CalculateClosestTick(float rawValue)
307  {
308  var absoluteValue = rawValue - Minimum;
309  var step = (Maximum - Minimum) / TickFrequency;
310  var times = (float)Math.Round(absoluteValue / step);
311  return times * step;
312  }
313 
314  /// <summary>
315  /// Increase the <see cref="Value"/> by <see cref="Step"/>.
316  /// </summary>
317  /// <remarks>If <see cref="ShouldSnapToTicks"/> is <value>True</value> then it increases of at least one tick.</remarks>
318  public void Increase()
319  {
320  Value += CalculateIncreamentValue();
321  }
322 
323  /// <summary>
324  /// Decrease the <see cref="Value"/> by <see cref="Step"/>.
325  /// </summary>
326  /// <remarks>If <see cref="ShouldSnapToTicks"/> is <value>True</value> then it decreases of at least one tick.</remarks>
327  public void Decrease()
328  {
329  Value -= CalculateIncreamentValue();
330  }
331 
332  private float CalculateIncreamentValue()
333  {
334  return shouldSnapToTicks? Math.Max(Step, (Maximum - Minimum) / TickFrequency): Step;
335  }
336 
337  protected override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
338  {
339  var image = TrackBackgroundImage;
340  if (image == null)
341  return base.MeasureOverride(availableSizeWithoutMargins);
342 
343  var idealSize = image.ImageIdealSize.Y;
344  var desiredSize = new Vector3(idealSize, idealSize, 0);
345  desiredSize[(int)Orientation] = availableSizeWithoutMargins[(int)Orientation];
346 
347  return desiredSize;
348  }
349 
350  /// <summary>
351  /// Occurs when the value of the slider changed.
352  /// </summary>
353  /// <remarks>A ValueChanged event is bubbling</remarks>
354  public event EventHandler<RoutedEventArgs> ValueChanged
355  {
356  add { AddHandler(ValueChangedEvent, value); }
357  remove { RemoveHandler(ValueChangedEvent, value); }
358  }
359 
360  /// <summary>
361  /// Identifies the <see cref="ValueChanged"/> routed event.
362  /// </summary>
363  public static readonly RoutedEvent<RoutedEventArgs> ValueChangedEvent = EventManager.RegisterRoutedEvent<RoutedEventArgs>(
364  "ValueChanged",
365  RoutingStrategy.Bubble,
366  typeof(Slider));
367 
368  /// <summary>
369  /// The class handler of the event <see cref="ValueChanged"/>.
370  /// This method can be overridden in inherited classes to perform actions common to all instances of a class.
371  /// </summary>
372  /// <param name="args">The arguments of the event</param>
373  protected virtual void OnValueChanged(RoutedEventArgs args)
374  {
375 
376  }
377 
378  private static void ValueChangedClassHandler(object sender, RoutedEventArgs args)
379  {
380  var slider = (Slider)sender;
381 
382  slider.OnValueChanged(args);
383  }
384 
385  protected override void OnTouchDown(TouchEventArgs args)
386  {
387  base.OnTouchDown(args);
388 
389  SetValueFromTouchPosition(args.WorldPosition);
390  }
391 
392  protected override void OnTouchMove(TouchEventArgs args)
393  {
394  base.OnTouchMove(args);
395 
396  SetValueFromTouchPosition(args.WorldPosition);
397  }
398 
399  internal override void OnKeyDown(KeyEventArgs args)
400  {
401  base.OnKeyDown(args);
402 
403  if(args.Key == Keys.Right)
404  Increase();
405  if(args.Key == Keys.Left)
406  Decrease();
407  }
408 
409  /// <summary>
410  /// Set <see cref="Value"/> from the world position of a touch event.
411  /// </summary>
412  /// <param name="touchPostionWorld">The world position of the touch</param>
413  protected void SetValueFromTouchPosition(Vector3 touchPostionWorld)
414  {
415  var axis = (int)Orientation;
416  var offsets = TrackStartingOffsets;
417  var elementSize = RenderSize[axis];
418  var touchPosition = touchPostionWorld[axis] - WorldMatrixInternal[12 + axis] + elementSize/2;
419  var ratio = (touchPosition - offsets.X) / (elementSize - offsets.X - offsets.Y);
420  Value = (Orientation == Orientation.Vertical ^ IsDirectionReversed) ? 1 - ratio : ratio;
421  }
422  }
423 }
Provides a base class for all the User Interface elements in Paradox applications.
Definition: UIElement.cs:21
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
override void OnTouchMove(TouchEventArgs args)
The class handler of the event TouchMove. This method can be overridden in inherited classes to perfo...
Definition: Slider.cs:392
Represents a two dimensional mathematical vector.
Definition: Vector2.cs:42
Slider()
Create a new instance of slider.
Definition: Slider.cs:128
void Increase()
Increase the Value by Step.
Definition: Slider.cs:318
Keys
Enumeration for keys.
Definition: Keys.cs:8
VerticalAlignment
Describes how a child element is vertically positioned or stretched within a parent's layout slot...
Orientation
Defines the different orientations that a control or layout can have.
Definition: Orientation.cs:8
void SnapToClosestTick()
Snap the current Value to the closest tick.
Definition: Slider.cs:296
Contains state information and event data associated with a routed event.
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
HorizontalAlignment
Indicates where an element should be displayed on the horizontal axis relative to the allocated layou...
override void OnTouchDown(TouchEventArgs args)
The class handler of the event TouchDown. This method can be overridden in inherited classes to perfo...
Definition: Slider.cs:385
Vector3 WorldPosition
Gets the position of the touch in the UI virtual world space.
Represents a slider element.
Definition: Slider.cs:16
Provides data for touch input events.
virtual void OnValueChanged(RoutedEventArgs args)
The class handler of the event ValueChanged. This method can be overridden in inherited classes to pe...
Definition: Slider.cs:373
void SetValueFromTouchPosition(Vector3 touchPostionWorld)
Set Value from the world position of a touch event.
Definition: Slider.cs:413
Represents and identifies a routed event and declares its characteristics.
Definition: RoutedEvent.cs:10
SiliconStudio.Core.Mathematics.Vector3 Vector3
Class holding all the data required to define an UI image.
Definition: UIImage.cs:15
float CalculateClosestTick(float rawValue)
Calculate the value of the closest tick to the provided value.
Definition: Slider.cs:306
void Decrease()
Decrease the Value by Step.
Definition: Slider.cs:327
Android.Widget.Orientation Orientation
Definition: Section.cs:9
A class that represents a tag propety.
Definition: PropertyKey.cs:17
override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
When overridden in a derived class, measures the size in layout required for possible child elements ...
Definition: Slider.cs:337