[NUI] Fix wearable style issues (#1847)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Wearable / src / public / CircularProgress.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     /// The CircularProgress class of Wearable is used to show the ongoing status with a circular bar.
27     /// CircularProgress can be counted in its percentage.
28     /// </summary>
29     [EditorBrowsable(EditorBrowsableState.Never)]
30     public class CircularProgress : Control
31     {
32         #region Fields
33
34         /// <summary>Bindable property of Thickness</summary>
35         [EditorBrowsable(EditorBrowsableState.Never)]
36         public static readonly BindableProperty ThicknessProperty = BindableProperty.Create(nameof(Thickness), typeof(float), typeof(CircularProgress), default(float), propertyChanged: (bindable, oldValue, newValue) =>
37         {
38             var instance = ((CircularProgress)bindable);
39             instance.CurrentStyle.Thickness = (float)newValue;
40             instance.UpdateVisualThickness((float)newValue);
41         },
42         defaultValueCreator: (bindable) =>
43         {
44             return ((CircularProgress)bindable).CurrentStyle.Thickness;
45         });
46
47         /// <summary>Bindable property of MaxValue</summary>
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(float), typeof(CircularProgress), default(float), propertyChanged: (bindable, oldValue, newValue) =>
50         {
51             var instance = (CircularProgress)bindable;
52             if (newValue != null)
53             {
54                 instance.maxValue = (float)newValue;
55                 instance.UpdateValue();
56             }
57         },
58         defaultValueCreator: (bindable) =>
59         {
60             var instance = (CircularProgress)bindable;
61             return instance.maxValue;
62         });
63
64         /// <summary>Bindable property of MinValue</summary>
65         [EditorBrowsable(EditorBrowsableState.Never)]
66         public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(float), typeof(CircularProgress), default(float), propertyChanged: (bindable, oldValue, newValue) =>
67         {
68             var instance = (CircularProgress)bindable;
69             if (newValue != null)
70             {
71                 instance.minValue = (float)newValue;
72                 instance.UpdateValue();
73             }
74         },
75         defaultValueCreator: (bindable) =>
76         {
77             var instance = (CircularProgress)bindable;
78             return instance.minValue;
79         });
80
81         /// <summary>Bindable property of CurrentValue</summary>
82         [EditorBrowsable(EditorBrowsableState.Never)]
83         public static readonly BindableProperty CurrentValueProperty = BindableProperty.Create(nameof(CurrentValue), typeof(float), typeof(CircularProgress), default(float), propertyChanged: (bindable, oldValue, newValue) =>
84         {
85             var instance = (CircularProgress)bindable;
86             if (newValue != null)
87             {
88                 if ((float)newValue > instance.maxValue || (float)newValue < instance.minValue)
89                 {
90                     return;
91                 }
92                 instance.currentValue = (float)newValue;
93                 instance.UpdateValue();
94             }
95         },
96         defaultValueCreator: (bindable) =>
97         {
98             var instance = (CircularProgress)bindable;
99             return instance.currentValue;
100         });
101
102         /// <summary>Bindable property of TrackColor</summary>
103         [EditorBrowsable(EditorBrowsableState.Never)]
104         public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(CircularProgress), null, propertyChanged: (bindable, oldValue, newValue) =>
105         {
106             var instance = (CircularProgress)bindable;
107             instance.CurrentStyle.TrackColor = (Color)newValue;
108             instance.UpdateTrackVisualColor((Color)newValue);
109         },
110         defaultValueCreator: (bindable) =>
111         {
112             return ((CircularProgress)bindable).CurrentStyle.TrackColor;
113         });
114
115         /// <summary>Bindable property of ProgressColor</summary>
116         [EditorBrowsable(EditorBrowsableState.Never)]
117         public static readonly BindableProperty ProgressColorProperty = BindableProperty.Create(nameof(ProgressColor), typeof(Color), typeof(CircularProgress), null, propertyChanged: (bindable, oldValue, newValue) =>
118         {
119             var instance = (CircularProgress)bindable;
120             instance.CurrentStyle.ProgressColor = (Color)newValue;
121             instance.UpdateProgressVisualColor((Color)newValue);
122         },
123         defaultValueCreator: (bindable) =>
124         {
125             return ((CircularProgress)bindable).CurrentStyle.ProgressColor;
126         });
127
128         /// <summary>Bindable property of IsEnabled</summary>
129         [EditorBrowsable(EditorBrowsableState.Never)]
130         public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(CircularProgress), true, propertyChanged: (bindable, oldValue, newValue) =>
131         {
132             var instance = (CircularProgress)bindable;
133             if (newValue != null)
134             {
135                 instance.privateIsEnabled = (bool)newValue;
136             }
137         },
138         defaultValueCreator: (bindable) =>
139         {
140             var instance = (CircularProgress)bindable;
141             return instance.privateIsEnabled;
142         });
143
144         private const string TrackVisualName = "Track";
145         private const string ProgressVisualName = "Progress";
146         private ArcVisual trackVisual;
147         private ArcVisual progressVisual;
148
149         private float maxValue = 100;
150         private float minValue = 0;
151         private float currentValue = 0;
152         private bool isEnabled = true;
153
154         private Animation sweepAngleAnimation;
155
156         #endregion Fields
157
158
159         #region Constructors
160
161         static CircularProgress() { }
162         /// <summary>
163         /// The constructor of CircularProgress.
164         /// Basically, CircularProgress is for full screen. (360 x 360)
165         /// But, it also can be displayed on the button or the list for small size.
166         /// User can set its size.
167         /// </summary>
168         [EditorBrowsable(EditorBrowsableState.Never)]
169         public CircularProgress() : base(new CircularProgressStyle())
170         {
171             Initialize();
172         }
173
174         /// <summary>
175         /// The constructor of the CircularProgress class with specific style.
176         /// </summary>
177         /// <param name="style">style name</param>
178         [EditorBrowsable(EditorBrowsableState.Never)]
179         public CircularProgress(string style) : base(style)
180         {
181             Initialize();
182         }
183
184         /// <summary>
185         /// The constructor of the CircularProgress class with specific style.
186         /// </summary>
187         /// <param name="progressStyle">The style object to initialize the CircularProgress.</param>
188         [EditorBrowsable(EditorBrowsableState.Never)]
189         public CircularProgress(CircularProgressStyle progressStyle) : base(progressStyle)
190         {
191             Initialize();
192         }
193
194         #endregion Constructors
195
196
197         #region Properties
198
199         /// <summary>
200         /// Return a copied Style instance of CircularProgress
201         /// </summary>
202         /// <remarks>
203         /// It returns copied Style instance and changing it does not effect to the CircularProgress.
204         /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
205         /// </remarks>
206         [EditorBrowsable(EditorBrowsableState.Never)]
207         public new CircularProgressStyle Style
208         {
209             get
210             {
211                 var result = new CircularProgressStyle(ViewStyle as CircularProgressStyle);
212                 result.CopyPropertiesFromView(this);
213                 return result;
214             }
215         }
216
217         /// <summary>
218         /// The thickness of the track and progress.
219         /// </summary>
220         [EditorBrowsable(EditorBrowsableState.Never)]
221         public float Thickness
222         {
223             get
224             {
225                 return (float)GetValue(ThicknessProperty);
226             }
227             set
228             {
229                 SetValue(ThicknessProperty, value);
230             }
231         }
232
233         /// <summary>
234         /// The property to get/set the maximum value of the CircularProgress.
235         /// The default value is 100.
236         /// </summary>
237         [EditorBrowsable(EditorBrowsableState.Never)]
238         public float MaxValue
239         {
240             get
241             {
242                 return (float)GetValue(MaxValueProperty);
243             }
244             set
245             {
246                 SetValue(MaxValueProperty, value);
247             }
248         }
249
250         /// <summary>
251         /// The property to get/set the minimum value of the CircularProgress.
252         /// The default value is 0.
253         /// </summary>
254         [EditorBrowsable(EditorBrowsableState.Never)]
255         public float MinValue
256         {
257             get
258             {
259                 return (float)GetValue(MinValueProperty);
260             }
261             set
262             {
263                 SetValue(MinValueProperty, value);
264             }
265         }
266
267         /// <summary>
268         /// The property to get/set the current value of the CircularProgress.
269         /// The default value is 0.
270         /// </summary>
271         [EditorBrowsable(EditorBrowsableState.Never)]
272         public float CurrentValue
273         {
274             get
275             {
276                 return (float)GetValue(CurrentValueProperty);
277             }
278             set
279             {
280                 if (sweepAngleAnimation)
281                 {
282                     sweepAngleAnimation.Stop();
283                 }
284                 sweepAngleAnimation = AnimateVisual(progressVisual, "sweepAngle", progressVisual.SweepAngle, 0, 100, AlphaFunction.BuiltinFunctions.EaseIn);
285
286                 SetValue(CurrentValueProperty, value);
287
288                 UpdateAnimation();
289             }
290         }
291
292         /// <summary>
293         /// The property to get/set Track object color of the CircularProgress.
294         /// </summary>
295         [EditorBrowsable(EditorBrowsableState.Never)]
296         public Color TrackColor
297         {
298             get
299             {
300                 return (Color)GetValue(TrackColorProperty);
301             }
302             set
303             {
304                 SetValue(TrackColorProperty, value);
305             }
306         }
307
308         /// <summary>
309         /// The property to get/set Progress object color of the CircularProgress.
310         /// </summary>
311         [EditorBrowsable(EditorBrowsableState.Never)]
312         public Color ProgressColor
313         {
314             get
315             {
316                 return (Color)GetValue(ProgressColorProperty);
317             }
318             set
319             {
320                 SetValue(ProgressColorProperty, value);
321             }
322         }
323
324         /// <summary>
325         /// Flag to be enabled or disabled in CircularProgress.
326         /// </summary>
327         [EditorBrowsable(EditorBrowsableState.Never)]
328         public bool IsEnabled
329         {
330             get
331             {
332                 return (bool)GetValue(IsEnabledProperty);
333             }
334             set
335             {
336                 SetValue(IsEnabledProperty, value);
337             }
338         }
339         private bool privateIsEnabled
340         {
341             get
342             {
343                 return isEnabled;
344             }
345             set
346             {
347                 isEnabled = value;
348                 if (isEnabled)
349                 {
350                     ControlState = ControlState.Normal;
351
352                     UpdateTrackVisualColor(new Color(0.0f, 0.16f, 0.30f, 1.0f)); // #002A4D
353                 }
354                 else
355                 {
356                     ControlState = ControlState.Disabled;
357
358                     UpdateTrackVisualColor(new Color(0.25f, 0.25f, 0.25f, 1.0f)); // #404040
359                 }
360             }
361         }
362
363         private CircularProgressStyle CurrentStyle => ViewStyle as CircularProgressStyle;
364
365         #endregion Properties
366
367
368         #region Methods
369
370         /// <summary>
371         /// Dispose Progress and all children on it.
372         /// </summary>
373         /// <param name="type">Dispose type.</param>
374         [EditorBrowsable(EditorBrowsableState.Never)]
375         protected override void Dispose(DisposeTypes type)
376         {
377             if (disposed)
378             {
379                 return;
380             }
381
382             if (type == DisposeTypes.Explicit)
383             {
384                 trackVisual = null;
385                 progressVisual = null;
386             }
387
388             base.Dispose(type);
389         }
390
391         /// <summary>
392         /// Update progress value
393         /// </summary>
394         [EditorBrowsable(EditorBrowsableState.Never)]
395         protected virtual void UpdateValue()
396         {
397             if (null == trackVisual || null == progressVisual)
398             {
399                 return;
400             }
401
402             if (minValue >= maxValue || currentValue < minValue || currentValue > maxValue)
403             {
404                 return;
405             }
406
407             HandleProgressVisualVisibility();
408
409             UpdateProgressVisualSweepAngle();
410         }
411
412         /// <summary>
413         /// Get Progress style.
414         /// </summary>
415         /// <returns>The default progress style.</returns>
416         [EditorBrowsable(EditorBrowsableState.Never)]
417         protected override ViewStyle CreateViewStyle()
418         {
419             return new CircularProgressStyle();
420         }
421
422         /// <inheritdoc/>
423         [EditorBrowsable(EditorBrowsableState.Never)]
424         protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
425         {
426             base.OnControlStateChanged(controlStateChangedInfo);
427
428             var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
429
430             if (isEnabled != stateEnabled)
431             {
432                 isEnabled = stateEnabled;
433             }
434         }
435
436
437         private void Initialize()
438         {
439             Size = new Size(360.0f, 360.0f);
440
441             sweepAngleAnimation?.Stop();
442             sweepAngleAnimation = null;
443
444             trackVisual = new ArcVisual
445             {
446                 SuppressUpdateVisual = true,
447                 Thickness = this.Thickness,
448                 Cap = ArcVisual.CapType.Butt,
449                 MixColor = TrackColor,
450                 StartAngle = 0.0f,
451                 SweepAngle = 360.0f
452             };
453             this.AddVisual(TrackVisualName, trackVisual);
454
455             progressVisual = new ArcVisual
456             {
457                 SuppressUpdateVisual = true,
458                 Thickness = this.Thickness,
459                 Cap = ArcVisual.CapType.Butt,
460                 MixColor = ProgressColor,
461                 StartAngle = 0.0f,
462                 SweepAngle = 0.0f
463             };
464             this.AddVisual(ProgressVisualName, progressVisual);
465
466             HandleProgressVisualVisibility();
467
468             UpdateProgressVisualSweepAngle();
469         }
470
471         private void HandleProgressVisualVisibility()
472         {
473             if (isEnabled)
474             {
475                 progressVisual.Opacity = 1.0f;
476             }
477             else if (!isEnabled)
478             {
479                 progressVisual.Opacity = 0.6f;
480             }
481         }
482
483         private void UpdateVisualThickness(float thickness)
484         {
485             if (trackVisual == null)
486             {
487                 return;
488             }
489
490             trackVisual.Thickness = thickness;
491             progressVisual.Thickness = thickness;
492
493             trackVisual.UpdateVisual(true);
494             progressVisual.UpdateVisual(true);
495         }
496
497         private void UpdateProgressVisualSweepAngle()
498         {
499             float progressRatio = (float)(currentValue - minValue) / (float)(maxValue - minValue);
500             float progressWidth = 360.0f * progressRatio; // Circle
501             progressVisual.SweepAngle = progressWidth;
502
503             if (!sweepAngleAnimation)
504             {
505                 progressVisual.UpdateVisual(true);
506             }
507         }
508
509         private void UpdateAnimation()
510         {
511             // TODO : Currently not sure which effect is needed.
512             AlphaFunction.BuiltinFunctions builtinAlphaFunction = AlphaFunction.BuiltinFunctions.EaseIn;
513
514             if (sweepAngleAnimation)
515             {
516                 sweepAngleAnimation.Stop();
517             }
518
519             sweepAngleAnimation = AnimateVisual(progressVisual, "sweepAngle", progressVisual.SweepAngle, 0, 100, builtinAlphaFunction);
520
521             if (sweepAngleAnimation)
522             {
523                 sweepAngleAnimation.Play();
524             }
525
526         }
527
528         private void UpdateTrackVisualColor(Color trackColor)
529         {
530             if (trackVisual == null)
531             {
532                 return;
533             }
534
535             trackVisual.MixColor = trackColor;
536             trackVisual.UpdateVisual(true);
537         }
538
539         private void UpdateProgressVisualColor(Color progressColor)
540         {
541             if (progressVisual == null)
542             {
543                 return;
544             }
545
546             progressVisual.MixColor = progressColor;
547             if (!isEnabled) // Dim state
548             {
549                 progressVisual.Opacity = 0.6f;
550             }
551
552             progressVisual.UpdateVisual(true);
553         }
554
555         #endregion Methods
556     }
557 }