[NUI] Fix Slider Thumb shown issue (#3187)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Progress.cs
1 /*
2  * Copyright(c) 2021 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 System.ComponentModel;
21 using System.Diagnostics;
22
23 namespace Tizen.NUI.Components
24 {
25     /// <summary>
26     /// The Progress class is used to show the ongoing status with a long narrow bar.
27     /// </summary>
28     /// <since_tizen> 6 </since_tizen>
29     public class Progress : Control
30     {
31         /// <summary>
32         /// MaxValueProperty
33         /// </summary>
34         [EditorBrowsable(EditorBrowsableState.Never)]
35         public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(float), typeof(Progress), default(float), propertyChanged: (bindable, oldValue, newValue) =>
36         {
37             var instance = (Progress)bindable;
38             if (newValue != null)
39             {
40                 instance.maxValue = (float)newValue;
41                 instance.UpdateValue();
42             }
43         },
44         defaultValueCreator: (bindable) =>
45         {
46             var instance = (Progress)bindable;
47             return instance.maxValue;
48         });
49
50         /// <summary>
51         /// MinValueProperty
52         /// </summary>
53         [EditorBrowsable(EditorBrowsableState.Never)]
54         public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(float), typeof(Progress), default(float), propertyChanged: (bindable, oldValue, newValue) =>
55         {
56             var instance = (Progress)bindable;
57             if (newValue != null)
58             {
59                 instance.minValue = (float)newValue;
60                 instance.UpdateValue();
61             }
62         },
63         defaultValueCreator: (bindable) =>
64         {
65             var instance = (Progress)bindable;
66             return instance.minValue;
67         });
68
69         /// <summary>
70         /// CurrentValueProperty
71         /// </summary>
72         [EditorBrowsable(EditorBrowsableState.Never)]
73         public static readonly BindableProperty CurrentValueProperty = BindableProperty.Create(nameof(CurrentValue), typeof(float), typeof(Progress), default(float), propertyChanged: (bindable, oldValue, newValue) =>
74         {
75             var instance = (Progress)bindable;
76             if (newValue != null)
77             {
78                 if ((float)newValue > instance.maxValue || (float)newValue < instance.minValue)
79                 {
80                     return;
81                 }
82                 instance.currentValue = (float)newValue;
83                 instance.UpdateValue();
84             }
85         },
86         defaultValueCreator: (bindable) =>
87         {
88             var instance = (Progress)bindable;
89             return instance.currentValue;
90         });
91
92         /// <summary>
93         /// BufferValueProperty
94         /// </summary>
95         [EditorBrowsable(EditorBrowsableState.Never)]
96         public static readonly BindableProperty BufferValueProperty = BindableProperty.Create(nameof(BufferValue), typeof(float), typeof(Progress), default(float), propertyChanged: (bindable, oldValue, newValue) =>
97         {
98             var instance = (Progress)bindable;
99             if (newValue != null)
100             {
101                 if ((float)newValue > instance.maxValue || (float)newValue < instance.minValue)
102                 {
103                     return;
104                 }
105                 instance.bufferValue = (float)newValue;
106                 instance.UpdateValue();
107             }
108         },
109         defaultValueCreator: (bindable) =>
110         {
111             var instance = (Progress)bindable;
112             return instance.bufferValue;
113         });
114
115         /// <summary>
116         /// ProgressStateProperty
117         /// </summary>
118         [EditorBrowsable(EditorBrowsableState.Never)]
119         public static readonly BindableProperty ProgressStateProperty = BindableProperty.Create(nameof(ProgressState), typeof(ProgressStatusType), typeof(Progress), ProgressStatusType.Indeterminate, propertyChanged: (bindable, oldValue, newValue) =>
120         {
121             var instance = (Progress)bindable;
122             if (newValue != null)
123             {
124                 instance.state = (ProgressStatusType)newValue;
125                 instance.UpdateStates();
126             }
127         },
128         defaultValueCreator: (bindable) =>
129         {
130             var instance = (Progress)bindable;
131             return instance.state;
132         });
133
134         /// This needs to be considered more if public-open is necessary.
135         private ProgressStatusType state = ProgressStatusType.Determinate;
136
137         private const float round = 0.5f;
138         private ImageView trackImage = null;
139         private ImageView progressImage = null;
140         private ImageView bufferImage = null;
141         private ImageVisual indeterminateImage = null;
142         private float maxValue = 100;
143         private float minValue = 0;
144         private float currentValue = 0;
145         private float bufferValue = 0;
146         private Animation indeterminateAnimation = null;
147
148         static Progress() { }
149         /// <summary>
150         /// The constructor of Progress
151         /// </summary>
152         /// <since_tizen> 6 </since_tizen>
153         public Progress() : base()
154         {
155             Initialize();
156         }
157
158         /// <summary>
159         /// The constructor of the Progress class with specific style.
160         /// </summary>
161         /// <param name="style">style name</param>
162         /// <since_tizen> 8 </since_tizen>
163         public Progress(string style) : base(style)
164         {
165             Initialize();
166         }
167
168         /// <summary>
169         /// The constructor of the Progress class with specific style.
170         /// </summary>
171         /// <param name="progressStyle">The style object to initialize the Progress.</param>
172         /// <since_tizen> 8 </since_tizen>
173         public Progress(ProgressStyle progressStyle) : base(progressStyle)
174         {
175             Initialize();
176         }
177
178         /// <summary>
179         /// Prevents from showing child widgets in AT-SPI tree.
180         /// </summary>
181         [EditorBrowsable(EditorBrowsableState.Never)]
182         protected override bool AccessibilityShouldReportZeroChildren()
183         {
184             return true;
185         }
186
187         /// <summary>
188         /// Minimum value.
189         /// </summary>
190         [EditorBrowsable(EditorBrowsableState.Never)]
191         protected override double AccessibilityGetMinimum()
192         {
193             if (this.ProgressState == Components.Progress.ProgressStatusType.Determinate)
194                 return (double)MinValue;
195             else return 0.0;
196         }
197
198         /// <summary>
199         /// Current value.
200         /// </summary>
201         [EditorBrowsable(EditorBrowsableState.Never)]
202         protected override double AccessibilityGetCurrent()
203         {
204             if (this.ProgressState == Components.Progress.ProgressStatusType.Determinate)
205                 return (double)CurrentValue;
206             else return 0.0;
207         }
208
209         /// <summary>
210         /// Maximum value.
211         /// </summary>
212         [EditorBrowsable(EditorBrowsableState.Never)]
213         protected override double AccessibilityGetMaximum()
214         {
215             if (this.ProgressState == Components.Progress.ProgressStatusType.Determinate)
216                 return (double)MaxValue;
217             else return 0.0;
218         }
219
220         /// <summary>
221         /// The status type of the Progress.
222         /// </summary>
223         /// <since_tizen> 6 </since_tizen>
224         public enum ProgressStatusType
225         {
226             /// <summary>
227             /// Show BufferImage
228             /// </summary>
229             /// <since_tizen> 6 </since_tizen>
230             Buffering,
231
232             /// <summary>
233             /// Show ProgressImage and BufferImage
234             /// </summary>
235             /// <since_tizen> 6 </since_tizen>
236             Determinate,
237
238             /// <summary>
239             /// Show TrackImage
240             /// </summary>
241             /// <since_tizen> 6 </since_tizen>
242             Indeterminate
243         }
244
245         /// <summary>
246         /// Return currently applied style.
247         /// </summary>
248         /// <remarks>
249         /// Modifying contents in style may cause unexpected behaviour.
250         /// </remarks>
251         /// <since_tizen> 8 </since_tizen>
252         public ProgressStyle Style => (ProgressStyle)(ViewStyle as ProgressStyle)?.Clone();
253
254         /// <summary>
255         /// The property to get/set Track image object URL of the Progress.
256         /// </summary>
257         /// <since_tizen> 6 </since_tizen>
258         public string TrackImageURL
259         {
260             get => trackImage.ResourceUrl;
261             set => trackImage.ResourceUrl = value;
262         }
263
264         /// <summary>
265         /// The property to get/set Progress object image URL of the Progress.
266         /// </summary>
267         /// <since_tizen> 6 </since_tizen>
268         public string ProgressImageURL
269         {
270             get => progressImage.ResourceUrl;
271             set => progressImage.ResourceUrl = value;
272         }
273
274         /// <summary>
275         /// The property to get/set Buffer object image resource URL of the Progress.
276         /// </summary>
277         /// <since_tizen> 6 </since_tizen>
278         public string BufferImageURL
279         {
280             get => bufferImage.ResourceUrl;
281             set => bufferImage.ResourceUrl = value;
282         }
283
284         /// <summary>
285         /// The property to get/set the indeterminate image.
286         /// </summary>
287         /// <exception cref="NullReferenceException">Thrown when setting null value.</exception>
288         /// <since_tizen> 9 </since_tizen>
289         public string IndeterminateImageUrl
290         {
291             get
292             {
293                 if (indeterminateImage == null)
294                 {
295                     return null;
296                 }
297                 else
298                 {
299                     return indeterminateImage?.URL;
300                 }
301             }
302             set
303             {
304                 if (value == null || indeterminateImage == null)
305                 {
306                     throw new NullReferenceException("Progress.IndeterminateImage is null");
307                 }
308                 else
309                 {
310                     indeterminateImage.URL = value;
311                 }
312             }
313         }
314
315         /// <summary>
316         /// The property to get/set Track object color of the Progress.
317         /// </summary>
318         /// <since_tizen> 6 </since_tizen>
319         public Color TrackColor
320         {
321             get => trackImage.BackgroundColor;
322             set => trackImage.BackgroundColor = value;
323         }
324
325         /// <summary>
326         /// The property to get/set Progress object color of the Progress.
327         /// </summary>
328         /// <since_tizen> 6 </since_tizen>
329         public Color ProgressColor
330         {
331             get => progressImage.BackgroundColor;
332             set => progressImage.BackgroundColor = value;
333         }
334
335         /// <summary>
336         /// The property to get/set Buffer object color of the Progress.
337         /// </summary>
338         /// <since_tizen> 6 </since_tizen>
339         public Color BufferColor
340         {
341             get => bufferImage.BackgroundColor;
342             set => bufferImage.BackgroundColor = value;
343         }
344
345         /// <summary>
346         /// The property to get/set the maximum value of the Progress.
347         /// </summary>
348         /// <since_tizen> 6 </since_tizen>
349         public float MaxValue
350         {
351             get
352             {
353                 return (float)GetValue(MaxValueProperty);
354             }
355             set
356             {
357                 SetValue(MaxValueProperty, value);
358             }
359         }
360
361         /// <summary>
362         /// The property to get/set the minim value of the Progress.
363         /// </summary>
364         /// <since_tizen> 6 </since_tizen>
365         public float MinValue
366         {
367             get
368             {
369                 return (float)GetValue(MinValueProperty);
370             }
371             set
372             {
373                 SetValue(MinValueProperty, value);
374             }
375         }
376
377         /// <summary>
378         /// The property to get/set the current value of the Progress.
379         /// </summary>
380         /// <since_tizen> 6 </since_tizen>
381         public float CurrentValue
382         {
383             get
384             {
385                 return (float)GetValue(CurrentValueProperty);
386             }
387             set
388             {
389                 SetValue(CurrentValueProperty, value);
390                 if (IsHighlighted)
391                 {
392                     EmitAccessibilityEvent(AccessibilityPropertyChangeEvent.Value);
393                 }
394             }
395         }
396
397         /// <summary>
398         /// The property to get/set the buffer value of the Progress.
399         /// </summary>
400         /// <since_tizen> 6 </since_tizen>
401         public float BufferValue
402         {
403             get
404             {
405                 return (float)GetValue(BufferValueProperty);
406             }
407             set
408             {
409                 SetValue(BufferValueProperty, value);
410             }
411         }
412
413         /// <summary>
414         /// Gets or sets state of progress.
415         /// </summary>
416         /// <since_tizen> 6 </since_tizen>
417         public ProgressStatusType ProgressState
418         {
419             get
420             {
421                 return (ProgressStatusType)GetValue(ProgressStateProperty);
422             }
423             set
424             {
425                 SetValue(ProgressStateProperty, value);
426             }
427         }
428
429         /// <inheritdoc/>
430         [EditorBrowsable(EditorBrowsableState.Never)]
431         public override void OnInitialize()
432         {
433             base.OnInitialize();
434             SetAccessibilityConstructor(Role.ProgressBar, AccessibilityInterface.Value);
435             // create necessary components
436             InitializeTrack();
437             InitializeBuffer();
438             InitializeProgress();
439             InitializeIndeterminate();
440
441             indeterminateAnimation?.Stop();
442             indeterminateAnimation = null;
443         }
444
445         /// <inheritdoc/>
446         [EditorBrowsable(EditorBrowsableState.Never)]
447         public override void ApplyStyle(ViewStyle style)
448         {
449             base.ApplyStyle(style);
450
451             if (style is ProgressStyle progressStyle)
452             {
453                 Debug.Assert(trackImage != null);
454                 Debug.Assert(progressImage != null);
455                 Debug.Assert(bufferImage != null);
456
457                 trackImage.ApplyStyle(progressStyle.Track);
458                 progressImage.ApplyStyle(progressStyle.Progress);
459                 bufferImage.ApplyStyle(progressStyle.Buffer);
460
461                 if (null != indeterminateImage && null != progressStyle.IndeterminateImageUrl)
462                 {
463                     indeterminateImage.URL = progressStyle.IndeterminateImageUrl;
464                 }
465             }
466         }
467
468         /// <summary>
469         /// Dispose Progress and all children on it.
470         /// </summary>
471         /// <param name="type">Dispose type.</param>
472         /// <since_tizen> 6 </since_tizen>
473         protected override void Dispose(DisposeTypes type)
474         {
475             if (disposed)
476             {
477                 return;
478             }
479
480             if (type == DisposeTypes.Explicit)
481             {
482                 //Called by User
483                 //Release your own managed resources here.
484                 //You should release all of your own disposable objects here.
485                 Utility.Dispose(trackImage);
486                 Utility.Dispose(progressImage);
487                 Utility.Dispose(bufferImage);
488                 indeterminateImage = null;
489             }
490
491             //You must call base.Dispose(type) just before exit.
492             base.Dispose(type);
493         }
494
495         /// <summary>
496         /// Change Image status. It can be override.
497         /// </summary>
498         /// This needs to be considered more if public-open is necessary.
499         [EditorBrowsable(EditorBrowsableState.Never)]
500         private void UpdateStates()
501         {
502             ChangeImageState(state);
503         }
504
505         /// <summary>
506         /// Update progress value
507         /// </summary>
508         /// This needs to be considered more if public-open is necessary.
509         [EditorBrowsable(EditorBrowsableState.Never)]
510         private void UpdateValue()
511         {
512             if (null == trackImage || null == progressImage)
513             {
514                 return;
515             }
516
517             if (minValue >= maxValue || currentValue < minValue || currentValue > maxValue)
518             {
519                 return;
520             }
521
522             float width = this.SizeWidth;
523             float height = this.SizeHeight;
524             float progressRatio = (float)(currentValue - minValue) / (float)(maxValue - minValue);
525             float progressWidth = width * progressRatio;
526             progressImage.Size2D = new Size2D((int)(progressWidth + round), (int)height); //Add const round to reach Math.Round function.
527             if (null != bufferImage)
528             {
529                 if (bufferValue < minValue || bufferValue > maxValue)
530                 {
531                     return;
532                 }
533
534                 float bufferRatio = (float)(bufferValue - minValue) / (float)(maxValue - minValue);
535                 float bufferWidth = width * bufferRatio;
536                 bufferImage.Size2D = new Size2D((int)(bufferWidth + round), (int)height); //Add const round to reach Math.Round function.
537             }
538         }
539
540         private Vector4 destinationValue = new Vector4(-1.0f, 0.0f, 10.0f, 1.0f);
541         private Vector4 initialValue = new Vector4(0.0f, 0.0f, 10.0f, 1.0f);
542
543         /// <summary>
544         /// Update Animation for Indeterminate mode.
545         /// </summary>
546         /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API.
547         [EditorBrowsable(EditorBrowsableState.Never)]
548         private void UpdateIndeterminateAnimation()
549         {
550             indeterminateAnimation?.Stop();
551
552             if (null != indeterminateImage)
553             {
554                 indeterminateAnimation = AnimateVisual(indeterminateImage, "pixelArea", destinationValue, 0, 1000,  AlphaFunction.BuiltinFunctions.Default, initialValue);
555                 indeterminateAnimation.Looping = true;
556                 indeterminateAnimation.Play();
557             }
558         }
559
560         /// <summary>
561         /// Get Progress style.
562         /// </summary>
563         /// <returns>The default progress style.</returns>
564         /// <since_tizen> 8 </since_tizen>
565         protected override ViewStyle CreateViewStyle()
566         {
567             return new ProgressStyle();
568         }
569
570         /// <summary>
571         /// Change Image status
572         /// </summary>
573         /// <since_tizen> 6 </since_tizen>
574         /// <param name="statusType">New status type</param>
575         protected void ChangeImageState(ProgressStatusType statusType)
576         {
577             if (statusType == ProgressStatusType.Buffering)
578             {
579                 indeterminateAnimation?.Stop();
580                 indeterminateAnimation = null;
581
582                 if (null != indeterminateImage)
583                 {
584                     indeterminateImage.Opacity = 0.0f;
585                 }
586                 progressImage.Hide();
587                 bufferImage.Show();
588             }
589             else if (statusType == ProgressStatusType.Determinate)
590             {
591                 indeterminateAnimation?.Stop();
592                 indeterminateAnimation = null;
593
594                 if (null != indeterminateImage)
595                 {
596                     indeterminateImage.Opacity = 0.0f;
597                 }
598                 bufferImage.Show();
599                 progressImage.Show();
600
601                 UpdateValue();
602             }
603             else if (statusType == ProgressStatusType.Indeterminate)
604             {
605                 bufferImage.Hide();
606                 progressImage.Hide();
607                 if (null != indeterminateImage)
608                 {
609                     indeterminateImage.Opacity = 1.0f;
610                 }
611
612                 UpdateIndeterminateAnimation();
613             }
614         }
615
616         private void Initialize()
617         {
618             AccessibilityHighlightable = true;
619         }
620
621         private void InitializeTrack()
622         {
623             if (null == trackImage)
624             {
625                 trackImage = new ImageView
626                 {
627                     WidthResizePolicy = ResizePolicyType.FillToParent,
628                     HeightResizePolicy = ResizePolicyType.FillToParent,
629                     PositionUsesPivotPoint = true,
630                     ParentOrigin = NUI.ParentOrigin.TopLeft,
631                     PivotPoint = NUI.PivotPoint.TopLeft
632                 };
633                 Add(trackImage);
634             }
635         }
636
637         private void InitializeProgress()
638         {
639             if (null == progressImage)
640             {
641                 progressImage = new ImageView
642                 {
643                     WidthResizePolicy = ResizePolicyType.FillToParent,
644                     HeightResizePolicy = ResizePolicyType.FillToParent,
645                     PositionUsesPivotPoint = true,
646                     ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
647                     PivotPoint = Tizen.NUI.PivotPoint.TopLeft
648                 };
649                 Add(progressImage);
650             }
651         }
652
653         private void InitializeBuffer()
654         {
655             if (null == bufferImage)
656             {
657                 bufferImage = new ImageView
658                 {
659                     WidthResizePolicy = ResizePolicyType.FillToParent,
660                     HeightResizePolicy = ResizePolicyType.FillToParent,
661                     PositionUsesPivotPoint = true,
662                     ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
663                     PivotPoint = Tizen.NUI.PivotPoint.TopLeft
664                 };
665                 Add(bufferImage);
666             }
667         }
668
669         private void InitializeIndeterminate()
670         {
671             indeterminateImage = new ImageVisual
672             {
673                 PixelArea = new Vector4(0.0f, 0.0f, 10.0f, 1.0f),
674                 WrapModeU = WrapModeType.Repeat,
675                 SizePolicy = VisualTransformPolicyType.Relative,
676                 Origin = Visual.AlignType.Center,
677                 AnchorPoint = Visual.AlignType.Center,
678                 Opacity = 0.0f,
679                 Size = new Size2D(1, 1),
680                 URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "nui_component_default_progress_indeterminate.png"
681             };
682             AddVisual("Indeterminate", indeterminateImage);
683
684             if (state == ProgressStatusType.Indeterminate)
685             {
686                 indeterminateImage.Opacity = 1.0f;
687             }
688         }
689     }
690 }