[NUI] Improve AddPackageTheme (#2603)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Wearable / src / public / CircularSlider.cs
1 /*
2  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using Tizen.NUI.BaseComponents;
19 using Tizen.NUI.Binding;
20 using Tizen.NUI.Components;
21 using System.ComponentModel;
22
23 namespace Tizen.NUI.Wearable
24 {
25     /// <summary>
26     /// Value Changed event data.
27     /// </summary>
28     [EditorBrowsable(EditorBrowsableState.Never)]
29     public class CircularSliderValueChangedEventArgs : EventArgs
30     {
31         private float currentValue = 0.0f;
32
33         /// <summary>
34         /// Current value
35         /// </summary>
36         [EditorBrowsable(EditorBrowsableState.Never)]
37         public float CurrentValue
38         {
39             get { return currentValue; }
40             set { currentValue = value; }
41         }
42     }
43
44     /// <summary>
45     /// The CircularSlider class of Wearable is used to let users select a value from a continuous or discrete range of values by moving the slider thumb.
46     /// CircularSlider shows the current value with the length of the line.
47     /// </summary>
48     [EditorBrowsable(EditorBrowsableState.Never)]
49     public class CircularSlider : Control
50     {
51         #region Fields
52
53         /// <summary>Bindable property of Thickness</summary>
54         [EditorBrowsable(EditorBrowsableState.Never)]
55         public static readonly BindableProperty ThicknessProperty = BindableProperty.Create(nameof(Thickness), typeof(float), typeof(CircularSlider), default(float), propertyChanged: (bindable, oldValue, newValue) =>
56         {
57             var instance = ((CircularSlider)bindable);
58             instance.CurrentStyle.Thickness = (float)newValue;
59             instance.UpdateVisualThickness((float)newValue);
60         },
61         defaultValueCreator: (bindable) =>
62         {
63             return ((CircularSlider)bindable).CurrentStyle.Thickness;
64         });
65
66         /// <summary>Bindable property of MaxValue</summary>
67         [EditorBrowsable(EditorBrowsableState.Never)]
68         public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(float), typeof(CircularSlider), default(float), propertyChanged: (bindable, oldValue, newValue) =>
69         {
70             var instance = (CircularSlider)bindable;
71             if (newValue != null)
72             {
73                 instance.maxValue = (float)newValue;
74                 instance.UpdateValue();
75             }
76         },
77         defaultValueCreator: (bindable) =>
78         {
79             var instance = (CircularSlider)bindable;
80             return instance.maxValue;
81         });
82
83         /// <summary>Bindable property of MinValue</summary>
84         [EditorBrowsable(EditorBrowsableState.Never)]
85         public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(float), typeof(CircularSlider), default(float), propertyChanged: (bindable, oldValue, newValue) =>
86         {
87             var instance = (CircularSlider)bindable;
88             if (newValue != null)
89             {
90                 instance.minValue = (float)newValue;
91                 instance.UpdateValue();
92             }
93         },
94         defaultValueCreator: (bindable) =>
95         {
96             var instance = (CircularSlider)bindable;
97             return instance.minValue;
98         });
99
100         /// <summary>Bindable property of CurrentValue</summary>
101         [EditorBrowsable(EditorBrowsableState.Never)]
102         public static readonly BindableProperty CurrentValueProperty = BindableProperty.Create(nameof(CurrentValue), typeof(float), typeof(CircularSlider), default(float), propertyChanged: (bindable, oldValue, newValue) =>
103         {
104             var instance = (CircularSlider)bindable;
105             if (newValue != null)
106             {
107                 if ((float)newValue > instance.maxValue || (float)newValue < instance.minValue)
108                 {
109                     return;
110                 }
111                 instance.currentValue = (float)newValue;
112                 instance.UpdateValue();
113             }
114         },
115         defaultValueCreator: (bindable) =>
116         {
117             var instance = (CircularSlider)bindable;
118             return instance.currentValue;
119         });
120
121         /// <summary>Bindable property of TrackColor</summary>
122         [EditorBrowsable(EditorBrowsableState.Never)]
123         public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(CircularSlider), null, propertyChanged: (bindable, oldValue, newValue) =>
124         {
125             var instance = (CircularSlider)bindable;
126             instance.CurrentStyle.TrackColor = (Color)newValue;
127             instance.UpdateTrackVisualColor((Color)newValue);
128         },
129         defaultValueCreator: (bindable) =>
130         {
131             return ((CircularSlider)bindable).CurrentStyle.TrackColor;
132         });
133
134         /// <summary>Bindable property of ProgressColor</summary>
135         [EditorBrowsable(EditorBrowsableState.Never)]
136         public static readonly BindableProperty ProgressColorProperty = BindableProperty.Create(nameof(ProgressColor), typeof(Color), typeof(CircularSlider), null, propertyChanged: (bindable, oldValue, newValue) =>
137         {
138             var instance = (CircularSlider)bindable;
139             instance.CurrentStyle.ProgressColor = (Color)newValue;
140             instance.UpdateProgressVisualColor((Color)newValue);
141         },
142         defaultValueCreator: (bindable) =>
143         {
144             return ((CircularSlider)bindable).CurrentStyle.ProgressColor;
145         });
146
147         /// <summary>Bindable property of ThumbSize</summary>
148         [EditorBrowsable(EditorBrowsableState.Never)]
149         public static readonly BindableProperty ThumbSizeProperty = BindableProperty.Create(nameof(ThumbSize), typeof(Size), typeof(CircularSlider), new Size(0,0), propertyChanged: (bindable, oldValue, newValue) =>
150         {
151             var instance = (CircularSlider)bindable;
152             if (newValue != null)
153             {
154                 instance.thumbSize = (Size)newValue;
155                 instance.UpdateThumbVisualSize((Size)newValue);
156             }
157         },
158         defaultValueCreator: (bindable) =>
159         {
160             var instance = (CircularSlider)bindable;
161             return instance.thumbSize;
162         });
163
164         /// <summary>Bindable property of ThumbColor</summary>
165         [EditorBrowsable(EditorBrowsableState.Never)]
166         public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(CircularSlider), null, propertyChanged: (bindable, oldValue, newValue) =>
167         {
168             var instance = (CircularSlider)bindable;
169             instance.CurrentStyle.ThumbColor = (Color)newValue;
170             instance.UpdateThumbVisualColor((Color)newValue);
171         },
172         defaultValueCreator: (bindable) =>
173         {
174             return ((CircularSlider)bindable).CurrentStyle.ThumbColor;
175         });
176
177         /// <summary>Bindable property of IsEnabled</summary>
178         [EditorBrowsable(EditorBrowsableState.Never)]
179         public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(CircularSlider), true, propertyChanged: (bindable, oldValue, newValue) =>
180         {
181             var instance = (CircularSlider)bindable;
182             if (newValue != null)
183             {
184                 instance.privateIsEnabled = (bool)newValue;
185             }
186         },
187         defaultValueCreator: (bindable) =>
188         {
189             var instance = (CircularSlider)bindable;
190             return instance.privateIsEnabled;
191         });
192
193         private const string TrackVisualName = "Track";
194         private const string ProgressVisualName = "Progress";
195         private const string ThumbVisualName = "Thumb";
196         private ArcVisual trackVisual;
197         private ArcVisual progressVisual;
198         private ArcVisual thumbVisual;
199
200         private float maxValue = 100;
201         private float minValue = 0;
202         private float currentValue = 0;
203         private Size thumbSize;
204         private bool isEnabled = true;
205
206         float sliderPadding = 6.0f;
207
208         private Animation sweepAngleAnimation;
209         private Animation thumbAnimation;
210
211         #endregion Fields
212
213
214         #region Constructors
215
216         static CircularSlider()
217         {
218             ThemeManager.AddPackageTheme(DefaultThemeCreator.Instance);
219         }
220
221         /// <summary>
222         /// The constructor of CircularSlider.
223         /// </summary>
224         [EditorBrowsable(EditorBrowsableState.Never)]
225         public CircularSlider() : base()
226         {
227             Initialize();
228         }
229
230         /// <summary>
231         /// The constructor of the CircularSlider class with specific style.
232         /// </summary>
233         /// <param name="progressStyle">The style object to initialize the CircularSlider.</param>
234         [EditorBrowsable(EditorBrowsableState.Never)]
235         public CircularSlider(CircularSliderStyle progressStyle) : base(progressStyle)
236         {
237             Initialize();
238         }
239
240         #endregion Constructors
241
242         #region Events
243
244         /// <summary>
245         /// The value changed event handler.
246         /// </summary>
247         [EditorBrowsable(EditorBrowsableState.Never)]
248         public event EventHandler<CircularSliderValueChangedEventArgs> ValueChanged;
249
250         #endregion Events
251
252         #region Properties
253
254         /// <summary>
255         /// Return a copied Style instance of CircularSlider
256         /// </summary>
257         /// <remarks>
258         /// It returns copied Style instance and changing it does not effect to the CircularSlider.
259         /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
260         /// </remarks>
261         [EditorBrowsable(EditorBrowsableState.Never)]
262         public new CircularSliderStyle Style
263         {
264             get
265             {
266                 var result = new CircularSliderStyle(ViewStyle as CircularSliderStyle);
267                 result.CopyPropertiesFromView(this);
268                 return result;
269             }
270         }
271
272         /// <summary>
273         /// The thickness of the track and progress.
274         /// </summary>
275         [EditorBrowsable(EditorBrowsableState.Never)]
276         public float Thickness
277         {
278             get
279             {
280                 return (float)GetValue(ThicknessProperty);
281             }
282             set
283             {
284                 SetValue(ThicknessProperty, value);
285             }
286         }
287
288         /// <summary>
289         /// The property to get/set the maximum value of the CircularSlider.
290         /// The default value is 100.
291         /// </summary>
292         [EditorBrowsable(EditorBrowsableState.Never)]
293         public float MaxValue
294         {
295             get
296             {
297                 return (float)GetValue(MaxValueProperty);
298             }
299             set
300             {
301                 SetValue(MaxValueProperty, value);
302             }
303         }
304
305         /// <summary>
306         /// The property to get/set the minimum value of the CircularSlider.
307         /// The default value is 0.
308         /// </summary>
309         [EditorBrowsable(EditorBrowsableState.Never)]
310         public float MinValue
311         {
312             get
313             {
314                 return (float)GetValue(MinValueProperty);
315             }
316             set
317             {
318                 SetValue(MinValueProperty, value);
319             }
320         }
321
322         /// <summary>
323         /// The property to get/set the current value of the CircularSlider.
324         /// The default value is 0.
325         /// </summary>
326         [EditorBrowsable(EditorBrowsableState.Never)]
327         public float CurrentValue
328         {
329             get
330             {
331                 return (float)GetValue(CurrentValueProperty);
332             }
333             set
334             {
335                 if (sweepAngleAnimation)
336                 {
337                     sweepAngleAnimation.Stop();
338                 }
339                 // For the first Animation effect
340                 sweepAngleAnimation = AnimateVisual(progressVisual, "sweepAngle", progressVisual.SweepAngle, 0, 100, AlphaFunction.BuiltinFunctions.EaseIn);
341
342                 SetValue(CurrentValueProperty, value);
343
344                 UpdateAnimation();
345             }
346         }
347
348         /// <summary>
349         /// The property to get/set Track object color of the CircularSlider.
350         /// </summary>
351         [EditorBrowsable(EditorBrowsableState.Never)]
352         public Color TrackColor
353         {
354             get
355             {
356                 return (Color)GetValue(TrackColorProperty);
357             }
358             set
359             {
360                 SetValue(TrackColorProperty, value);
361             }
362         }
363
364         /// <summary>
365         /// The property to get/set Progress object color of the CircularSlider.
366         /// </summary>
367         [EditorBrowsable(EditorBrowsableState.Never)]
368         public Color ProgressColor
369         {
370             get
371             {
372                 return (Color)GetValue(ProgressColorProperty);
373             }
374             set
375             {
376                 SetValue(ProgressColorProperty, value);
377             }
378         }
379
380         /// <summary>
381         /// Gets or sets the size of the thumb of Slider.
382         /// </summary>
383         [EditorBrowsable(EditorBrowsableState.Never)]
384         public Size ThumbSize
385         {
386             get
387             {
388                 return (Size)GetValue(ThumbSizeProperty);
389             }
390             set
391             {
392                 SetValue(ThumbSizeProperty, value);
393             }
394         }
395
396         /// <summary>
397         /// The property to get/set Thumb object color of the CircularSlider.
398         /// </summary>
399         [EditorBrowsable(EditorBrowsableState.Never)]
400         public Color ThumbColor
401         {
402             get
403             {
404                 return (Color)GetValue(ThumbColorProperty);
405             }
406             set
407             {
408                 SetValue(ThumbColorProperty, value);
409             }
410         }
411
412         /// <summary>
413         /// Flag to be enabled or disabled in CircularSlider.
414         /// </summary>
415         [EditorBrowsable(EditorBrowsableState.Never)]
416         public bool IsEnabled
417         {
418             get
419             {
420                 return (bool)GetValue(IsEnabledProperty);
421             }
422             set
423             {
424                 SetValue(IsEnabledProperty, value);
425             }
426         }
427         private bool privateIsEnabled
428         {
429             get
430             {
431                 return isEnabled;
432             }
433             set
434             {
435                 isEnabled = value;
436                 if (isEnabled)
437                 {
438                     UpdateTrackVisualColor(new Color(0.0f, 0.16f, 0.30f, 1.0f)); // #002A4D
439                 }
440                 else
441                 {
442                     UpdateTrackVisualColor(new Color(0.25f, 0.25f, 0.25f, 1.0f)); // #404040
443                 }
444             }
445         }
446
447         private CircularSliderStyle CurrentStyle => ViewStyle as CircularSliderStyle;
448
449         #endregion Properties
450
451
452         #region Methods
453
454         /// <summary>
455         /// Dispose Progress and all children on it.
456         /// </summary>
457         /// <param name="type">Dispose type.</param>
458         [EditorBrowsable(EditorBrowsableState.Never)]
459         protected override void Dispose(DisposeTypes type)
460         {
461             if (disposed)
462             {
463                 return;
464             }
465
466             if (type == DisposeTypes.Explicit)
467             {
468                 trackVisual = null;
469                 progressVisual = null;
470                 thumbVisual = null;
471             }
472
473             base.Dispose(type);
474         }
475
476         /// <summary>
477         /// Update progress value
478         /// </summary>
479         [EditorBrowsable(EditorBrowsableState.Never)]
480         protected virtual void UpdateValue()
481         {
482             if (null == trackVisual || null == progressVisual || null == thumbVisual)
483             {
484                 return;
485             }
486
487             if (minValue >= maxValue || currentValue < minValue || currentValue > maxValue)
488             {
489                 return;
490             }
491
492             HandleProgressVisualVisibility();
493
494             UpdateProgressVisualSweepAngle();
495         }
496
497         /// <summary>
498         /// Get Progress style.
499         /// </summary>
500         /// <returns>The default progress style.</returns>
501         [EditorBrowsable(EditorBrowsableState.Never)]
502         protected override ViewStyle CreateViewStyle()
503         {
504             return new CircularSliderStyle();
505         }
506
507         private void Initialize()
508         {
509             Size = new Size(360.0f, 360.0f);
510
511             sweepAngleAnimation?.Stop();
512             sweepAngleAnimation = null;
513
514             thumbAnimation?.Stop();
515             thumbAnimation = null;
516
517             trackVisual = new ArcVisual
518             {
519                 SuppressUpdateVisual = true,
520                 Size = new Size(this.Size.Width - sliderPadding, this.Size.Height - sliderPadding),
521                 SizePolicy = VisualTransformPolicyType.Absolute,
522                 Thickness = this.Thickness,
523                 Cap = ArcVisual.CapType.Butt,
524                 MixColor = TrackColor,
525                 StartAngle = 0.0f,
526                 SweepAngle = 360.0f
527             };
528             this.AddVisual(TrackVisualName, trackVisual);
529
530             progressVisual = new ArcVisual
531             {
532                 SuppressUpdateVisual = true,
533                 Size = new Size(this.Size.Width - sliderPadding, this.Size.Height - sliderPadding),
534                 SizePolicy = VisualTransformPolicyType.Absolute,
535                 Thickness = this.Thickness,
536                 Cap = ArcVisual.CapType.Butt,
537                 MixColor = ProgressColor,
538                 StartAngle = 0.0f,
539                 SweepAngle = 0.0f
540             };
541             this.AddVisual(ProgressVisualName, progressVisual);
542
543             thumbVisual = new ArcVisual
544             {
545                 SuppressUpdateVisual = true,
546                 Size = new Size(this.Size.Width + sliderPadding, this.Size.Height + sliderPadding),
547                 SizePolicy = VisualTransformPolicyType.Absolute,
548                 Thickness = this.ThumbSize.Width,
549                 Cap = ArcVisual.CapType.Round,
550                 MixColor = this.ThumbColor,
551                 StartAngle = 0.0f,
552                 SweepAngle = 0.0f
553             };
554             this.AddVisual(ThumbVisualName,  thumbVisual);
555
556             HandleProgressVisualVisibility();
557
558             UpdateProgressVisualSweepAngle();
559         }
560
561         private void HandleProgressVisualVisibility()
562         {
563             if (isEnabled)
564             {
565                 progressVisual.Opacity = 1.0f;
566             }
567             else if (!isEnabled)
568             {
569                 progressVisual.Opacity = 0.6f;
570             }
571         }
572
573         private void UpdateVisualThickness(float thickness)
574         {
575             if (trackVisual == null)
576             {
577                 return;
578             }
579
580             trackVisual.Thickness = thickness;
581             progressVisual.Thickness = thickness;
582
583             trackVisual.UpdateVisual(true);
584             progressVisual.UpdateVisual(true);
585         }
586
587         private void UpdateProgressVisualSweepAngle()
588         {
589             float progressRatio = (float)(currentValue - minValue) / (float)(maxValue - minValue);
590             float progressWidth = 360.0f * progressRatio; // Circle
591
592             progressVisual.SweepAngle = progressWidth;
593             thumbVisual.StartAngle = progressWidth;
594
595             if (!sweepAngleAnimation)
596             {
597                 progressVisual.UpdateVisual(true);
598                 thumbVisual.UpdateVisual(true);
599             }
600         }
601
602         private void UpdateAnimation()
603         {
604             // TODO : Currently not sure which effect is needed.
605             AlphaFunction.BuiltinFunctions builtinAlphaFunction = AlphaFunction.BuiltinFunctions.EaseOut;
606
607             if (sweepAngleAnimation)
608             {
609                 sweepAngleAnimation.Stop();
610             }
611             if (thumbAnimation)
612             {
613                 thumbAnimation.Stop();
614             }
615
616             sweepAngleAnimation = AnimateVisual(progressVisual, "sweepAngle", progressVisual.SweepAngle, 0, 500, builtinAlphaFunction);
617             thumbAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, 500, builtinAlphaFunction);
618
619             if (sweepAngleAnimation)
620             {
621                 sweepAngleAnimation.Play();
622                 thumbAnimation.Play();
623             }
624
625             ValueChanged?.Invoke(this, new CircularSliderValueChangedEventArgs() { CurrentValue = currentValue });
626         }
627
628         private void UpdateTrackVisualColor(Color trackColor)
629         {
630             if (trackVisual == null)
631             {
632                 return;
633             }
634
635             trackVisual.MixColor = trackColor;
636             trackVisual.UpdateVisual(true);
637         }
638
639         private void UpdateProgressVisualColor(Color progressColor)
640         {
641             if (progressVisual == null)
642             {
643                 return;
644             }
645
646             progressVisual.MixColor = progressColor;
647             if (!isEnabled) // Dim state
648             {
649                 progressVisual.Opacity = 0.6f;
650             }
651
652             progressVisual.UpdateVisual(true);
653         }
654
655         private void UpdateThumbVisualSize(Size thumbSize)
656         {
657             if (thumbVisual == null)
658             {
659                 return;
660             }
661
662             thumbVisual.Thickness = thumbSize.Width;
663             thumbVisual.UpdateVisual(true);
664         }
665
666         private void UpdateThumbVisualColor(Color thumbColor)
667         {
668             if (thumbVisual == null)
669             {
670                 return;
671             }
672
673             thumbVisual.MixColor = thumbColor;
674             thumbVisual.UpdateVisual(true);
675         }
676
677
678         #endregion Methods
679     }
680 }