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