/* * Copyright(c) 2022 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 System.ComponentModel; using Tizen.NUI.Accessibility; using Tizen.NUI.BaseComponents; using Tizen.NUI.Binding; namespace Tizen.NUI.Components { /// /// Slider value changed event data. /// /// 8 public class SliderValueChangedEventArgs : EventArgs { /// /// Current Slider value /// /// 8 public float CurrentValue { get; set; } } /// /// Slider sliding started event data. /// /// 8 public class SliderSlidingStartedEventArgs : EventArgs { /// /// Current Slider value /// /// 8 public float CurrentValue { get; set; } } /// /// Slider sliding finished event data. /// /// 8 public class SliderSlidingFinishedEventArgs : EventArgs { /// /// Current Slider value /// /// 8 public float CurrentValue { get; set; } } /// /// A slider lets users select a value from a continuous or discrete range of values by moving the slider thumb. /// /// 6 public partial class Slider : Control, IAtspiValue { /// /// SpaceBetweenTrackAndIndicatorProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty IndicatorProperty = BindableProperty.Create(nameof(Indicator), typeof(IndicatorType), typeof(Slider), IndicatorType.None, propertyChanged: (bindable, oldValue, newValue) => { var instance = (Slider)bindable; if (newValue != null) { instance.privateIndicatorType = (IndicatorType)newValue; } }, defaultValueCreator: (bindable) => { var instance = (Slider)bindable; return instance.privateIndicatorType; }); /// /// SpaceBetweenTrackAndIndicatorProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty SpaceBetweenTrackAndIndicatorProperty = BindableProperty.Create(nameof(SpaceBetweenTrackAndIndicator), typeof(uint), typeof(Slider), (uint)0, propertyChanged: (bindable, oldValue, newValue) => { var instance = (Slider)bindable; if (newValue != null) { instance.privateSpaceBetweenTrackAndIndicator = (uint)newValue; } }, defaultValueCreator: (bindable) => { var instance = (Slider)bindable; return instance.privateSpaceBetweenTrackAndIndicator; }); /// /// TrackThicknessProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty TrackThicknessProperty = BindableProperty.Create(nameof(TrackThickness), typeof(uint), typeof(Slider), (uint)0, propertyChanged: (bindable, oldValue, newValue) => { var instance = (Slider)bindable; if (newValue != null) { instance.privateTrackThickness = (uint)newValue; } }, defaultValueCreator: (bindable) => { var instance = (Slider)bindable; return instance.privateTrackThickness; }); /// /// IsValueShownProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty IsValueShownProperty = BindableProperty.Create(nameof(IsValueShown), typeof(bool), typeof(Slider), true, propertyChanged: (bindable, oldValue, newValue) => { var instance = (Slider)bindable; if (newValue != null) { bool newValueShown = (bool)newValue; if (instance.isValueShown != newValueShown) { instance.isValueShown = newValueShown; } } }, defaultValueCreator: (bindable) => { var instance = (Slider)bindable; return instance.isValueShown; }); /// /// ValueIndicatorTextProperty /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty ValueIndicatorTextProperty = BindableProperty.Create(nameof(ValueIndicatorText), typeof(string), typeof(Slider), string.Empty, propertyChanged: (bindable, oldValue, newValue) => { var instance = (Slider)bindable; if (newValue != null) { string newText = (string)newValue; instance.valueIndicatorText.Text = newText; } }, defaultValueCreator: (bindable) => { var instance = (Slider)bindable; return instance.valueIndicatorText.Text; }); /// /// Bindable property of CurrentValue /// /// Hidden API, used for NUI XAML data binding /// /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly BindableProperty CurrentValueProperty = BindableProperty.Create(nameof(CurrentValue), typeof(float), typeof(Slider), 0.0f, BindingMode.TwoWay, propertyChanged: (bindable, oldValue, newValue) => { var instance = (Slider)bindable; if (newValue != null) { float value = (float)newValue; if (value < instance.minValue) { instance.curValue = instance.minValue; } else if (value > instance.maxValue) { instance.curValue = instance.maxValue; } else { instance.curValue = value; } instance.sliderValueChangedHandler?.Invoke(instance, new SliderValueChangedEventArgs { CurrentValue = instance.curValue }); if (Accessibility.Accessibility.IsEnabled && instance.IsHighlighted) { instance.EmitAccessibilityEvent(AccessibilityPropertyChangeEvent.Value); } instance.UpdateValue(); } }, defaultValueCreator: (bindable) => { var instance = (Slider)bindable; return instance.curValue; } ); static Slider() { } /// /// The constructor of the Slider class. /// /// 6 public Slider() { Focusable = true; Initialize(); } /// /// The constructor of the Slider class with specific style. /// /// The string to initialize the Slider /// 8 public Slider(string style) : base(style) { Focusable = true; Initialize(); } /// /// The constructor of the Slider class with specific style. /// /// The style object to initialize the Slider /// 8 public Slider(SliderStyle sliderStyle) : base(sliderStyle) { Focusable = true; Initialize(); } /// /// The value changed event handler. /// /// 8 public event EventHandler ValueChanged { add { sliderValueChangedHandler += value; } remove { sliderValueChangedHandler -= value; } } /// /// The sliding started event handler. /// /// 8 public event EventHandler SlidingStarted { add { sliderSlidingStartedHandler += value; } remove { sliderSlidingStartedHandler -= value; } } /// /// The sliding finished event handler. /// /// 8 public event EventHandler SlidingFinished { add { sliderSlidingFinishedHandler += value; } remove { sliderSlidingFinishedHandler -= value; } } /// /// The direction type of slider. /// /// 6 public enum DirectionType { /// /// The Horizontal type. /// /// 6 Horizontal, /// /// The Vertical type. /// /// 6 Vertical } /// /// The indicator type of slider. /// /// 6 public enum IndicatorType { /// Only contains slider bar. /// 6 None, /// Contains slider bar, IndicatorImage. /// 6 Image, /// Contains slider bar, IndicatorText. /// 6 Text } /// /// Return currently applied style. /// /// /// Modifying contents in style may cause unexpected behaviour. /// /// 8 public SliderStyle Style => (SliderStyle)(ViewStyle as SliderStyle)?.Clone(); /// /// Gets or sets the direction type of slider. /// /// 6 public DirectionType Direction { get { return (DirectionType)GetValue(DirectionProperty); } set { SetValue(DirectionProperty, value); NotifyPropertyChanged(); } } private DirectionType InternalDirection { get { return direction; } set { if (direction == value) { return; } direction = value; RelayoutBaseComponent(false); UpdateBgTrackSize(); UpdateBgTrackPosition(); UpdateWarningTrackSize(); UpdateValue(); } } /// /// Gets or sets the indicator type, arrow or sign. /// /// 6 public IndicatorType Indicator { get { return (IndicatorType)GetValue(IndicatorProperty); } set { SetValue(IndicatorProperty, value); } } /// /// Gets or sets the minimum value of slider. /// /// 6 public float MinValue { get { return (float)GetValue(MinValueProperty); } set { SetValue(MinValueProperty, value); NotifyPropertyChanged(); } } private float InternalMinValue { get { return minValue; } set { minValue = value; UpdateValue(); } } /// /// Gets or sets the maximum value of slider. /// /// 6 public float MaxValue { get { return (float)GetValue(MaxValueProperty); } set { SetValue(MaxValueProperty, value); NotifyPropertyChanged(); } } private float InternalMaxValue { get { return maxValue; } set { maxValue = value; UpdateValue(); } } /// /// Gets or sets the current value of slider. /// /// 6 public float CurrentValue { get { return (float)GetValue(CurrentValueProperty); } set { SetValue(CurrentValueProperty, value); } } /// /// Gets or sets the size of the thumb image object. /// /// 6 public Size ThumbSize { get { return GetValue(ThumbSizeProperty) as Size; } set { SetValue(ThumbSizeProperty, value); NotifyPropertyChanged(); } } private Size InternalThumbSize { get { return thumbImage?.Size; } set { if (null != thumbImage) { thumbImage.Size = value; thumbSize = value; } } } /// /// Gets or sets the resource url of the thumb image object. /// /// Please use ThumbImageUrl property. /// /// 6 public string ThumbImageURL { get { return GetValue(ThumbImageURLProperty) as string; } set { SetValue(ThumbImageURLProperty, value); NotifyPropertyChanged(); } } private string InternalThumbImageURL { get { return thumbImage?.ResourceUrl; } set { if (null != thumbImage) { thumbImage.ResourceUrl = value; thumbImageUrl = value; } } } /// /// Gets or sets the resource url selector of the thumb image object. /// Getter returns copied selector value if exist, null otherwise. /// /// Please use ThumbImageUrl property. /// /// Thrown when setting null value. /// 6 public StringSelector ThumbImageURLSelector { get { return GetValue(ThumbImageURLSelectorProperty) as StringSelector; } set { SetValue(ThumbImageURLSelectorProperty, value); NotifyPropertyChanged(); } } private StringSelector InternalThumbImageURLSelector { get { Selector resourceUrlSelector = thumbImage?.ResourceUrlSelector; if(resourceUrlSelector != null) { return new StringSelector(resourceUrlSelector); } return null; } set { if (value == null || thumbImage == null) { throw new NullReferenceException("Slider.ThumbImageURLSelector is null"); } else { thumbImage.ResourceUrlSelector = value; } } } /// /// Gets or sets the Url of the thumb image. /// /// Thrown when setting null value. /// 9 public Selector ThumbImageUrl { get { return GetValue(ThumbImageUrlProperty) as Selector; } set { SetValue(ThumbImageUrlProperty, value); NotifyPropertyChanged(); } } private Selector InternalThumbImageUrl { get { if (thumbImage == null) { return null; } else { return thumbImage.ResourceUrlSelector; } } set { if (value == null || thumbImage == null) { throw new NullReferenceException("Slider.ThumbImageUrl is null"); } else { thumbImage.ResourceUrlSelector = value; } } } /// /// Gets or sets the color of the thumb image object. /// /// 8 public Color ThumbColor { get { return GetValue(ThumbColorProperty) as Color; } set { SetValue(ThumbColorProperty, value); NotifyPropertyChanged(); } } private Color InternalThumbColor { get { return thumbColor; } set { if (null != thumbImage) { thumbColor = value; if (thumbImage.ResourceUrl != null) { thumbImage.ResourceUrl = null; } using (PropertyMap map = new PropertyMap()) { // To remove CA2000 warning messages, use `using` statement. using (PropertyValue type = new PropertyValue((int)Visual.Type.Color)) { map.Insert((int)Visual.Property.Type, type); } using (PropertyValue color = new PropertyValue(thumbColor)) { map.Insert((int)ColorVisualProperty.MixColor, color); } using (PropertyValue radius = new PropertyValue(0.5f)) { map.Insert((int)Visual.Property.CornerRadius, radius); } using (PropertyValue policyType = new PropertyValue((int)VisualTransformPolicyType.Relative)) { map.Insert((int)Visual.Property.CornerRadiusPolicy, policyType); } thumbImage.Image = map; } } } } /// /// Gets or sets the color of the background track image object. /// /// 6 public Color BgTrackColor { get { return GetValue(BgTrackColorProperty) as Color; } set { SetValue(BgTrackColorProperty, value); NotifyPropertyChanged(); } } private Color InternalBgTrackColor { get { return bgTrackImage?.BackgroundColor; } set { if (null != bgTrackImage) { bgTrackImage.BackgroundColor = value; } } } /// /// Gets or sets the color of the slided track image object. /// /// 6 public Color SlidedTrackColor { get { return GetValue(SlidedTrackColorProperty) as Color; } set { SetValue(SlidedTrackColorProperty, value); NotifyPropertyChanged(); } } private Color InternalSlidedTrackColor { get { return slidedTrackImage?.BackgroundColor; } set { if (null != slidedTrackImage) { slidedTrackImage.BackgroundColor = value; } } } /// /// Gets or sets the thickness value of the track. /// /// 6 public uint TrackThickness { get { return (uint)GetValue(TrackThicknessProperty); } set { SetValue(TrackThicknessProperty, value); } } /// /// Gets or sets the warning start value between minimum value and maximum value of slider. /// /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public float WarningStartValue { get { return (float)GetValue(WarningStartValueProperty); } set { SetValue(WarningStartValueProperty, value); NotifyPropertyChanged(); } } private float InternalWarningStartValue { get { return warningStartValue; } set { warningStartValue = value; UpdateValue(); } } /// /// Gets or sets the color of the warning track image object. /// /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public Color WarningTrackColor { get { return GetValue(WarningTrackColorProperty) as Color; } set { SetValue(WarningTrackColorProperty, value); NotifyPropertyChanged(); } } private Color InternalWarningTrackColor { get { return warningTrackImage?.BackgroundColor; } set { if (null != warningTrackImage) { warningTrackImage.BackgroundColor = value; } } } /// /// Gets or sets the color of the warning slided track image object. /// /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public Color WarningSlidedTrackColor { get { return GetValue(WarningSlidedTrackColorProperty) as Color; } set { SetValue(WarningSlidedTrackColorProperty, value); NotifyPropertyChanged(); } } private Color InternalWarningSlidedTrackColor { get { return warningSlidedTrackImage?.BackgroundColor; } set { if (null != warningSlidedTrackImage) { warningSlidedTrackImage.BackgroundColor = value; } } } /// /// Gets or sets the Url of the warning thumb image. /// /// Thrown when setting null value. /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public Selector WarningThumbImageUrl { get { return GetValue(WarningThumbImageUrlProperty) as Selector; } set { SetValue(WarningThumbImageUrlProperty, value); NotifyPropertyChanged(); } } private Selector InternalWarningThumbImageUrl { get { return warningThumbImageUrlSelector; } set { if (value == null || thumbImage == null) { throw new NullReferenceException("Slider.WarningThumbImageUrl is null"); } else { warningThumbImageUrlSelector = value; } } } /// /// Gets or sets the color of the warning thumb image object. /// /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public Color WarningThumbColor { get { return GetValue(WarningThumbColorProperty) as Color; } set { SetValue(WarningThumbColorProperty, value); NotifyPropertyChanged(); } } private Color InternalWarningThumbColor { get { return warningThumbColor; } set { warningThumbColor = value; } } /// /// Gets or sets the resource url of the low indicator image object. /// /// 6 public string LowIndicatorImageURL { get { return GetValue(LowIndicatorImageURLProperty) as string; } set { SetValue(LowIndicatorImageURLProperty, value); NotifyPropertyChanged(); } } private string InternalLowIndicatorImageURL { get { return lowIndicatorImage?.ResourceUrl; } set { if (null == lowIndicatorImage) { lowIndicatorImage = new ImageView { AccessibilityHidden = true, }; } lowIndicatorImage.ResourceUrl = value; } } /// /// Gets or sets the resource url of the high indicator image object. /// /// 6 public string HighIndicatorImageURL { get { return GetValue(HighIndicatorImageURLProperty) as string; } set { SetValue(HighIndicatorImageURLProperty, value); NotifyPropertyChanged(); } } private string InternalHighIndicatorImageURL { get { return highIndicatorImage?.ResourceUrl; } set { if (null == highIndicatorImage) { highIndicatorImage = new ImageView { AccessibilityHidden = true, }; } highIndicatorImage.ResourceUrl = value; } } /// /// Gets or sets the text content of the low indicator text object. /// /// 6 public string LowIndicatorTextContent { get { return GetValue(LowIndicatorTextContentProperty) as string; } set { SetValue(LowIndicatorTextContentProperty, value); NotifyPropertyChanged(); } } private string InternalLowIndicatorTextContent { get { return lowIndicatorText?.Text; } set { if (null != lowIndicatorText) { lowIndicatorText.Text = value; } } } /// /// Gets or sets the text content of the high indicator text object. /// /// 6 public string HighIndicatorTextContent { get { return GetValue(HighIndicatorTextContentProperty) as string; } set { SetValue(HighIndicatorTextContentProperty, value); NotifyPropertyChanged(); } } private string InternalHighIndicatorTextContent { get { return highIndicatorText?.Text; } set { if (null != highIndicatorText) { highIndicatorText.Text = value; } } } /// /// Gets or sets the size of the low indicator object(image or text). /// /// 6 public Size LowIndicatorSize { get { return GetValue(LowIndicatorSizeProperty) as Size; } set { SetValue(LowIndicatorSizeProperty, value); NotifyPropertyChanged(); } } private Size InternalLowIndicatorSize { get { return lowIndicatorSize; } set { lowIndicatorSize = value; UpdateLowIndicatorSize(); UpdateBgTrackSize(); UpdateBgTrackPosition(); UpdateValue(); } } /// /// Gets or sets the size of the high indicator object(image or text). /// /// 6 public Size HighIndicatorSize { get { return GetValue(HighIndicatorSizeProperty) as Size; } set { SetValue(HighIndicatorSizeProperty, value); NotifyPropertyChanged(); } } private Size InternalHighIndicatorSize { get { return highIndicatorSize; } set { highIndicatorSize = value; UpdateHighIndicatorSize(); UpdateBgTrackSize(); UpdateBgTrackPosition(); UpdateValue(); } } /// /// Gets or sets the value of the space between track and indicator. /// /// 6 public uint SpaceBetweenTrackAndIndicator { get { return (uint)GetValue(SpaceBetweenTrackAndIndicatorProperty); } set { SetValue(SpaceBetweenTrackAndIndicatorProperty, value); } } /// /// Flag to decide whether the value indicator is shown /// /// 9 public bool IsValueShown { get { return (bool)GetValue(IsValueShownProperty); } set { SetValue(IsValueShownProperty, value); } } /// /// Gets or sets the text of value indicator. /// /// 9 public string ValueIndicatorText { get { return (string)GetValue(ValueIndicatorTextProperty); } set { SetValue(ValueIndicatorTextProperty, value); } } /// /// Gets or sets the size of the value indicator image object. /// /// 9 public Size ValueIndicatorSize { get { return GetValue(ValueIndicatorSizeProperty) as Size; } set { SetValue(ValueIndicatorSizeProperty, value); NotifyPropertyChanged(); } } private Size InternalValueIndicatorSize { get { return valueIndicatorImage?.Size; } set { if (null != valueIndicatorImage) { valueIndicatorImage.Size = value; } } } /// /// Gets or sets the resource url of the value indicator image object. /// /// 9 public string ValueIndicatorUrl { get { return GetValue(ValueIndicatorUrlProperty) as string; } set { SetValue(ValueIndicatorUrlProperty, value); NotifyPropertyChanged(); } } private string InternalValueIndicatorUrl { get { return valueIndicatorImage?.ResourceUrl; } set { if (null != valueIndicatorImage) { valueIndicatorImage.ResourceUrl = value; } } } /// /// Flag to decide whether the thumb snaps to the nearest discrete value when the user drags the thumb or taps. /// /// The default value is false. /// /// 9 public bool IsDiscrete { get { return (bool)GetValue(IsDiscreteProperty); } set { SetValue(IsDiscreteProperty, value); NotifyPropertyChanged(); } } private bool InternalIsDiscrete { get; set; } = false; /// /// Gets or sets the discrete value of slider. /// /// /// The discrete value is evenly spaced between MinValue and MaxValue. /// For example, MinValue is 0, MaxValue is 100, and DiscreteValue is 20. /// Then, the thumb can only go to 0, 20, 40, 60, 80, and 100. /// The default is 0. /// /// 9 public float DiscreteValue { get { return (float)GetValue(DiscreteValueProperty); } set { SetValue(DiscreteValueProperty, value); NotifyPropertyChanged(); } } private float InternalDiscreteValue { get { return discreteValue; } set { discreteValue = value; UpdateValue(); } } private Extents spaceBetweenTrackAndIndicator { get { if (null == spaceTrackIndicator) { spaceTrackIndicator = new Extents((ushort start, ushort end, ushort top, ushort bottom) => { Extents extents = new Extents(start, end, top, bottom); spaceTrackIndicator.CopyFrom(extents); }, 0, 0, 0, 0); } return spaceTrackIndicator; } } private IndicatorType privateIndicatorType { get { return indicatorType; } set { if (indicatorType == value) { return; } indicatorType = value; RelayoutBaseComponent(false); UpdateBgTrackSize(); UpdateBgTrackPosition(); UpdateValue(); } } private uint privateTrackThickness { get { return trackThickness ?? 0; } set { trackThickness = value; if (bgTrackImage != null) { if (direction == DirectionType.Horizontal) { bgTrackImage.SizeHeight = (float)trackThickness.Value; } else if (direction == DirectionType.Vertical) { bgTrackImage.SizeWidth = (float)trackThickness.Value; } } if (slidedTrackImage != null) { if (direction == DirectionType.Horizontal) { slidedTrackImage.SizeHeight = (float)trackThickness.Value; } else if (direction == DirectionType.Vertical) { slidedTrackImage.SizeWidth = (float)trackThickness.Value; } } if (warningTrackImage != null) { if (direction == DirectionType.Horizontal) { warningTrackImage.SizeHeight = (float)trackThickness.Value; } else if (direction == DirectionType.Vertical) { warningTrackImage.SizeWidth = (float)trackThickness.Value; } } if (warningSlidedTrackImage != null) { if (direction == DirectionType.Horizontal) { warningSlidedTrackImage.SizeHeight = (float)trackThickness.Value; } else if (direction == DirectionType.Vertical) { warningSlidedTrackImage.SizeWidth = (float)trackThickness.Value; } } } } private uint privateSpaceBetweenTrackAndIndicator { get { return privateTrackPadding.Start; } set { ushort val = (ushort)value; privateTrackPadding = new Extents(val, val, val, val); } } private Extents privateTrackPadding { get { return spaceBetweenTrackAndIndicator; } set { spaceBetweenTrackAndIndicator.CopyFrom(value); UpdateComponentByIndicatorTypeChanged(); UpdateBgTrackSize(); UpdateBgTrackPosition(); UpdateValue(); } } /// /// Focus gained callback. /// /// 8 public override void OnFocusGained() { //State = ControlStates.Focused; UpdateState(true, isPressed); base.OnFocusGained(); } /// /// Focus Lost callback. /// /// 8 public override void OnFocusLost() { //State = ControlStates.Normal; UpdateState(false, isPressed); base.OnFocusLost(); } private bool editMode = false; private View recoverIndicator; private View editModeIndicator; /// [EditorBrowsable(EditorBrowsableState.Never)] public override bool OnKeyboardEnter() { if (!IsEnabled) { return false; } if (editMode) { //set editMode false (toggle the mode) editMode = false; FocusManager.Instance.FocusIndicator = recoverIndicator; } else { //set editMode true (toggle the mode) editMode = true; if (editModeIndicator == null) { editModeIndicator = new View() { PositionUsesPivotPoint = true, PivotPoint = new Position(0, 0, 0), WidthResizePolicy = ResizePolicyType.FillToParent, HeightResizePolicy = ResizePolicyType.FillToParent, BorderlineColor = Color.Red, BorderlineWidth = 6.0f, BorderlineOffset = -1f, BackgroundColor = new Color(0.2f, 0.2f, 0.2f, 0.4f), AccessibilityHidden = true, }; } recoverIndicator = FocusManager.Instance.FocusIndicator; FocusManager.Instance.FocusIndicator = editModeIndicator; } UpdateState(true, isPressed); return true; } /// [EditorBrowsable(EditorBrowsableState.Never)] public override bool OnKey(Key key) { if (!IsEnabled || null == key) { return false; } if (key.State == Key.StateType.Down) { float valueDiff; if (IsDiscrete) { valueDiff = discreteValue; } else { // TODO : Currently we set the velocity of value as 1% hardly. // Can we use AccessibilityGetMinimumIncrement? valueDiff = (maxValue - minValue) * 0.01f; } if ((direction == DirectionType.Horizontal && key.KeyPressedName == "Left") || (direction == DirectionType.Vertical && key.KeyPressedName == "Down")) { if (editMode) { if (minValue < curValue) { isPressed = true; CurrentValue -= valueDiff; } return true; // Consumed } } else if ((direction == DirectionType.Horizontal && key.KeyPressedName == "Right") || (direction == DirectionType.Vertical && key.KeyPressedName == "Up")) { if (editMode) { if (maxValue > curValue) { isPressed = true; CurrentValue += valueDiff; } return true; // Consumed } } } else if (key.State == Key.StateType.Up) { isPressed = false; } if (key.KeyPressedName == "Up" || key.KeyPressedName == "Right" || key.KeyPressedName == "Down" || key.KeyPressedName == "Left") { if (editMode) { return true; } } return false; } /// /// Apply style to scrollbar. /// /// The style to apply. /// 8 public override void ApplyStyle(ViewStyle viewStyle) { base.ApplyStyle(viewStyle); SliderStyle sliderStyle = viewStyle as SliderStyle; if (null != sliderStyle?.Progress) { CreateSlidedTrack().ApplyStyle(sliderStyle.Progress); } if (null != sliderStyle?.LowIndicator) { CreateLowIndicatorText().ApplyStyle(sliderStyle.LowIndicator); } if (null != sliderStyle?.HighIndicator) { CreateHighIndicatorText().ApplyStyle(sliderStyle.HighIndicator); } if (null != sliderStyle?.LowIndicatorImage) { CreateLowIndicatorImage().ApplyStyle(sliderStyle.LowIndicatorImage); } if (null != sliderStyle?.HighIndicatorImage) { CreateHighIndicatorImage().ApplyStyle(sliderStyle.HighIndicatorImage); } if (null != sliderStyle?.Track) { CreateBackgroundTrack().ApplyStyle(sliderStyle.Track); } if (null != sliderStyle?.Thumb) { CreateThumb().ApplyStyle(sliderStyle.Thumb); } if (null != sliderStyle?.ValueIndicatorText) { CreateValueIndicatorText().ApplyStyle(sliderStyle.ValueIndicatorText); } if (null != sliderStyle?.ValueIndicatorImage) { CreateValueIndicatorImage().ApplyStyle(sliderStyle.ValueIndicatorImage); } if (null != sliderStyle?.WarningTrack) { CreateWarningTrack().ApplyStyle(sliderStyle.WarningTrack); } if (null != sliderStyle?.WarningProgress) { CreateWarningSlidedTrack().ApplyStyle(sliderStyle.WarningProgress); } EnableControlStatePropagation = true; } /// /// Gets minimum value for Accessibility. /// [EditorBrowsable(EditorBrowsableState.Never)] double IAtspiValue.AccessibilityGetMinimum() { return (double)MinValue; } /// /// Gets the current value for Accessibility. /// [EditorBrowsable(EditorBrowsableState.Never)] double IAtspiValue.AccessibilityGetCurrent() { return (double)CurrentValue; } /// /// Formatted current value. /// [EditorBrowsable(EditorBrowsableState.Never)] string IAtspiValue.AccessibilityGetValueText() { return $"{CurrentValue}"; } /// /// Gets maximum value for Accessibility. /// [EditorBrowsable(EditorBrowsableState.Never)] double IAtspiValue.AccessibilityGetMaximum() { return (double)MaxValue; } /// /// Sets the current value using Accessibility. /// [EditorBrowsable(EditorBrowsableState.Never)] bool IAtspiValue.AccessibilitySetCurrent(double value) { var current = (float)value; if (current >= MinValue && current <= MaxValue) { CurrentValue = current; return true; } return false; } /// /// Gets minimum increment for Accessibility. /// [EditorBrowsable(EditorBrowsableState.Never)] double IAtspiValue.AccessibilityGetMinimumIncrement() { // FIXME return (MaxValue - MinValue) / 20.0; } /// /// Initialize AT-SPI object. /// [EditorBrowsable(EditorBrowsableState.Never)] public override void OnInitialize() { base.OnInitialize(); AccessibilityRole = Role.Slider; } /// /// Get Slider style. /// /// The default slider style. /// 8 protected override ViewStyle CreateViewStyle() { return new SliderStyle(); } /// /// Dispose Slider. /// /// Dispose type. /// 6 protected override void Dispose(DisposeTypes type) { if (disposed) { return; } if (type == DisposeTypes.Explicit) { if (null != panGestureDetector) { panGestureDetector.Detach(this); panGestureDetector.Detected -= OnPanGestureDetected; panGestureDetector.Dispose(); panGestureDetector = null; } if (null != thumbImage) { thumbImage.TouchEvent -= OnTouchEventForThumb; Utility.Dispose(thumbImage); } Utility.Dispose(warningSlidedTrackImage); Utility.Dispose(warningTrackImage); Utility.Dispose(slidedTrackImage); Utility.Dispose(bgTrackImage); Utility.Dispose(lowIndicatorImage); Utility.Dispose(highIndicatorImage); Utility.Dispose(lowIndicatorText); Utility.Dispose(highIndicatorText); Utility.Dispose(valueIndicatorImage); Utility.Dispose(valueIndicatorText); this.TouchEvent -= OnTouchEventForTrack; recoverIndicator = null; if (editModeIndicator != null) { editModeIndicator.Dispose(); editModeIndicator = null; } } base.Dispose(type); } /// /// Update Slider by style. /// /// This will be public opened later after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] protected override void OnUpdate() { RelayoutBaseComponent(); UpdateComponentByIndicatorTypeChanged(); UpdateBgTrackSize(); UpdateBgTrackPosition(); UpdateWarningTrackSize(); UpdateLowIndicatorSize(); UpdateHighIndicatorSize(); UpdateValue(); } /// [EditorBrowsable(EditorBrowsableState.Never)] protected override void OnEnabled(bool enabled) { base.OnEnabled(enabled); UpdateValue(); } private void CalculateCurrentValueByGesture(float offset) { currentSlidedOffset += offset; float resultValue = this.CurrentValue; int bgTrackLength = GetBgTrackLength(); if (bgTrackLength != 0) { resultValue = ((currentSlidedOffset / (float)bgTrackLength) * (float)(maxValue - minValue)) + minValue; } if (IsDiscrete) { this.CurrentValue = CalculateDiscreteValue(resultValue); } else { this.CurrentValue = resultValue; } } private bool OnTouchEventForTrack(object source, TouchEventArgs e) { if (!IsEnabled) { return false; } if (this.FocusableInTouch == false && editMode == false) { isFocused = false; } PointStateType state = e.Touch.GetState(0); if (state == PointStateType.Down) { if (isValueShown) { valueIndicatorImage.Show(); } UpdateState(isFocused, true); sliderSlidingStartedHandler?.Invoke(this, new SliderSlidingStartedEventArgs { CurrentValue = curValue }); Vector2 pos = e.Touch.GetLocalPosition(0); CalculateCurrentValueByTouch(pos); UpdateValue(); } else if (state == PointStateType.Up) { if (isValueShown) { valueIndicatorImage.Hide(); } UpdateState(isFocused, false); sliderSlidingFinishedHandler?.Invoke(this, new SliderSlidingFinishedEventArgs { CurrentValue = curValue }); } return false; } private bool OnTouchEventForThumb(object source, TouchEventArgs e) { if (this.FocusableInTouch == false && editMode == false) { isFocused = false; } PointStateType state = e.Touch.GetState(0); if (state == PointStateType.Down) { UpdateState(isFocused, true); } else if (state == PointStateType.Up) { UpdateState(isFocused, false); } return true; } private void CalculateCurrentValueByTouch(Vector2 pos) { int bgTrackLength = GetBgTrackLength(); if (direction == DirectionType.Horizontal) { currentSlidedOffset = pos.X - GetBgTrackLowIndicatorOffset(); } else if (direction == DirectionType.Vertical) { currentSlidedOffset = this.Size2D.Height - pos.Y - GetBgTrackLowIndicatorOffset(); } if (bgTrackLength != 0) { float resultValue = ((currentSlidedOffset / (float)bgTrackLength) * (maxValue - minValue)) + minValue; if (IsDiscrete) { this.CurrentValue = CalculateDiscreteValue(resultValue); } else { this.CurrentValue = resultValue; } } } private float CalculateDiscreteValue(float value) { return ((float)Math.Round((value / discreteValue)) * discreteValue); } private void UpdateState(bool isFocusedNew, bool isPressedNew) { if (isFocused == isFocusedNew && isPressed == isPressedNew) { return; } if (thumbImage == null || Style == null) { return; } isFocused = isFocusedNew; isPressed = isPressedNew; if (!IsEnabled) // Disabled { ControlState = ControlState.Disabled; } else if (!isFocused && !isPressed) { ControlState = ControlState.Normal; } else if (isPressed) { ControlState = ControlState.Pressed; } else if (!isPressed && isFocused) { ControlState = ControlState.Focused; } } private void UpdateComponentByIndicatorTypeChanged() { IndicatorType type = CurrentIndicatorType(); if (type == IndicatorType.None) { if (lowIndicatorImage != null) { lowIndicatorImage.Hide(); } if (highIndicatorImage != null) { highIndicatorImage.Hide(); } if (lowIndicatorText != null) { lowIndicatorText.Hide(); } if (highIndicatorText != null) { highIndicatorText.Hide(); } } else if (type == IndicatorType.Image) { if (lowIndicatorImage != null) { lowIndicatorImage.Show(); } if (highIndicatorImage != null) { highIndicatorImage.Show(); } if (lowIndicatorText != null) { lowIndicatorText.Hide(); } if (highIndicatorText != null) { highIndicatorText.Hide(); } } else if (type == IndicatorType.Text) { if (lowIndicatorText != null) { lowIndicatorText.Show(); } if (highIndicatorText != null) { highIndicatorText.Show(); } if (lowIndicatorImage != null) { lowIndicatorImage.Hide(); } if (highIndicatorImage != null) { highIndicatorImage.Hide(); } } } } }