[NUI][ATSPI] Add a function to check whether accessibility is enabled (#3463)
[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         /// <summary>
135         /// IsEnabledProperty
136         /// </summary>
137         [EditorBrowsable(EditorBrowsableState.Never)]
138         public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(Progress), true, propertyChanged: (bindable, oldValue, newValue) =>
139         {
140             var instance = (Progress)bindable;
141             if (newValue != null)
142             {
143                 bool newEnabled = (bool)newValue;
144                 if (instance.isEnabled != newEnabled)
145                 {
146                     instance.isEnabled = newEnabled;
147                     instance.Sensitive = newEnabled;
148                     instance.UpdateStates();
149                 }
150             }
151         },
152         defaultValueCreator: (bindable) => ((Progress)bindable).isEnabled);
153
154         /// This needs to be considered more if public-open is necessary.
155         private ProgressStatusType state = ProgressStatusType.Determinate;
156
157         private Vector2 size = null;
158         private const float round = 0.5f;
159         private ImageView trackImage = null;
160         private ImageView progressImage = null;
161         private ImageView bufferImage = null;
162         private ImageVisual indeterminateImage = null;
163         private float maxValue = 100;
164         private float minValue = 0;
165         private float currentValue = 0;
166         private float bufferValue = 0;
167         private Animation indeterminateAnimation = null;
168         bool isEnabled = true;
169
170         static Progress() { }
171         /// <summary>
172         /// The constructor of Progress
173         /// </summary>
174         /// <since_tizen> 6 </since_tizen>
175         public Progress() : base()
176         {
177             Initialize();
178         }
179
180         /// <summary>
181         /// The constructor of the Progress class with specific style.
182         /// </summary>
183         /// <param name="style">style name</param>
184         /// <since_tizen> 8 </since_tizen>
185         public Progress(string style) : base(style)
186         {
187             Initialize();
188         }
189
190         /// <summary>
191         /// The constructor of the Progress class with specific style.
192         /// </summary>
193         /// <param name="progressStyle">The style object to initialize the Progress.</param>
194         /// <since_tizen> 8 </since_tizen>
195         public Progress(ProgressStyle progressStyle) : base(progressStyle)
196         {
197             Initialize();
198         }
199
200         /// <summary>
201         /// The status type of the Progress.
202         /// </summary>
203         /// <since_tizen> 6 </since_tizen>
204         public enum ProgressStatusType
205         {
206             /// <summary>
207             /// Show BufferImage
208             /// </summary>
209             /// <since_tizen> 6 </since_tizen>
210             Buffering,
211
212             /// <summary>
213             /// Show ProgressImage and BufferImage
214             /// </summary>
215             /// <since_tizen> 6 </since_tizen>
216             Determinate,
217
218             /// <summary>
219             /// Show TrackImage
220             /// </summary>
221             /// <since_tizen> 6 </since_tizen>
222             Indeterminate
223         }
224
225         /// <summary>
226         /// Return currently applied style.
227         /// </summary>
228         /// <remarks>
229         /// Modifying contents in style may cause unexpected behaviour.
230         /// </remarks>
231         /// <since_tizen> 8 </since_tizen>
232         public ProgressStyle Style => (ProgressStyle)(ViewStyle as ProgressStyle)?.Clone();
233
234         /// <summary>
235         /// The property to get/set Track image object URL of the Progress.
236         /// </summary>
237         /// <since_tizen> 6 </since_tizen>
238         public string TrackImageURL
239         {
240             get => trackImage.ResourceUrl;
241             set => trackImage.ResourceUrl = value;
242         }
243
244         /// <summary>
245         /// The property to get/set Progress object image URL of the Progress.
246         /// </summary>
247         /// <since_tizen> 6 </since_tizen>
248         public string ProgressImageURL
249         {
250             get => progressImage.ResourceUrl;
251             set => progressImage.ResourceUrl = value;
252         }
253
254         /// <summary>
255         /// The property to get/set Buffer object image resource URL of the Progress.
256         /// </summary>
257         /// <since_tizen> 6 </since_tizen>
258         public string BufferImageURL
259         {
260             get => bufferImage.ResourceUrl;
261             set => bufferImage.ResourceUrl = value;
262         }
263
264         /// <summary>
265         /// The property to get/set the indeterminate image.
266         /// </summary>
267         /// <exception cref="NullReferenceException">Thrown when setting null value.</exception>
268         /// <since_tizen> 9 </since_tizen>
269         public string IndeterminateImageUrl
270         {
271             get
272             {
273                 if (indeterminateImage == null)
274                 {
275                     return null;
276                 }
277                 else
278                 {
279                     return indeterminateImage?.URL;
280                 }
281             }
282             set
283             {
284                 if (value == null || indeterminateImage == null)
285                 {
286                     throw new NullReferenceException("Progress.IndeterminateImage is null");
287                 }
288                 else
289                 {
290                     indeterminateImage.URL = value;
291                 }
292             }
293         }
294
295         /// <summary>
296         /// The property to get/set Track object color of the Progress.
297         /// </summary>
298         /// <since_tizen> 6 </since_tizen>
299         public Color TrackColor
300         {
301             get => trackImage.BackgroundColor;
302             set => trackImage.BackgroundColor = value;
303         }
304
305         /// <summary>
306         /// The property to get/set Progress object color of the Progress.
307         /// </summary>
308         /// <since_tizen> 6 </since_tizen>
309         public Color ProgressColor
310         {
311             get => progressImage.BackgroundColor;
312             set => progressImage.BackgroundColor = value;
313         }
314
315         /// <summary>
316         /// The property to get/set Buffer object color of the Progress.
317         /// </summary>
318         /// <since_tizen> 6 </since_tizen>
319         public Color BufferColor
320         {
321             get => bufferImage.BackgroundColor;
322             set => bufferImage.BackgroundColor = value;
323         }
324
325         /// <summary>
326         /// The property to get/set the maximum value of the Progress.
327         /// </summary>
328         /// <since_tizen> 6 </since_tizen>
329         public float MaxValue
330         {
331             get
332             {
333                 return (float)GetValue(MaxValueProperty);
334             }
335             set
336             {
337                 SetValue(MaxValueProperty, value);
338             }
339         }
340
341         /// <summary>
342         /// The property to get/set the minim value of the Progress.
343         /// </summary>
344         /// <since_tizen> 6 </since_tizen>
345         public float MinValue
346         {
347             get
348             {
349                 return (float)GetValue(MinValueProperty);
350             }
351             set
352             {
353                 SetValue(MinValueProperty, value);
354             }
355         }
356
357         /// <summary>
358         /// The property to get/set the current value of the Progress.
359         /// </summary>
360         /// <since_tizen> 6 </since_tizen>
361         public float CurrentValue
362         {
363             get
364             {
365                 return (float)GetValue(CurrentValueProperty);
366             }
367             set
368             {
369                 SetValue(CurrentValueProperty, value);
370                 if (Accessibility.Accessibility.Enabled && IsHighlighted)
371                 {
372                     EmitAccessibilityEvent(AccessibilityPropertyChangeEvent.Value);
373                 }
374             }
375         }
376
377         /// <summary>
378         /// The property to get/set the buffer value of the Progress.
379         /// </summary>
380         /// <since_tizen> 6 </since_tizen>
381         public float BufferValue
382         {
383             get
384             {
385                 return (float)GetValue(BufferValueProperty);
386             }
387             set
388             {
389                 SetValue(BufferValueProperty, value);
390             }
391         }
392
393         /// <summary>
394         /// Gets or sets state of progress.
395         /// </summary>
396         /// <since_tizen> 6 </since_tizen>
397         public ProgressStatusType ProgressState
398         {
399             get
400             {
401                 return (ProgressStatusType)GetValue(ProgressStateProperty);
402             }
403             set
404             {
405                 SetValue(ProgressStateProperty, value);
406             }
407         }
408
409         /// <summary>
410         /// Flag to decide enable or disable in Progress.
411         /// </summary>
412         [EditorBrowsable(EditorBrowsableState.Never)]
413         public bool IsEnabled
414         {
415             get
416             {
417                 return (bool)GetValue(IsEnabledProperty);
418             }
419             set
420             {
421                 SetValue(IsEnabledProperty, value);
422             }
423         }
424
425         /// <inheritdoc/>
426         [EditorBrowsable(EditorBrowsableState.Never)]
427         public override void OnInitialize()
428         {
429             base.OnInitialize();
430             SetAccessibilityConstructor(Role.ProgressBar, AccessibilityInterface.Value);
431             // create necessary components
432             InitializeTrack();
433             InitializeBuffer();
434             InitializeProgress();
435             InitializeIndeterminate();
436
437             indeterminateAnimation?.Stop();
438             indeterminateAnimation = null;
439         }
440
441         /// <inheritdoc/>
442         [EditorBrowsable(EditorBrowsableState.Never)]
443         public override void ApplyStyle(ViewStyle style)
444         {
445             base.ApplyStyle(style);
446
447             if (style is ProgressStyle progressStyle)
448             {
449                 Debug.Assert(trackImage != null);
450                 Debug.Assert(progressImage != null);
451                 Debug.Assert(bufferImage != null);
452
453                 trackImage.ApplyStyle(progressStyle.Track);
454                 progressImage.ApplyStyle(progressStyle.Progress);
455                 bufferImage.ApplyStyle(progressStyle.Buffer);
456
457                 if (null != indeterminateImage && null != progressStyle.IndeterminateImageUrl)
458                 {
459                     indeterminateImage.URL = progressStyle.IndeterminateImageUrl;
460                 }
461             }
462         }
463
464         /// <summary>
465         /// Prevents from showing child widgets in AT-SPI tree.
466         /// </summary>
467         [EditorBrowsable(EditorBrowsableState.Never)]
468         protected override bool AccessibilityShouldReportZeroChildren()
469         {
470             return true;
471         }
472
473         /// <summary>
474         /// Gets minimum value for Accessibility.
475         /// </summary>
476         [EditorBrowsable(EditorBrowsableState.Never)]
477         protected override double AccessibilityGetMinimum()
478         {
479             if (this.ProgressState == Progress.ProgressStatusType.Determinate)
480             {
481                 return (double)MinValue;
482             }
483             else
484             {
485                 return 0.0;
486             }
487         }
488
489         /// <summary>
490         /// Gets the current value for Accessibility.
491         /// </summary>
492         [EditorBrowsable(EditorBrowsableState.Never)]
493         protected override double AccessibilityGetCurrent()
494         {
495             if (this.ProgressState == Progress.ProgressStatusType.Determinate)
496             {
497                 return (double)CurrentValue;
498             }
499             else
500             {
501                 return 0.0;
502             }
503         }
504
505         /// <summary>
506         /// Gets maximum value for Accessibility.
507         /// </summary>
508         [EditorBrowsable(EditorBrowsableState.Never)]
509         protected override double AccessibilityGetMaximum()
510         {
511             if (this.ProgressState == Progress.ProgressStatusType.Determinate)
512             {
513                 return (double)MaxValue;
514             }
515             else
516             {
517                 return 0.0;
518             }
519         }
520
521         /// <summary>
522         /// Dispose Progress and all children on it.
523         /// </summary>
524         /// <param name="type">Dispose type.</param>
525         /// <since_tizen> 6 </since_tizen>
526         protected override void Dispose(DisposeTypes type)
527         {
528             if (disposed)
529             {
530                 return;
531             }
532
533             if (type == DisposeTypes.Explicit)
534             {
535                 //Called by User
536                 //Release your own managed resources here.
537                 //You should release all of your own disposable objects here.
538                 Utility.Dispose(trackImage);
539                 Utility.Dispose(progressImage);
540                 Utility.Dispose(bufferImage);
541                 indeterminateImage = null;
542             }
543
544             //You must call base.Dispose(type) just before exit.
545             base.Dispose(type);
546         }
547
548         /// <summary>
549         /// Change Image status. It can be override.
550         /// </summary>
551         /// This needs to be considered more if public-open is necessary.
552         [EditorBrowsable(EditorBrowsableState.Never)]
553         private void UpdateStates()
554         {
555             ChangeImageState(state);
556         }
557
558         /// <inheritdoc/>
559         [EditorBrowsable(EditorBrowsableState.Never)]
560         public override void OnRelayout(Vector2 size, RelayoutContainer container)
561         {
562             if (size == null) return;
563
564             if (size.Equals(this.size))
565             {
566                 return;
567             }
568
569             this.size = new Vector2(size);
570             UpdateValue();
571         }
572
573         /// <summary>
574         /// Update progress value
575         /// </summary>
576         /// This needs to be considered more if public-open is necessary.
577         [EditorBrowsable(EditorBrowsableState.Never)]
578         private void UpdateValue()
579         {
580             if (null == trackImage || null == progressImage)
581             {
582                 return;
583             }
584
585             if (minValue >= maxValue || currentValue < minValue || currentValue > maxValue)
586             {
587                 return;
588             }
589
590             float width = this.SizeWidth;
591             float height = this.SizeHeight;
592             float progressRatio = (float)(currentValue - minValue) / (float)(maxValue - minValue);
593             float progressWidth = width * progressRatio;
594             progressImage.Size2D = new Size2D((int)(progressWidth + round), (int)height); //Add const round to reach Math.Round function.
595             if (null != bufferImage)
596             {
597                 if (bufferValue < minValue || bufferValue > maxValue)
598                 {
599                     return;
600                 }
601
602                 float bufferRatio = (float)(bufferValue - minValue) / (float)(maxValue - minValue);
603                 float bufferWidth = width * bufferRatio;
604                 bufferImage.Size2D = new Size2D((int)(bufferWidth + round), (int)height); //Add const round to reach Math.Round function.
605             }
606         }
607
608         private Vector4 destinationValue = new Vector4(-1.0f, 0.0f, 10.0f, 1.0f);
609         private Vector4 initialValue = new Vector4(0.0f, 0.0f, 10.0f, 1.0f);
610
611         /// <summary>
612         /// Update Animation for Indeterminate mode.
613         /// </summary>
614         /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API.
615         [EditorBrowsable(EditorBrowsableState.Never)]
616         private void UpdateIndeterminateAnimation()
617         {
618             indeterminateAnimation?.Stop();
619
620             if (null != indeterminateImage)
621             {
622                 indeterminateAnimation = AnimateVisual(indeterminateImage, "pixelArea", destinationValue, 0, 1000,  AlphaFunction.BuiltinFunctions.Default, initialValue);
623                 indeterminateAnimation.Looping = true;
624                 indeterminateAnimation.Play();
625             }
626         }
627
628         /// <summary>
629         /// Get Progress style.
630         /// </summary>
631         /// <returns>The default progress style.</returns>
632         /// <since_tizen> 8 </since_tizen>
633         protected override ViewStyle CreateViewStyle()
634         {
635             return new ProgressStyle();
636         }
637
638         /// <summary>
639         /// Change Image status
640         /// </summary>
641         /// <since_tizen> 6 </since_tizen>
642         /// <param name="statusType">New status type</param>
643         protected void ChangeImageState(ProgressStatusType statusType)
644         {
645             if (!IsEnabled)
646             {
647                 ControlState = ControlState.Disabled;
648
649                 indeterminateAnimation?.Stop();
650                 indeterminateAnimation = null;
651
652                 if (null != indeterminateImage)
653                 {
654                     indeterminateImage.Opacity = 0.0f;
655                 }
656                 progressImage.Hide();
657                 bufferImage.Hide();
658                 return;
659             }
660
661             if (statusType == ProgressStatusType.Buffering)
662             {
663                 indeterminateAnimation?.Stop();
664                 indeterminateAnimation = null;
665
666                 if (null != indeterminateImage)
667                 {
668                     indeterminateImage.Opacity = 0.0f;
669                 }
670                 progressImage.Hide();
671                 bufferImage.Show();
672             }
673             else if (statusType == ProgressStatusType.Determinate)
674             {
675                 indeterminateAnimation?.Stop();
676                 indeterminateAnimation = null;
677
678                 if (null != indeterminateImage)
679                 {
680                     indeterminateImage.Opacity = 0.0f;
681                 }
682                 bufferImage.Hide();
683                 progressImage.Show();
684
685                 UpdateValue();
686             }
687             else if (statusType == ProgressStatusType.Indeterminate)
688             {
689                 bufferImage.Hide();
690                 progressImage.Hide();
691                 if (null != indeterminateImage)
692                 {
693                     indeterminateImage.Opacity = 1.0f;
694                 }
695
696                 UpdateIndeterminateAnimation();
697             }
698         }
699
700         private void Initialize()
701         {
702             AccessibilityHighlightable = true;
703         }
704
705         private void InitializeTrack()
706         {
707             if (null == trackImage)
708             {
709                 trackImage = new ImageView
710                 {
711                     WidthResizePolicy = ResizePolicyType.FillToParent,
712                     HeightResizePolicy = ResizePolicyType.FillToParent,
713                     PositionUsesPivotPoint = true,
714                     ParentOrigin = NUI.ParentOrigin.TopLeft,
715                     PivotPoint = NUI.PivotPoint.TopLeft
716                 };
717                 Add(trackImage);
718             }
719         }
720
721         private void InitializeProgress()
722         {
723             if (null == progressImage)
724             {
725                 progressImage = new ImageView
726                 {
727                     WidthResizePolicy = ResizePolicyType.FillToParent,
728                     HeightResizePolicy = ResizePolicyType.FillToParent,
729                     PositionUsesPivotPoint = true,
730                     ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
731                     PivotPoint = Tizen.NUI.PivotPoint.TopLeft
732                 };
733                 Add(progressImage);
734             }
735         }
736
737         private void InitializeBuffer()
738         {
739             if (null == bufferImage)
740             {
741                 bufferImage = new ImageView
742                 {
743                     WidthResizePolicy = ResizePolicyType.FillToParent,
744                     HeightResizePolicy = ResizePolicyType.FillToParent,
745                     PositionUsesPivotPoint = true,
746                     ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
747                     PivotPoint = Tizen.NUI.PivotPoint.TopLeft
748                 };
749                 Add(bufferImage);
750                 bufferImage.Hide(); // At first, buffer image does not show.
751             }
752         }
753
754         private void InitializeIndeterminate()
755         {
756             indeterminateImage = new ImageVisual
757             {
758                 PixelArea = new Vector4(0.0f, 0.0f, 10.0f, 1.0f),
759                 WrapModeU = WrapModeType.Repeat,
760                 SizePolicy = VisualTransformPolicyType.Relative,
761                 Origin = Visual.AlignType.Center,
762                 AnchorPoint = Visual.AlignType.Center,
763                 Opacity = 0.0f,
764                 Size = new Size2D(1, 1),
765                 URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "nui_component_default_progress_indeterminate.png"
766             };
767             AddVisual("Indeterminate", indeterminateImage);
768
769             if (state == ProgressStatusType.Indeterminate)
770             {
771                 indeterminateImage.Opacity = 1.0f;
772             }
773         }
774     }
775 }