/* * Copyright(c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ using System; using Tizen.NUI.BaseComponents; using Tizen.NUI.Binding; using System.ComponentModel; using System.Diagnostics; namespace Tizen.NUI.Components { /// /// The Progress class is used to show the ongoing status with a long narrow bar. /// /// 6 public partial class Progress : Control { /// /// MaxValueProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(float), typeof(Progress), default(float), propertyChanged: (bindable, oldValue, newValue) => { var instance = (Progress)bindable; if (newValue != null) { instance.maxValue = (float)newValue; instance.UpdateValue(); } }, defaultValueCreator: (bindable) => { var instance = (Progress)bindable; return instance.maxValue; }); /// /// MinValueProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(float), typeof(Progress), default(float), propertyChanged: (bindable, oldValue, newValue) => { var instance = (Progress)bindable; if (newValue != null) { instance.minValue = (float)newValue; instance.UpdateValue(); } }, defaultValueCreator: (bindable) => { var instance = (Progress)bindable; return instance.minValue; }); /// /// CurrentValueProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty CurrentValueProperty = BindableProperty.Create(nameof(CurrentValue), typeof(float), typeof(Progress), default(float), propertyChanged: (bindable, oldValue, newValue) => { var instance = (Progress)bindable; if (newValue != null) { if ((float)newValue > instance.maxValue || (float)newValue < instance.minValue) { return; } instance.currentValue = (float)newValue; instance.UpdateValue(); } }, defaultValueCreator: (bindable) => { var instance = (Progress)bindable; return instance.currentValue; }); /// /// BufferValueProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty BufferValueProperty = BindableProperty.Create(nameof(BufferValue), typeof(float), typeof(Progress), default(float), propertyChanged: (bindable, oldValue, newValue) => { var instance = (Progress)bindable; if (newValue != null) { if ((float)newValue > instance.maxValue || (float)newValue < instance.minValue) { return; } instance.bufferValue = (float)newValue; instance.UpdateValue(); } }, defaultValueCreator: (bindable) => { var instance = (Progress)bindable; return instance.bufferValue; }); /// /// ProgressStateProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty ProgressStateProperty = BindableProperty.Create(nameof(ProgressState), typeof(ProgressStatusType), typeof(Progress), ProgressStatusType.Indeterminate, propertyChanged: (bindable, oldValue, newValue) => { var instance = (Progress)bindable; if (newValue != null) { instance.state = (ProgressStatusType)newValue; instance.UpdateStates(); } }, defaultValueCreator: (bindable) => { var instance = (Progress)bindable; return instance.state; }); /// /// IsEnabledProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(Progress), true, propertyChanged: (bindable, oldValue, newValue) => { var instance = (Progress)bindable; if (newValue != null) { bool newEnabled = (bool)newValue; if (instance.isEnabled != newEnabled) { instance.isEnabled = newEnabled; instance.Sensitive = newEnabled; instance.UpdateStates(); } } }, defaultValueCreator: (bindable) => ((Progress)bindable).isEnabled); /// This needs to be considered more if public-open is necessary. private ProgressStatusType state = ProgressStatusType.Determinate; private Vector2 size = null; private const float round = 0.5f; private ImageView trackImage = null; private ImageView progressImage = null; private ImageView bufferImage = null; private ImageVisual indeterminateImage = null; private float maxValue = 100; private float minValue = 0; private float currentValue = 0; private float bufferValue = 0; private Animation indeterminateAnimation = null; bool isEnabled = true; static Progress() { } /// /// The constructor of Progress /// /// 6 public Progress() : base() { Initialize(); } /// /// The constructor of the Progress class with specific style. /// /// style name /// 8 public Progress(string style) : base(style) { Initialize(); } /// /// The constructor of the Progress class with specific style. /// /// The style object to initialize the Progress. /// 8 public Progress(ProgressStyle progressStyle) : base(progressStyle) { Initialize(); } /// /// The status type of the Progress. /// /// 6 public enum ProgressStatusType { /// /// Show BufferImage /// /// 6 Buffering, /// /// Show ProgressImage and BufferImage /// /// 6 Determinate, /// /// Show TrackImage /// /// 6 Indeterminate } /// /// Return currently applied style. /// /// /// Modifying contents in style may cause unexpected behaviour. /// /// 8 public ProgressStyle Style => (ProgressStyle)(ViewStyle as ProgressStyle)?.Clone(); /// /// The property to get/set Track image object URL of the Progress. /// /// 6 public string TrackImageURL { get { return GetValue(TrackImageURLProperty) as string; } set { SetValue(TrackImageURLProperty, value); NotifyPropertyChanged(); } } private string InternalTrackImageURL { get => trackImage.ResourceUrl; set => trackImage.ResourceUrl = value; } /// /// The property to get/set Progress object image URL of the Progress. /// /// 6 public string ProgressImageURL { get { return GetValue(ProgressImageURLProperty) as string; } set { SetValue(ProgressImageURLProperty, value); NotifyPropertyChanged(); } } private string InternalProgressImageURL { get => progressImage.ResourceUrl; set => progressImage.ResourceUrl = value; } /// /// The property to get/set Buffer object image resource URL of the Progress. /// /// 6 public string BufferImageURL { get { return GetValue(BufferImageURLProperty) as string; } set { SetValue(BufferImageURLProperty, value); NotifyPropertyChanged(); } } private string InternalBufferImageURL { get => bufferImage.ResourceUrl; set => bufferImage.ResourceUrl = value; } /// /// The property to get/set the indeterminate image. /// /// Thrown when setting null value. /// 9 public string IndeterminateImageUrl { get { return GetValue(IndeterminateImageUrlProperty) as string; } set { SetValue(IndeterminateImageUrlProperty, value); NotifyPropertyChanged(); } } private string InternalIndeterminateImageUrl { get { if (indeterminateImage == null) { return null; } else { return indeterminateImage?.URL; } } set { if (value == null || indeterminateImage == null) { throw new NullReferenceException("Progress.IndeterminateImage is null"); } else { indeterminateImage.URL = value; } } } /// /// The property to get/set Track object color of the Progress. /// /// 6 public Color TrackColor { get { return GetValue(TrackColorProperty) as Color; } set { SetValue(TrackColorProperty, value); NotifyPropertyChanged(); } } private Color InternalTrackColor { get => trackImage.BackgroundColor; set => trackImage.BackgroundColor = value; } /// /// The property to get/set Progress object color of the Progress. /// /// 6 public Color ProgressColor { get { return GetValue(ProgressColorProperty) as Color; } set { SetValue(ProgressColorProperty, value); NotifyPropertyChanged(); } } private Color InternalProgressColor { get => progressImage.BackgroundColor; set => progressImage.BackgroundColor = value; } /// /// The property to get/set Buffer object color of the Progress. /// /// 6 public Color BufferColor { get { return GetValue(BufferColorProperty) as Color; } set { SetValue(BufferColorProperty, value); NotifyPropertyChanged(); } } private Color InternalBufferColor { get => bufferImage.BackgroundColor; set => bufferImage.BackgroundColor = value; } /// /// The property to get/set the maximum value of the Progress. /// /// 6 public float MaxValue { get { return (float)GetValue(MaxValueProperty); } set { SetValue(MaxValueProperty, value); } } /// /// The property to get/set the minim value of the Progress. /// /// 6 public float MinValue { get { return (float)GetValue(MinValueProperty); } set { SetValue(MinValueProperty, value); } } /// /// The property to get/set the current value of the Progress. /// /// 6 public float CurrentValue { get { return (float)GetValue(CurrentValueProperty); } set { SetValue(CurrentValueProperty, value); if (Accessibility.Accessibility.IsEnabled && IsHighlighted) { EmitAccessibilityEvent(AccessibilityPropertyChangeEvent.Value); } } } /// /// The property to get/set the buffer value of the Progress. /// /// 6 public float BufferValue { get { return (float)GetValue(BufferValueProperty); } set { SetValue(BufferValueProperty, value); } } /// /// Gets or sets state of progress. /// /// 6 public ProgressStatusType ProgressState { get { return (ProgressStatusType)GetValue(ProgressStateProperty); } set { SetValue(ProgressStateProperty, value); } } /// /// Flag to decide enable or disable in Progress. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEnabled { get { return (bool)GetValue(IsEnabledProperty); } set { SetValue(IsEnabledProperty, value); } } /// [EditorBrowsable(EditorBrowsableState.Never)] public override void OnInitialize() { base.OnInitialize(); SetAccessibilityConstructor(Role.ProgressBar, AccessibilityInterface.Value); // create necessary components InitializeTrack(); InitializeBuffer(); InitializeProgress(); InitializeIndeterminate(); indeterminateAnimation?.Stop(); indeterminateAnimation = null; } /// [EditorBrowsable(EditorBrowsableState.Never)] public override void ApplyStyle(ViewStyle style) { base.ApplyStyle(style); if (style is ProgressStyle progressStyle) { Debug.Assert(trackImage != null); Debug.Assert(progressImage != null); Debug.Assert(bufferImage != null); trackImage.ApplyStyle(progressStyle.Track); progressImage.ApplyStyle(progressStyle.Progress); bufferImage.ApplyStyle(progressStyle.Buffer); if (null != indeterminateImage && null != progressStyle.IndeterminateImageUrl) { indeterminateImage.URL = progressStyle.IndeterminateImageUrl; } } } /// /// Prevents from showing child widgets in AT-SPI tree. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override bool AccessibilityShouldReportZeroChildren() { return true; } /// /// Gets minimum value for Accessibility. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override double AccessibilityGetMinimum() { if (this.ProgressState == Progress.ProgressStatusType.Determinate) { return (double)MinValue; } else { return 0.0; } } /// /// Gets the current value for Accessibility. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override double AccessibilityGetCurrent() { if (this.ProgressState == Progress.ProgressStatusType.Determinate) { return (double)CurrentValue; } else { return 0.0; } } /// /// Gets maximum value for Accessibility. /// [EditorBrowsable(EditorBrowsableState.Never)] protected override double AccessibilityGetMaximum() { if (this.ProgressState == Progress.ProgressStatusType.Determinate) { return (double)MaxValue; } else { return 0.0; } } /// /// Dispose Progress and all children on it. /// /// Dispose type. /// 6 protected override void Dispose(DisposeTypes type) { if (disposed) { return; } if (type == DisposeTypes.Explicit) { //Called by User //Release your own managed resources here. //You should release all of your own disposable objects here. Utility.Dispose(trackImage); Utility.Dispose(progressImage); Utility.Dispose(bufferImage); indeterminateImage = null; } //You must call base.Dispose(type) just before exit. base.Dispose(type); } /// /// Change Image status. It can be override. /// /// This needs to be considered more if public-open is necessary. [EditorBrowsable(EditorBrowsableState.Never)] private void UpdateStates() { ChangeImageState(state); } /// [EditorBrowsable(EditorBrowsableState.Never)] public override void OnRelayout(Vector2 size, RelayoutContainer container) { if (size == null) return; if (size.Equals(this.size)) { return; } this.size = new Vector2(size); UpdateValue(); } /// /// Update progress value /// /// This needs to be considered more if public-open is necessary. [EditorBrowsable(EditorBrowsableState.Never)] private void UpdateValue() { if (null == trackImage || null == progressImage) { return; } if (minValue >= maxValue || currentValue < minValue || currentValue > maxValue) { return; } float width = this.SizeWidth; float height = this.SizeHeight; float progressRatio = (float)(currentValue - minValue) / (float)(maxValue - minValue); float progressWidth = width * progressRatio; progressImage.Size2D = new Size2D((int)(progressWidth + round), (int)height); //Add const round to reach Math.Round function. if (null != bufferImage) { if (bufferValue < minValue || bufferValue > maxValue) { return; } float bufferRatio = (float)(bufferValue - minValue) / (float)(maxValue - minValue); float bufferWidth = width * bufferRatio; bufferImage.Size2D = new Size2D((int)(bufferWidth + round), (int)height); //Add const round to reach Math.Round function. } } private Vector4 destinationValue = new Vector4(-1.0f, 0.0f, 10.0f, 1.0f); private Vector4 initialValue = new Vector4(0.0f, 0.0f, 10.0f, 1.0f); /// /// Update Animation for Indeterminate mode. /// /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] private void UpdateIndeterminateAnimation() { indeterminateAnimation?.Stop(); if (null != indeterminateImage) { indeterminateAnimation = AnimateVisual(indeterminateImage, "pixelArea", destinationValue, 0, 1000, AlphaFunction.BuiltinFunctions.Default, initialValue); indeterminateAnimation.Looping = true; indeterminateAnimation.Play(); } } /// /// Get Progress style. /// /// The default progress style. /// 8 protected override ViewStyle CreateViewStyle() { return new ProgressStyle(); } /// /// Change Image status /// /// 6 /// New status type protected void ChangeImageState(ProgressStatusType statusType) { if (!IsEnabled) { ControlState = ControlState.Disabled; indeterminateAnimation?.Stop(); indeterminateAnimation = null; if (null != indeterminateImage) { indeterminateImage.Opacity = 0.0f; } progressImage.Hide(); bufferImage.Hide(); return; } if (statusType == ProgressStatusType.Buffering) { indeterminateAnimation?.Stop(); indeterminateAnimation = null; if (null != indeterminateImage) { indeterminateImage.Opacity = 0.0f; } progressImage.Hide(); bufferImage.Show(); } else if (statusType == ProgressStatusType.Determinate) { indeterminateAnimation?.Stop(); indeterminateAnimation = null; if (null != indeterminateImage) { indeterminateImage.Opacity = 0.0f; } bufferImage.Hide(); progressImage.Show(); UpdateValue(); } else if (statusType == ProgressStatusType.Indeterminate) { bufferImage.Hide(); progressImage.Hide(); if (null != indeterminateImage) { indeterminateImage.Opacity = 1.0f; } UpdateIndeterminateAnimation(); } } private void Initialize() { AccessibilityHighlightable = true; } private void InitializeTrack() { if (null == trackImage) { trackImage = new ImageView { WidthResizePolicy = ResizePolicyType.FillToParent, HeightResizePolicy = ResizePolicyType.FillToParent, PositionUsesPivotPoint = true, ParentOrigin = NUI.ParentOrigin.TopLeft, PivotPoint = NUI.PivotPoint.TopLeft }; Add(trackImage); } } private void InitializeProgress() { if (null == progressImage) { progressImage = new ImageView { WidthResizePolicy = ResizePolicyType.FillToParent, HeightResizePolicy = ResizePolicyType.FillToParent, PositionUsesPivotPoint = true, ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft, PivotPoint = Tizen.NUI.PivotPoint.TopLeft }; Add(progressImage); } } private void InitializeBuffer() { if (null == bufferImage) { bufferImage = new ImageView { WidthResizePolicy = ResizePolicyType.FillToParent, HeightResizePolicy = ResizePolicyType.FillToParent, PositionUsesPivotPoint = true, ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft, PivotPoint = Tizen.NUI.PivotPoint.TopLeft }; Add(bufferImage); bufferImage.Hide(); // At first, buffer image does not show. } } private void InitializeIndeterminate() { indeterminateImage = new ImageVisual { PixelArea = new Vector4(0.0f, 0.0f, 10.0f, 1.0f), WrapModeU = WrapModeType.Repeat, SizePolicy = VisualTransformPolicyType.Relative, Origin = Visual.AlignType.Center, AnchorPoint = Visual.AlignType.Center, Opacity = 0.0f, Size = new Size2D(1, 1), URL = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "nui_component_default_progress_indeterminate.png" }; AddVisual("Indeterminate", indeterminateImage); if (state == ProgressStatusType.Indeterminate) { indeterminateImage.Opacity = 1.0f; } } } }