From: Jiyun Yang Date: Wed, 27 May 2020 07:32:31 +0000 (+0900) Subject: [NUI] Add Scrollbar and CircularScrollbar (#1628) X-Git-Tag: accepted/tizen/unified/20210219.040944~731 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9ec619e284025b8c4f96c8f89dc4ba6136613884;p=platform%2Fcore%2Fcsapi%2Ftizenfx.git [NUI] Add Scrollbar and CircularScrollbar (#1628) * Implement Arc Visual to support arc shapes used in CircularScrollbar * Move previous ScrollBar class code from Scrollbar.cs to ImageScrollBar.cs. * Implement new Scrollbar class based on a ScrollbarBase class. * Implement new CircularScrollbar class based on a ScrollbarBase class. Signed-off-by: Jiyun Yang --- diff --git a/src/Tizen.NUI.Components/Controls/ImageScrollBar.cs b/src/Tizen.NUI.Components/Controls/ImageScrollBar.cs new file mode 100755 index 0000000..16f66db --- /dev/null +++ b/src/Tizen.NUI.Components/Controls/ImageScrollBar.cs @@ -0,0 +1,657 @@ +/* + * Copyright(c) 2019 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 System.ComponentModel; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Components +{ + /// + /// The ScrollBar class of nui component. It allows users to recognize the direction and the range of lists/content. + /// + /// + /// Please note that this class will be replaced with Scrollbar class in the near future. + /// + /// 6 + public class ScrollBar : Control + { + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty DirectionProperty = BindableProperty.Create(nameof(Direction), typeof(DirectionType), typeof(ScrollBar), DirectionType.Horizontal, propertyChanged: (bindable, oldValue, newValue) => + { + var instance = (ScrollBar)bindable; + if (newValue != null) + { + instance.Style.Direction = (DirectionType?)newValue; + instance.UpdateValue(); + } + }, + defaultValueCreator: (bindable) => + { + var instance = (ScrollBar)bindable; + return instance.Style.Direction; + }); + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(int), typeof(ScrollBar), default(int), propertyChanged: (bindable, oldValue, newValue) => + { + var instance = (ScrollBar)bindable; + if (newValue != null) + { + if ((int)newValue >= 0) + { + instance.maxValue = (int)newValue; + instance.UpdateValue(); + } + } + }, + defaultValueCreator: (bindable) => + { + var instance = (ScrollBar)bindable; + return instance.maxValue; + }); + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(int), typeof(ScrollBar), default(int), propertyChanged: (bindable, oldValue, newValue) => + { + var instance = (ScrollBar)bindable; + if (newValue != null) + { + if ((int)newValue >= 0) + { + instance.minValue = (int)newValue; + instance.UpdateValue(); + } + } + }, + defaultValueCreator: (bindable) => + { + var instance = (ScrollBar)bindable; + return instance.minValue; + }); + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty CurrentValueProperty = BindableProperty.Create(nameof(CurrentValue), typeof(int), typeof(ScrollBar), default(int), propertyChanged: (bindable, oldValue, newValue) => + { + var instance = (ScrollBar)bindable; + if (newValue != null) + { + if ((int)newValue >= 0) + { + instance.curValue = (int)newValue; + instance.UpdateValue(); + } + } + }, + defaultValueCreator: (bindable) => + { + var instance = (ScrollBar)bindable; + return instance.curValue; + }); + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty DurationProperty = BindableProperty.Create(nameof(Duration), typeof(uint), typeof(ScrollBar), default(uint), propertyChanged: (bindable, oldValue, newValue) => + { + var instance = (ScrollBar)bindable; + if (newValue != null) + { + instance.Style.Duration = (uint)newValue; + if (instance.scrollAniPlayer != null) + { + instance.scrollAniPlayer.Duration = (int)(uint)newValue; + } + } + }, + defaultValueCreator: (bindable) => + { + var instance = (ScrollBar)bindable; + return instance.Style.Duration; + }); + + private ImageView trackImage; + private ImageView thumbImage; + private Animation scrollAniPlayer = null; + private float thumbImagePosX; + private float thumbImagePosY; + private bool enableAni = false; + private int minValue; + private int maxValue; + private int curValue; + static ScrollBar() { } + + /// + /// The constructor of ScrollBar. + /// + /// 6 + public ScrollBar() : base() + { + Initialize(); + } + + /// + /// The constructor of ScrollBar with specific style. + /// + /// style name + /// 8 + public ScrollBar(string style) : base(style) + { + Initialize(); + } + + /// + /// The constructor of ScrollBar with specific style. + /// + /// The style object to initialize the ScrollBar. + /// 8 + public ScrollBar(ScrollBarStyle scrollBarStyle) : base(scrollBarStyle) + { + Initialize(); + } + + /// + /// The direction type of the Scroll. + /// + /// 6 + public enum DirectionType + { + /// + /// The Horizontal type. + /// + /// 6 + Horizontal, + + /// + /// The Vertical type. + /// + /// 6 + Vertical + } + + #region public property + /// + /// Get style of scrollbar. + /// + /// 8 + public new ScrollBarStyle Style => ViewStyle as ScrollBarStyle; + + /// + /// The property to get/set the direction of the ScrollBar. + /// + /// 6 + public DirectionType Direction + { + get + { + return (DirectionType)GetValue(DirectionProperty); + } + set + { + SetValue(DirectionProperty, value); + } + } + + /// + /// The property to get/set the size of the thumb object. + /// + /// Throw when ThumbSize is null. + /// + /// + /// ScrollBar scroll; + /// try + /// { + /// scroll.ThumbSize = new Size(500, 10, 0); + /// } + /// catch(InvalidOperationException e) + /// { + /// Tizen.Log.Error(LogTag, "Failed to set ThumbSize value : " + e.Message); + /// } + /// + /// + /// 6 + public Size ThumbSize + { + get + { + return Style?.Thumb?.Size; + } + set + { + if (null != Style?.Thumb) + { + Style.Thumb.Size = value; + RelayoutRequest(); + } + } + } + + /// + /// The property to get/set the image URL of the track object. + /// + /// 6 + public string TrackImageURL + { + get + { + return Style?.Track?.ResourceUrl?.All; + } + set + { + if (null != Style?.Track) + { + Style.Track.ResourceUrl = value; + RelayoutRequest(); + } + } + } + + /// + /// The property to get/set the color of the track object. + /// + /// 6 + public Color TrackColor + { + get + { + return Style?.Track?.BackgroundColor?.All; + } + set + { + if (null != Style?.Track) + { + Style.Track.BackgroundColor = value; + RelayoutRequest(); + } + } + } + + /// + /// The property to get/set the color of the thumb object. + /// + /// 6 + public Color ThumbColor + { + get + { + return Style?.Thumb?.BackgroundColor?.All; + } + set + { + if(null != Style?.Thumb) + { + Style.Thumb.BackgroundColor = value; + RelayoutRequest(); + } + } + } + + /// + /// The property to get/set the max value of the ScrollBar. + /// + /// 6 + public int MaxValue + { + get + { + return (int)GetValue(MaxValueProperty); + } + set + { + SetValue(MaxValueProperty, value); + } + } + + /// + /// The property to get/set the min value of the ScrollBar. + /// + /// 6 + public int MinValue + { + get + { + return (int)GetValue(MinValueProperty); + } + set + { + SetValue(MinValueProperty, value); + } + } + + /// + /// The property to get/set the current value of the ScrollBar. + /// + /// Throw when Current value is less than Min value, or greater than Max value. + /// + /// + /// ScrollBar scroll; + /// scroll.MaxValue = 100; + /// scroll.MinValue = 0; + /// try + /// { + /// scroll.CurrentValue = 50; + /// } + /// catch(ArgumentOutOfRangeException e) + /// { + /// Tizen.Log.Error(LogTag, "Failed to set Current value : " + e.Message); + /// } + /// + /// + /// 6 + public int CurrentValue + { + get + { + return (int)GetValue(CurrentValueProperty); + } + set + { + SetValue(CurrentValueProperty, value); + } + } + + /// + /// Property to set/get animation duration. + /// + /// 6 + public uint Duration + { + get + { + return (uint)GetValue(DurationProperty); + } + set + { + SetValue(DurationProperty, value); + } + } + #endregion + + /// + /// Method to set current value. The thumb object would move to the corresponding position with animation or not. + /// + /// The special current value. + /// Enable move with animation or not, the default value is true. + /// Throw when current size is less than the min value, or greater than the max value. + /// + /// + /// ScrollBar scroll; + /// scroll.MinValue = 0; + /// scroll.MaxValue = 100; + /// try + /// { + /// scroll.SetCurrentValue(50); + /// } + /// catch(ArgumentOutOfRangeException e) + /// { + /// Tizen.Log.Error(LogTag, "Failed to set current value : " + e.Message); + /// } + /// + /// + /// 6 + public void SetCurrentValue(int currentValue, bool enableAnimation = true) + { + if (currentValue < minValue || currentValue > maxValue) + { + //TNLog.E("Current value is less than the Min value, or greater than the Max value. currentValue = " + currentValue + ";"); +#pragma warning disable CA2208 // Instantiate argument exceptions correctly + throw new ArgumentOutOfRangeException("Wrong Current value. It shoud be greater than the Min value, and less than the Max value!"); +#pragma warning restore CA2208 // Instantiate argument exceptions correctly + } + + enableAni = enableAnimation; + CurrentValue = currentValue; + } + + /// + /// Dispose ScrollBar. + /// + /// The DisposeTypes value. + /// 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(thumbImage); + + if (scrollAniPlayer != null) + { + scrollAniPlayer.Stop(); + scrollAniPlayer.Clear(); + scrollAniPlayer.Dispose(); + scrollAniPlayer = null; + } + } + + //Release your own unmanaged resources here. + //You should not access any managed member here except static instance. + //because the execution order of Finalizes is non-deterministic. + //Unreference this from if a static instance refer to this. + + //You must call base.Dispose(type) just before exit. + base.Dispose(type); + } + + /// + /// Get Scrollbar style. + /// + /// The default scrollbar style. + /// 8 + protected override ViewStyle GetViewStyle() + { + return new ScrollBarStyle(); + } + + /// + /// Theme change callback when theme is changed, this callback will be trigger. + /// + /// The sender + /// The event data + /// 8 + protected override void OnThemeChangedEvent(object sender, StyleManager.ThemeChangeEventArgs e) + { + ScrollBarStyle tempStyle = StyleManager.Instance.GetViewStyle(style) as ScrollBarStyle; + if (tempStyle != null) + { + Style.CopyFrom(tempStyle); + UpdateValue(); + } + } + + private void Initialize() + { + this.Focusable = false; + + trackImage = new ImageView + { + Focusable = false, + WidthResizePolicy = ResizePolicyType.FillToParent, + HeightResizePolicy = ResizePolicyType.FillToParent, + PositionUsesPivotPoint = true, + ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft, + PivotPoint = Tizen.NUI.PivotPoint.CenterLeft + }; + this.Add(trackImage); + trackImage.ApplyStyle(Style.Track); + + thumbImage = new ImageView + { + Focusable = false, + WidthResizePolicy = ResizePolicyType.Fixed, + HeightResizePolicy = ResizePolicyType.Fixed, + PositionUsesPivotPoint = true, + ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft, + PivotPoint = Tizen.NUI.PivotPoint.CenterLeft + }; + this.Add(thumbImage); + thumbImage.ApplyStyle(Style.Thumb); + + scrollAniPlayer = new Animation(334); + scrollAniPlayer.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear); + + thumbImagePosX = 0; + thumbImagePosY = 0; + LayoutDirectionChanged += OnLayoutDirectionChanged; + } + + private void OnLayoutDirectionChanged(object sender, LayoutDirectionChangedEventArgs e) + { + RelayoutRequest(); + } + + private void UpdateValue() + { + if (minValue >= maxValue || curValue < minValue || curValue > maxValue) return; + + float width = (float)Size2D.Width; + float height = (float)Size2D.Height; + float thumbW = 0.0f; + float thumbH = 0.0f; + if (Style.Thumb.Size == null) + { + return; + } + else + { + thumbW = Style.Thumb.Size.Width; + thumbH = Style.Thumb.Size.Height; + } + float ratio = (float)(curValue - minValue) / (float)(maxValue - minValue); + + if (Style.Direction == DirectionType.Horizontal) + { + if (LayoutDirection == ViewLayoutDirectionType.RTL) + { + ratio = 1.0f - ratio; + } + + float posX = ratio * (width - thumbW); + float posY = (height - thumbH) / 2.0f; + + thumbImagePosX = posX; + if (null != scrollAniPlayer) + { + scrollAniPlayer.Stop(); + } + + if (!enableAni) + { + thumbImage.Position = new Position(posX, posY, 0); + } + else + { + if (null != scrollAniPlayer) + { + scrollAniPlayer.Clear(); + scrollAniPlayer.AnimateTo(thumbImage, "PositionX", posX); + scrollAniPlayer.Play(); + } + } + } + else + { + float posX = (width - thumbW) / 2.0f; + float posY = ratio * (height - thumbH); + + thumbImagePosY = posY; + if (null != scrollAniPlayer) + { + scrollAniPlayer.Stop(); + } + + if (!enableAni) + { + thumbImage.Position = new Position(posX, posY, 0); + } + else + { + if (null != scrollAniPlayer) + { + scrollAniPlayer.Clear(); + scrollAniPlayer.AnimateTo(thumbImage, "PositionY", posY); + scrollAniPlayer.Play(); + } + } + } + + if (enableAni) enableAni = false; + } + + private DirectionType CurrentDirection() + { + DirectionType dir = DirectionType.Horizontal; + if (null != Style.Direction) + { + dir = Style.Direction.Value; + } + return dir; + } + + private int CalculateCurrentValue(float offset, DirectionType dir) + { + if (dir == DirectionType.Horizontal) + { + thumbImagePosX += offset; + if (thumbImagePosX < 0) + { + thumbImage.PositionX = 0; + curValue = minValue; + } + else if (thumbImagePosX > Size2D.Width - thumbImage.Size2D.Width) + { + thumbImage.PositionX = Size2D.Width - thumbImage.Size2D.Width; + curValue = maxValue; + } + else + { + thumbImage.PositionX = thumbImagePosX; + curValue = (int)((thumbImagePosX / (float)(Size2D.Width - thumbImage.Size2D.Width)) * (float)(maxValue - minValue) + 0.5f); + } + } + else + { + thumbImagePosY += offset; + if (thumbImagePosY < 0) + { + thumbImage.PositionY = 0; + curValue = minValue; + } + else if (thumbImagePosY > Size2D.Height - thumbImage.Size2D.Height) + { + thumbImage.PositionY = Size2D.Height - thumbImage.Size2D.Height; + curValue = maxValue; + } + else + { + thumbImage.PositionY = thumbImagePosY; + curValue = (int)((thumbImagePosY / (float)(Size2D.Height - thumbImage.Size2D.Height)) * (float)(maxValue - minValue) + 0.5f); + } + } + + return curValue; + } + } +} diff --git a/src/Tizen.NUI.Components/Controls/Scrollbar.cs b/src/Tizen.NUI.Components/Controls/Scrollbar.cs old mode 100755 new mode 100644 index 64a2d49..7ebe33c --- a/src/Tizen.NUI.Components/Controls/Scrollbar.cs +++ b/src/Tizen.NUI.Components/Controls/Scrollbar.cs @@ -1,5 +1,5 @@ -/* - * Copyright(c) 2019 Samsung Electronics Co., Ltd. +/* + * Copyright(c) 2020 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. @@ -15,640 +15,561 @@ * */ using System; -using Tizen.NUI.BaseComponents; using System.ComponentModel; +using Tizen.NUI.BaseComponents; using Tizen.NUI.Binding; namespace Tizen.NUI.Components { /// - /// The ScrollBar class of nui component. It allows users to recognize the direction and the range of lists/content. . + /// The Scrollbar is a component that contains track and thumb to indicate the current scrolled position of a scrollable object. /// - /// 6 - public class ScrollBar : Control + [EditorBrowsable(EditorBrowsableState.Never)] + public class Scrollbar : ScrollbarBase { - /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + #region Fields + + /// Bindable property of TrackThickness [EditorBrowsable(EditorBrowsableState.Never)] - public static readonly BindableProperty DirectionProperty = BindableProperty.Create(nameof(Direction), typeof(DirectionType), typeof(ScrollBar), DirectionType.Horizontal, propertyChanged: (bindable, oldValue, newValue) => + public static readonly BindableProperty TrackThicknessProperty = BindableProperty.Create(nameof(TrackThickness), typeof(float), typeof(Scrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) => { - var instance = (ScrollBar)bindable; - if (newValue != null) - { - instance.Style.Direction = (DirectionType?)newValue; - instance.UpdateValue(); - } + var instance = ((Scrollbar)bindable); + var thickness = (float?)newValue; + + ((ScrollbarStyle)instance.viewStyle).TrackThickness = thickness; + instance.UpdateTrackThickness(thickness ?? 0); }, defaultValueCreator: (bindable) => { - var instance = (ScrollBar)bindable; - return instance.Style.Direction; + return ((ScrollbarStyle)((Scrollbar)bindable).viewStyle)?.TrackThickness ?? 0; }); - /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + + /// Bindable property of ThumbThickness [EditorBrowsable(EditorBrowsableState.Never)] - public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(int), typeof(ScrollBar), default(int), propertyChanged: (bindable, oldValue, newValue) => + public static readonly BindableProperty ThumbThicknessProperty = BindableProperty.Create(nameof(ThumbThickness), typeof(float), typeof(Scrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) => { - var instance = (ScrollBar)bindable; - if (newValue != null) - { - if ((int)newValue >= 0) - { - instance.maxValue = (int)newValue; - instance.UpdateValue(); - } - } + var instance = ((Scrollbar)bindable); + var thickness = (float?)newValue; + + ((ScrollbarStyle)instance.viewStyle).ThumbThickness = thickness; + instance.UpdateThumbThickness(thickness ?? 0); }, defaultValueCreator: (bindable) => { - var instance = (ScrollBar)bindable; - return instance.maxValue; + return ((ScrollbarStyle)((Scrollbar)bindable).viewStyle)?.ThumbThickness ?? 0; }); - /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + + /// Bindable property of TrackColor [EditorBrowsable(EditorBrowsableState.Never)] - public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(int), typeof(ScrollBar), default(int), propertyChanged: (bindable, oldValue, newValue) => + public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) => { - var instance = (ScrollBar)bindable; - if (newValue != null) - { - if ((int)newValue >= 0) - { - instance.minValue = (int)newValue; - instance.UpdateValue(); - } - } + var instance = ((Scrollbar)bindable); + var color = (Color)newValue; + + ((ScrollbarStyle)instance.viewStyle).TrackColor = color; + instance.UpdateTrackColor(color); }, defaultValueCreator: (bindable) => { - var instance = (ScrollBar)bindable; - return instance.minValue; + return ((ScrollbarStyle)((Scrollbar)bindable).viewStyle)?.TrackColor; }); - /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + + /// Bindable property of ThumbColor [EditorBrowsable(EditorBrowsableState.Never)] - public static readonly BindableProperty CurrentValueProperty = BindableProperty.Create(nameof(CurrentValue), typeof(int), typeof(ScrollBar), default(int), propertyChanged: (bindable, oldValue, newValue) => + public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) => { - var instance = (ScrollBar)bindable; - if (newValue != null) - { - if ((int)newValue >= 0) - { - instance.curValue = (int)newValue; - instance.UpdateValue(); - } - } + var instance = ((Scrollbar)bindable); + var color = (Color)newValue; + + ((ScrollbarStyle)instance.viewStyle).ThumbColor = color; + instance.UpdateThumbColor(color); }, defaultValueCreator: (bindable) => { - var instance = (ScrollBar)bindable; - return instance.curValue; + return ((ScrollbarStyle)((Scrollbar)bindable).viewStyle)?.ThumbColor; }); - /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + + /// Bindable property of TrackPadding [EditorBrowsable(EditorBrowsableState.Never)] - public static readonly BindableProperty DurationProperty = BindableProperty.Create(nameof(Duration), typeof(uint), typeof(ScrollBar), default(uint), propertyChanged: (bindable, oldValue, newValue) => + public static readonly BindableProperty TrackPaddingProperty = BindableProperty.Create(nameof(TrackPadding), typeof(Extents), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) => { - var instance = (ScrollBar)bindable; - if (newValue != null) - { - instance.Style.Duration = (uint)newValue; - if (instance.scrollAniPlayer != null) - { - instance.scrollAniPlayer.Duration = (int)(uint)newValue; - } - } + var instance = ((Scrollbar)bindable); + var trackPadding = (Extents)newValue; + + ((ScrollbarStyle)instance.viewStyle).TrackPadding = trackPadding; + instance.UpdateTrackPadding(trackPadding); }, defaultValueCreator: (bindable) => { - var instance = (ScrollBar)bindable; - return instance.Style.Duration; + return ((ScrollbarStyle)((Scrollbar)bindable).viewStyle)?.TrackPadding; }); - private ImageView trackImage; - private ImageView thumbImage; - private Animation scrollAniPlayer = null; - private float thumbImagePosX; - private float thumbImagePosY; - private bool enableAni = false; - private int minValue; - private int maxValue; - private int curValue; - static ScrollBar() { } + private static readonly string TrackVisualName = "Track"; + private static readonly string ThumbVisualName = "Thumb"; + private ColorVisual trackVisual; + private ColorVisual thumbVisual; + private Animation thumbPositionAnimation; + private Animation thumbSizeAnimation; + private Calculator calculator; + + #endregion Fields + + + #region Constructors /// - /// The constructor of ScrollBar. + /// Create an empty Scrollbar. /// - /// 6 - public ScrollBar() : base() + public Scrollbar() : base(new ScrollbarStyle()) { - Initialize(); } /// - /// The constructor of ScrollBar with specific style. + /// Create a Scrollbar and initialize with properties. /// - /// style name - /// 8 - public ScrollBar(string style) : base(style) + /// The total length of the content. + /// The size of View that contains the content to scroll. + /// The start position of the View in content length. This is the View's top position if the scroller is vertical, otherwise, View's left position. + /// Whether the direction of scrolling is horizontal or not. It is vertical if the value is false. + [EditorBrowsable(EditorBrowsableState.Never)] + public Scrollbar(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false) : base(new ScrollbarStyle()) { - Initialize(); + Initialize(contentLength, viewSize, currentPosition, isHorizontal); } /// - /// The constructor of ScrollBar with specific style. + /// Create an empty Scrollbar with a ScrollbarStyle instance to set style properties. /// - /// The style object to initialize the ScrollBar. - /// 8 - public ScrollBar(ScrollBarStyle scrollBarStyle) : base(scrollBarStyle) + [EditorBrowsable(EditorBrowsableState.Never)] + public Scrollbar(ScrollbarStyle style) : base(style) { - Initialize(); } /// - /// The direction type of the Scroll. + /// Static constructor to initialize bindable properties when loading. /// - /// 6 - public enum DirectionType - { - /// - /// The Horizontal type. - /// - /// 6 - Horizontal, - - /// - /// The Vertical type. - /// - /// 6 - Vertical + static Scrollbar() + { } - #region public property + #endregion Constructors + + + #region Properties + /// - /// Get style of scrollbar. + /// The thickness of the track. /// - /// 8 - public new ScrollBarStyle Style => ViewStyle as ScrollBarStyle; + [EditorBrowsable(EditorBrowsableState.Never)] + public float TrackThickness + { + get => (float)GetValue(TrackThicknessProperty); + set => SetValue(TrackThicknessProperty, value); + } /// - /// The property to get/set the direction of the ScrollBar. + /// The thickness of the thumb. /// - /// 6 - public DirectionType Direction + [EditorBrowsable(EditorBrowsableState.Never)] + public float ThumbThickness { - get - { - return (DirectionType)GetValue(DirectionProperty); - } - set - { - SetValue(DirectionProperty, value); - } + get => (float)GetValue(ThumbThicknessProperty); + set => SetValue(ThumbThicknessProperty, value); } /// - /// The property to get/set the size of the thumb object. + /// The color of the track part. /// - /// Throw when ThumbSize is null. - /// - /// - /// ScrollBar scroll; - /// try - /// { - /// scroll.ThumbSize = new Size(500, 10, 0); - /// } - /// catch(InvalidOperationException e) - /// { - /// Tizen.Log.Error(LogTag, "Failed to set ThumbSize value : " + e.Message); - /// } - /// - /// - /// 6 - public Size ThumbSize - { - get - { - return Style?.Thumb?.Size; - } - set - { - if (null != Style?.Thumb) - { - Style.Thumb.Size = value; - RelayoutRequest(); - } - } + [EditorBrowsable(EditorBrowsableState.Never)] + public Color TrackColor + { + get => (Color)GetValue(TrackColorProperty); + set => SetValue(TrackColorProperty, value); } /// - /// The property to get/set the image URL of the track object. + /// The color of the thumb part. /// - /// 6 - public string TrackImageURL + [EditorBrowsable(EditorBrowsableState.Never)] + public Color ThumbColor { - get - { - return Style?.Track?.ResourceUrl?.All; - } - set - { - if (null != Style?.Track) - { - Style.Track.ResourceUrl = value; - RelayoutRequest(); - } - } + get => (Color)GetValue(ThumbColorProperty); + set => SetValue(ThumbColorProperty, value); } /// - /// The property to get/set the color of the track object. + /// The padding value of the track. /// - /// 6 - public Color TrackColor + [EditorBrowsable(EditorBrowsableState.Never)] + public Extents TrackPadding + { + get => (Extents)GetValue(TrackPaddingProperty); + set => SetValue(TrackPaddingProperty, value); + } + + #endregion Properties + + + #region Methods + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Initialize(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false) { - get + if (isHorizontal) { - return Style?.Track?.BackgroundColor?.All; + calculator = new HorizontalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewSize.Width); } - set + else { - if (null != Style?.Track) - { - Style.Track.BackgroundColor = value; - RelayoutRequest(); - } + calculator = new VerticalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewSize.Height); } + + Size = viewSize; + + thumbPositionAnimation?.Stop(); + thumbPositionAnimation = null; + + thumbSizeAnimation?.Stop(); + thumbSizeAnimation = null; + + CreateTrackVisual(); + CreateThumbVisual(currentPosition); + + AddVisual(TrackVisualName, trackVisual); + AddVisual(ThumbVisualName, thumbVisual); } - /// - /// The property to get/set the color of the thumb object. - /// - /// 6 - public Color ThumbColor + /// + /// Please note that, for now, only alpha functions created with BuiltinFunctions are valid when animating. Otherwise, it will be treated as a linear alpha function. + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null) { - get + if (calculator == null) { - return Style?.Thumb?.BackgroundColor?.All; + return; } - set + + calculator.Update(contentLength > 0.0f ? contentLength : 0.0f); + + thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size); + thumbVisual.Position = calculator.CalculateThumbScrollPosition(position, trackVisual.Size, thumbVisual.Position, TrackPadding); + thumbVisual.Opacity = calculator.CalculateThumbVisibility() ? 1.0f : 0.0f; + + if (durationMs == 0) { - if(null != Style?.Thumb) - { - Style.Thumb.BackgroundColor = value; - RelayoutRequest(); - } + thumbVisual.UpdateVisual(true); + + return; } + + // TODO Support non built-in alpha function for visual trainsition in DALi. + AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default; + + thumbPositionAnimation?.Stop(); + thumbPositionAnimation = AnimateVisual(thumbVisual, "position", thumbVisual.Position, 0, (int)durationMs, builtinAlphaFunction); + thumbPositionAnimation.Play(); + + thumbSizeAnimation?.Stop(); + thumbSizeAnimation = AnimateVisual(thumbVisual, "size", thumbVisual.Size, 0, (int)durationMs, builtinAlphaFunction); + thumbSizeAnimation.Play(); } - /// - /// The property to get/set the max value of the ScrollBar. - /// - /// 6 - public int MaxValue + /// + /// Please note that, for now, only alpha functions created with BuiltinFunctions are valid when animating. Otherwise, it will be treated as a linear alpha function. + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null) { - get + if (thumbVisual == null) { - return (int)GetValue(MaxValueProperty); + return; } - set + + thumbVisual.Position = calculator.CalculateThumbScrollPosition(position, trackVisual.Size, thumbVisual.Position, TrackPadding); + + if (durationMs == 0) { - SetValue(MaxValueProperty, value); + thumbVisual.UpdateVisual(true); + + return; } + + // TODO Support non built-in alpha function for visual trainsition in DALi. + AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default; + + thumbPositionAnimation?.Stop(); + thumbPositionAnimation = AnimateVisual(thumbVisual, "position", thumbVisual.Position, 0, (int)durationMs, builtinAlphaFunction); + thumbPositionAnimation.Play(); + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override ViewStyle GetViewStyle() + { + return new ScrollbarStyle(); } /// - /// The property to get/set the min value of the ScrollBar. + /// Create a track visual. /// - /// 6 - public int MinValue + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void CreateTrackVisual() { - get + trackVisual = new ColorVisual { - return (int)GetValue(MinValueProperty); - } - set - { - SetValue(MinValueProperty, value); - } + SuppressUpdateVisual = true, + Color = Color.Black, + SizePolicy = VisualTransformPolicyType.Absolute, + Origin = calculator.CalculatorTrackAlign(), + AnchorPoint = calculator.CalculatorTrackAlign(), + Size = calculator.CalculateTrackSize(TrackThickness, TrackPadding), + Position = calculator.CalculateTrackPosition(TrackPadding), + }; } /// - /// The property to get/set the current value of the ScrollBar. + /// Create a thumb visual. /// - /// Throw when Current value is less than Min value, or greater than Max value. - /// - /// - /// ScrollBar scroll; - /// scroll.MaxValue = 100; - /// scroll.MinValue = 0; - /// try - /// { - /// scroll.CurrentValue = 50; - /// } - /// catch(ArgumentOutOfRangeException e) - /// { - /// Tizen.Log.Error(LogTag, "Failed to set Current value : " + e.Message); - /// } - /// - /// - /// 6 - public int CurrentValue - { - get - { - return (int)GetValue(CurrentValueProperty); - } - set - { - SetValue(CurrentValueProperty, value); - } + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void CreateThumbVisual(float currentPosition) + { + Size thumbSize = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size); + + thumbVisual = new ColorVisual + { + SuppressUpdateVisual = true, + MixColor = ThumbColor, + SizePolicy = VisualTransformPolicyType.Absolute, + Origin = calculator.CalculatorThumbAlign(), + AnchorPoint = calculator.CalculatorThumbAlign(), + Size = thumbSize, + Position = calculator.CalculateThumbPosition(currentPosition, trackVisual.Size, thumbSize, TrackPadding), + Opacity = (calculator.CalculateThumbVisibility() ? 1.0f : 0.0f), + }; } /// - /// Property to set/get animation duration. + /// Update TrackThickness property of the scrollbar. /// - /// 6 - public uint Duration + /// The width of the track. + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void UpdateTrackThickness(float thickness) { - get - { - return (uint)GetValue(DurationProperty); - } - set + if (trackVisual == null) { - SetValue(DurationProperty, value); + return; } - } - #endregion - /// - /// Method to set current value. The thumb object would move to the corresponding position with animation or not. - /// - /// The special current value. - /// Enable move with animation or not, the default value is true. - /// Throw when current size is less than the min value, or greater than the max value. - /// - /// - /// ScrollBar scroll; - /// scroll.MinValue = 0; - /// scroll.MaxValue = 100; - /// try - /// { - /// scroll.SetCurrentValue(50); - /// } - /// catch(ArgumentOutOfRangeException e) - /// { - /// Tizen.Log.Error(LogTag, "Failed to set current value : " + e.Message); - /// } - /// - /// - /// 6 - public void SetCurrentValue(int currentValue, bool enableAnimation = true) - { - if (currentValue < minValue || currentValue > maxValue) - { - //TNLog.E("Current value is less than the Min value, or greater than the Max value. currentValue = " + currentValue + ";"); -#pragma warning disable CA2208 // Instantiate argument exceptions correctly - throw new ArgumentOutOfRangeException("Wrong Current value. It shoud be greater than the Min value, and less than the Max value!"); -#pragma warning restore CA2208 // Instantiate argument exceptions correctly - } - - enableAni = enableAnimation; - CurrentValue = currentValue; + trackVisual.Size = calculator.CalculateTrackSize(thickness, TrackPadding); + trackVisual.UpdateVisual(true); } /// - /// Dispose ScrollBar. + /// Update ThumbThickness property of the scrollbar. /// - /// The DisposeTypes value. - /// 6 - protected override void Dispose(DisposeTypes type) + /// The width of the track. + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void UpdateThumbThickness(float thickness) { - if (disposed) + if (thumbVisual == null) { 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(thumbImage); + thumbVisual.Size = calculator.CalculateThumbSize(thickness, trackVisual.Size); + thumbVisual.UpdateVisual(true); + } - if (scrollAniPlayer != null) - { - scrollAniPlayer.Stop(); - scrollAniPlayer.Clear(); - scrollAniPlayer.Dispose(); - scrollAniPlayer = null; - } + /// + /// Update TrackColor property of the scrollbar. + /// + /// The color of the track. + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void UpdateTrackColor(Color color) + { + if (trackVisual == null) + { + return; } - //Release your own unmanaged resources here. - //You should not access any managed member here except static instance. - //because the execution order of Finalizes is non-deterministic. - //Unreference this from if a static instance refer to this. - - //You must call base.Dispose(type) just before exit. - base.Dispose(type); + trackVisual.MixColor = color; + trackVisual.UpdateVisual(true); } /// - /// Get Scrollbar style. + /// Update ThumbColor property of the scrollbar. /// - /// The default scrollbar style. - /// 8 - protected override ViewStyle GetViewStyle() + /// The color of the thumb. + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void UpdateThumbColor(Color color) { - return new ScrollBarStyle(); + if (thumbVisual == null) + { + return; + } + + thumbVisual.MixColor = color; + thumbVisual.UpdateVisual(true); } /// - /// Theme change callback when theme is changed, this callback will be trigger. + /// Update TrackPadding property of the scrollbar. /// - /// The sender - /// The event data - /// 8 - protected override void OnThemeChangedEvent(object sender, StyleManager.ThemeChangeEventArgs e) + /// The padding of the track. + protected virtual void UpdateTrackPadding(Extents trackPadding) { - ScrollBarStyle tempStyle = StyleManager.Instance.GetViewStyle(style) as ScrollBarStyle; - if (tempStyle != null) + if (trackVisual == null || thumbVisual == null) { - Style.CopyFrom(tempStyle); - UpdateValue(); + return; } + + trackVisual.Size = calculator.CalculateTrackSize(TrackThickness, trackPadding); + trackVisual.Position = calculator.CalculateTrackPosition(trackPadding); + thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size); + thumbVisual.Position = calculator.CalculateThumbPaddingPosition(trackVisual.Size, thumbVisual.Size, thumbVisual.Position, trackPadding); + + trackVisual.UpdateVisual(true); + thumbVisual.UpdateVisual(true); } - private void Initialize() + #endregion Methods + + + #region Classes + + private abstract class Calculator { - this.Focusable = false; + protected float contentLength; + protected float visibleLength; - trackImage = new ImageView + public Calculator(float contentLength, float visibleLength) { - Focusable = false, - WidthResizePolicy = ResizePolicyType.FillToParent, - HeightResizePolicy = ResizePolicyType.FillToParent, - PositionUsesPivotPoint = true, - ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft, - PivotPoint = Tizen.NUI.PivotPoint.CenterLeft - }; - this.Add(trackImage); - trackImage.ApplyStyle(Style.Track); + this.contentLength = contentLength; + this.visibleLength = visibleLength; + } - thumbImage = new ImageView + public void Update(float contentLength) { - Focusable = false, - WidthResizePolicy = ResizePolicyType.Fixed, - HeightResizePolicy = ResizePolicyType.Fixed, - PositionUsesPivotPoint = true, - ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft, - PivotPoint = Tizen.NUI.PivotPoint.CenterLeft - }; - this.Add(thumbImage); - thumbImage.ApplyStyle(Style.Thumb); + this.contentLength = contentLength; + } - scrollAniPlayer = new Animation(334); - scrollAniPlayer.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear); + public bool CalculateThumbVisibility() + { + return contentLength > visibleLength; + } - thumbImagePosX = 0; - thumbImagePosY = 0; - LayoutDirectionChanged += OnLayoutDirectionChanged; + public abstract Visual.AlignType CalculatorTrackAlign(); + public abstract Visual.AlignType CalculatorThumbAlign(); + public abstract Size CalculateTrackSize(float thickness, Extents trackPadding); + public abstract Vector2 CalculateTrackPosition(Extents trackPadding); + public abstract Size CalculateThumbSize(float thickness, Size trackSize); + public abstract Vector2 CalculateThumbPosition(float position, Size trackSize, Size thumbSize, Extents trackPadding); + public abstract Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding); + public abstract Vector2 CalculateThumbScrollPosition(float position, Size trackSize, Vector2 thumbCurrentPosition, Extents trackPadding); } - private void OnLayoutDirectionChanged(object sender, LayoutDirectionChangedEventArgs e) + private class HorizontalCalculator : Calculator { - RelayoutRequest(); - } + public HorizontalCalculator(float contentLength, float visibleLength) : base(contentLength, visibleLength) + { + } - private void UpdateValue() - { - if (minValue >= maxValue || curValue < minValue || curValue > maxValue) return; + public override Visual.AlignType CalculatorTrackAlign() + { + return Visual.AlignType.BottomCenter; + } - float width = (float)Size2D.Width; - float height = (float)Size2D.Height; - float thumbW = 0.0f; - float thumbH = 0.0f; - if (Style.Thumb.Size == null) + public override Visual.AlignType CalculatorThumbAlign() { - return; + return Visual.AlignType.BottomBegin; } - else + + public override Size CalculateTrackSize(float thickness, Extents trackPadding) { - thumbW = Style.Thumb.Size.Width; - thumbH = Style.Thumb.Size.Height; - } - float ratio = (float)(curValue - minValue) / (float)(maxValue - minValue); - - if (Style.Direction == DirectionType.Horizontal) - { - if (LayoutDirection == ViewLayoutDirectionType.RTL) - { - ratio = 1.0f - ratio; - } - - float posX = ratio * (width - thumbW); - float posY = (height - thumbH) / 2.0f; - - thumbImagePosX = posX; - if (null != scrollAniPlayer) - { - scrollAniPlayer.Stop(); - } - - if (!enableAni) - { - thumbImage.Position = new Position(posX, posY, 0); - } - else - { - if (null != scrollAniPlayer) - { - scrollAniPlayer.Clear(); - scrollAniPlayer.AnimateTo(thumbImage, "PositionX", posX); - scrollAniPlayer.Play(); - } - } + return new Size(visibleLength - trackPadding.Start - trackPadding.End, thickness); } - else + + public override Vector2 CalculateTrackPosition(Extents trackPadding) + { + return new Vector2(0, -trackPadding.Bottom); + } + + public override Size CalculateThumbSize(float thickness, Size trackSize) { - float posX = (width - thumbW) / 2.0f; - float posY = ratio * (height - thumbH); - - thumbImagePosY = posY; - if (null != scrollAniPlayer) - { - scrollAniPlayer.Stop(); - } - - if (!enableAni) - { - thumbImage.Position = new Position(posX, posY, 0); - } - else - { - if (null != scrollAniPlayer) - { - scrollAniPlayer.Clear(); - scrollAniPlayer.AnimateTo(thumbImage, "PositionY", posY); - scrollAniPlayer.Play(); - } - } - } - - if (enableAni) enableAni = false; + return new Size(trackSize.Width * visibleLength / contentLength, thickness); + } + + public override Vector2 CalculateThumbPosition(float position, Size trackSize, Size thumbSize, Extents trackPadding) + { + float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding.Bottom; + float pos = Math.Min(Math.Max(position, 0.0f), contentLength - visibleLength); + return new Vector2(trackPadding.Start + trackSize.Width * pos / contentLength, -padding); + } + + public override Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding) + { + float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding.Bottom; + return new Vector2(thumbCurrentPosition.X, -padding); + } + + public override Vector2 CalculateThumbScrollPosition(float position, Size trackSize, Vector2 thumbCurrentPosition, Extents trackPadding) + { + float pos = Math.Min(Math.Max(position, 0.0f), contentLength - visibleLength); + return new Vector2(trackPadding.Start + trackSize.Width * pos / contentLength, thumbCurrentPosition.Y); + } } - private DirectionType CurrentDirection() + private class VerticalCalculator : Calculator { - DirectionType dir = DirectionType.Horizontal; - if (null != Style.Direction) + public VerticalCalculator(float contentLength, float visibleLength) : base(contentLength, visibleLength) { - dir = Style.Direction.Value; } - return dir; - } - private int CalculateCurrentValue(float offset, DirectionType dir) - { - if (dir == DirectionType.Horizontal) - { - thumbImagePosX += offset; - if (thumbImagePosX < 0) - { - thumbImage.PositionX = 0; - curValue = minValue; - } - else if (thumbImagePosX > Size2D.Width - thumbImage.Size2D.Width) - { - thumbImage.PositionX = Size2D.Width - thumbImage.Size2D.Width; - curValue = maxValue; - } - else - { - thumbImage.PositionX = thumbImagePosX; - curValue = (int)((thumbImagePosX / (float)(Size2D.Width - thumbImage.Size2D.Width)) * (float)(maxValue - minValue) + 0.5f); - } + public override Visual.AlignType CalculatorTrackAlign() + { + return Visual.AlignType.CenterEnd; } - else + + public override Visual.AlignType CalculatorThumbAlign() + { + return Visual.AlignType.TopEnd; + } + + public override Size CalculateTrackSize(float thickness, Extents trackPadding) + { + return new Size(thickness, visibleLength - trackPadding.Top - trackPadding.Bottom); + } + + public override Vector2 CalculateTrackPosition(Extents trackPadding) + { + return new Vector2(-trackPadding.End, 0); + } + + public override Size CalculateThumbSize(float thickness, Size trackSize) { - thumbImagePosY += offset; - if (thumbImagePosY < 0) - { - thumbImage.PositionY = 0; - curValue = minValue; - } - else if (thumbImagePosY > Size2D.Height - thumbImage.Size2D.Height) - { - thumbImage.PositionY = Size2D.Height - thumbImage.Size2D.Height; - curValue = maxValue; - } - else - { - thumbImage.PositionY = thumbImagePosY; - curValue = (int)((thumbImagePosY / (float)(Size2D.Height - thumbImage.Size2D.Height)) * (float)(maxValue - minValue) + 0.5f); - } - } - - return curValue; + return new Size(thickness, trackSize.Height * visibleLength / contentLength); + } + + public override Vector2 CalculateThumbPosition(float position, Size trackSize, Size thumbSize, Extents trackPadding) + { + float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding.End; + float pos = Math.Min(Math.Max(position, 0.0f), contentLength - visibleLength); + return new Vector2(-padding, trackPadding.Top + trackSize.Height * pos / contentLength); + } + + public override Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding) + { + float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding.End; + return new Vector2(-padding, thumbCurrentPosition.Y); + } + + public override Vector2 CalculateThumbScrollPosition(float position, Size trackSize, Vector2 thumbPosition, Extents trackPadding) + { + float pos = Math.Min(Math.Max(position, 0.0f), contentLength - visibleLength); + return new Vector2(thumbPosition.X, trackPadding.Top + trackSize.Height * pos / contentLength); + } } + + #endregion Classes } } diff --git a/src/Tizen.NUI.Components/Controls/ScrollbarBase.cs b/src/Tizen.NUI.Components/Controls/ScrollbarBase.cs new file mode 100644 index 0000000..65b278a --- /dev/null +++ b/src/Tizen.NUI.Components/Controls/ScrollbarBase.cs @@ -0,0 +1,89 @@ +/* + * Copyright(c) 2020 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.ComponentModel; + +namespace Tizen.NUI.Components +{ + /// + /// The ScrollbarBase is an abstract class that can be linked to the scrollable objects + /// indicating the current scrolled position of the scrollable object. + /// This only contains non-graphical functionalities of basic scrollbar. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public abstract class ScrollbarBase : Control + { + #region Constructors + + /// + /// Create an empty ScrollbarBase. + /// + public ScrollbarBase() : base() + { + } + + /// + /// Create an empty Scrollbar with a ControlStyle instance to set style properties. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ScrollbarBase(ControlStyle style) : base(style) + { + } + + /// + /// Static constructor to initialize bindable properties when loading. + /// + static ScrollbarBase() + { + } + + #endregion Constructors + + + #region Methods + + /// + /// Update content length and position at once. + /// + /// The total length of the content. + /// The destination position of the View in content length. This is the View's top position if the scroller is vertical, otherwise, View's left position. + /// The time it takes to scroll in milliseconds. + /// The timing function used in animation. It describes the rate of change of the animation parameter over time. (e.g. EaseOut) + [EditorBrowsable(EditorBrowsableState.Never)] + public abstract void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null); + + /// + /// Scroll content to a specific position. + /// + /// The destination to scroll. + /// The time it takes to scroll in milliseconds. + /// The timing function used in animation. It describes the rate of change of the animation parameter over time. (e.g. EaseOut) + [EditorBrowsable(EditorBrowsableState.Never)] + public abstract void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null); + + /// + /// Initialize the scroll bar. + /// + /// The total length of the content. + /// The size of the View that contains the content to scroll. The ScrollbarBase will have the same size as the given viewSize. + /// The start position of the View in content length. This is the View's top position if the scroller is vertical, otherwise, View's left position. + /// Whether the direction of scrolling is horizontal or not. It is vertical if the value is false. + [EditorBrowsable(EditorBrowsableState.Never)] + public abstract void Initialize(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false); + + #endregion Methods + } +} diff --git a/src/Tizen.NUI.Components/Style/ImageScrollBarStyle.cs b/src/Tizen.NUI.Components/Style/ImageScrollBarStyle.cs new file mode 100755 index 0000000..234ce25 --- /dev/null +++ b/src/Tizen.NUI.Components/Style/ImageScrollBarStyle.cs @@ -0,0 +1,131 @@ +/* + * Copyright(c) 2019 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.ComponentModel; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Components +{ + /// + /// ScrollBarStyle is a class which saves Scrollbar's ux data. + /// + /// + /// Please note that this class will be replaced with ScrollbarStyle class in the near future. + /// + /// 8 + public class ScrollBarStyle : ControlStyle + { + static ScrollBarStyle() { } + + /// + /// Creates a new instance of a ScrollBarStyle. + /// + /// 8 + public ScrollBarStyle() : base() + { + InitSubStyle(); + Direction = ScrollBar.DirectionType.Horizontal; + } + + /// + /// Creates a new instance of a ScrollBarStyle with style. + /// + /// Create ScrollBarStyle by style customized by user. + /// 8 + public ScrollBarStyle(ScrollBarStyle style) : base(style) + { + if (null == style) return; + + InitSubStyle(); + + this.CopyFrom(style); + } + + /// + /// Get or set track image style. + /// + /// 8 + public ImageViewStyle Track { get; set; } + + /// + /// Get or set thumb image style. + /// + /// 8 + public ImageViewStyle Thumb { get; set; } + + /// + /// Get or set direction type. + /// + /// 8 + public ScrollBar.DirectionType? Direction { get; set; } + + /// + /// Get or set duration. + /// + /// 8 + public uint Duration { get; set; } + + /// + /// Style's clone function. + /// + /// The style that need to copy. + /// 8 + public override void CopyFrom(BindableObject bindableObject) + { + base.CopyFrom(bindableObject); + + ScrollBarStyle scrollBarStyle = bindableObject as ScrollBarStyle; + + if (null != scrollBarStyle) + { + if (null != scrollBarStyle.Track) + { + Track?.CopyFrom(scrollBarStyle.Track); + } + + if (null != scrollBarStyle.Thumb) + { + Thumb?.CopyFrom(scrollBarStyle.Thumb); + } + + Direction = scrollBarStyle.Direction; + Duration = scrollBarStyle.Duration; + } + } + + private void InitSubStyle() + { + Track = new ImageViewStyle() + { + PositionUsesPivotPoint = true, + ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft, + PivotPoint = Tizen.NUI.PivotPoint.CenterLeft, + WidthResizePolicy = ResizePolicyType.FillToParent, + HeightResizePolicy = ResizePolicyType.FillToParent + }; + + Thumb = new ImageViewStyle() + { + PositionUsesPivotPoint = true, + ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft, + PivotPoint = Tizen.NUI.PivotPoint.CenterLeft, + WidthResizePolicy = ResizePolicyType.Fixed, + HeightResizePolicy = ResizePolicyType.Fixed + }; + } + } +} diff --git a/src/Tizen.NUI.Components/Style/ScrollbarStyle.cs b/src/Tizen.NUI.Components/Style/ScrollbarStyle.cs old mode 100755 new mode 100644 index 461f00c..c707f9b --- a/src/Tizen.NUI.Components/Style/ScrollbarStyle.cs +++ b/src/Tizen.NUI.Components/Style/ScrollbarStyle.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright(c) 2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,114 +15,197 @@ * */ using System.ComponentModel; -using Tizen.NUI.BaseComponents; using Tizen.NUI.Binding; namespace Tizen.NUI.Components { /// - /// ScrollBarStyle is a class which saves Scrollbar's ux data. + /// ScrollbarStyle is a class which saves Scrollbar's style data. /// - /// 8 - public class ScrollBarStyle : ControlStyle + [EditorBrowsable(EditorBrowsableState.Never)] + public class ScrollbarStyle : ControlStyle { - static ScrollBarStyle() { } + #region Fields + + /// Bindable property of TrackThickness + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty TrackThicknessProperty = BindableProperty.Create(nameof(TrackThickness), typeof(float?), typeof(ScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((ScrollbarStyle)bindable).trackThickness = (float?)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((ScrollbarStyle)bindable).trackThickness; + }); + + /// Bindable property of ThumbThickness + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty ThumbThicknessProperty = BindableProperty.Create(nameof(ThumbThickness), typeof(float?), typeof(ScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((ScrollbarStyle)bindable).thumbThickness = (float?)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((ScrollbarStyle)bindable).thumbThickness; + }); + + /// Bindable property of TrackColor + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(ScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((ScrollbarStyle)bindable).trackColor = (Color)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((ScrollbarStyle)bindable).trackColor; + }); + + /// Bindable property of ThumbColor + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(ScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((ScrollbarStyle)bindable).thumbColor = (Color)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((ScrollbarStyle)bindable).thumbColor; + }); + + /// Bindable property of TrackPadding + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty TrackPaddingProperty = BindableProperty.Create(nameof(TrackPadding), typeof(Extents), typeof(ScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((ScrollbarStyle)bindable).trackPadding = (Extents)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((ScrollbarStyle)bindable).trackPadding; + }); + + private float? trackThickness; + private float? thumbThickness; + private Color trackColor; + private Color thumbColor; + private Extents trackPadding; + + #endregion Fields + + + #region Constructors /// - /// Creates a new instance of a ScrollBarStyle. + /// Creates a new instance of a ScrollbarStyle. /// - /// 8 - public ScrollBarStyle() : base() + [EditorBrowsable(EditorBrowsableState.Never)] + public ScrollbarStyle() : base() { - InitSubStyle(); - Direction = ScrollBar.DirectionType.Horizontal; + Initialize(); } /// - /// Creates a new instance of a ScrollBarStyle with style. + /// Copy constructor. /// - /// Create ScrollBarStyle by style customized by user. - /// 8 - public ScrollBarStyle(ScrollBarStyle style) : base(style) + /// Create ScrollbarStyle by style customized by user. + [EditorBrowsable(EditorBrowsableState.Never)] + public ScrollbarStyle(ScrollbarStyle style) : base(style) { - if (null == style) return; - - InitSubStyle(); - this.CopyFrom(style); } /// - /// Get or set track image style. + /// Static constructor to initialize bindable properties when loading. + /// + static ScrollbarStyle() + { + } + + #endregion Constructors + + + #region Properties + + /// + /// The thickness of the track. /// - /// 8 - public ImageViewStyle Track { get; set; } + [EditorBrowsable(EditorBrowsableState.Never)] + public float? TrackThickness + { + get => (float?)GetValue(TrackThicknessProperty); + set => SetValue(TrackThicknessProperty, value); + } /// - /// Get or set thumb image style. + /// The thickness of the thumb. /// - /// 8 - public ImageViewStyle Thumb { get; set; } + [EditorBrowsable(EditorBrowsableState.Never)] + public float? ThumbThickness + { + get => (float?)GetValue(ThumbThicknessProperty); + set => SetValue(ThumbThicknessProperty, value); + } /// - /// Get or set direction type. + /// The color of the track part. /// - /// 8 - public ScrollBar.DirectionType? Direction { get; set; } + [EditorBrowsable(EditorBrowsableState.Never)] + public Color TrackColor + { + get => (Color)GetValue(TrackColorProperty); + set => SetValue(TrackColorProperty, value); + } /// - /// Get or set duration. + /// The color of the thumb part. /// - /// 8 - public uint Duration { get; set; } + [EditorBrowsable(EditorBrowsableState.Never)] + public Color ThumbColor + { + get => (Color)GetValue(ThumbColorProperty); + set => SetValue(ThumbColorProperty, value); + } /// - /// Style's clone function. + /// The padding value of the track. /// - /// The style that need to copy. - /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public Extents TrackPadding + { + get => (Extents)GetValue(TrackPaddingProperty); + set => SetValue(TrackPaddingProperty, value); + } + + #endregion Properties + + + #region Methods + + /// + [EditorBrowsable(EditorBrowsableState.Never)] public override void CopyFrom(BindableObject bindableObject) { base.CopyFrom(bindableObject); - ScrollBarStyle scrollBarStyle = bindableObject as ScrollBarStyle; + var style = bindableObject as ScrollbarStyle; - if (null != scrollBarStyle) + if (null != style) { - if (null != scrollBarStyle.Track) - { - Track?.CopyFrom(scrollBarStyle.Track); - } - - if (null != scrollBarStyle.Thumb) - { - Thumb?.CopyFrom(scrollBarStyle.Thumb); - } - - Direction = scrollBarStyle.Direction; - Duration = scrollBarStyle.Duration; + TrackThickness = style.TrackThickness; + ThumbThickness = style.ThumbThickness; + TrackColor = style.TrackColor; + ThumbColor = style.ThumbColor; + TrackPadding = style.TrackPadding; } } - private void InitSubStyle() + private void Initialize() { - Track = new ImageViewStyle() - { - PositionUsesPivotPoint = true, - ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft, - PivotPoint = Tizen.NUI.PivotPoint.CenterLeft, - WidthResizePolicy = ResizePolicyType.FillToParent, - HeightResizePolicy = ResizePolicyType.FillToParent - }; - - Thumb = new ImageViewStyle() - { - PositionUsesPivotPoint = true, - ParentOrigin = Tizen.NUI.ParentOrigin.CenterLeft, - PivotPoint = Tizen.NUI.PivotPoint.CenterLeft, - WidthResizePolicy = ResizePolicyType.Fixed, - HeightResizePolicy = ResizePolicyType.Fixed - }; + TrackThickness = 6.0f; + ThumbThickness = 6.0f; + TrackColor = new Color(1.0f, 1.0f, 1.0f, 0.15f); + ThumbColor = new Color(0.6f, 0.6f, 0.6f, 1.0f); + TrackPadding = 4; } + + #endregion Methods } } diff --git a/src/Tizen.NUI.Wearable/src/public/CircularScrollbar.cs b/src/Tizen.NUI.Wearable/src/public/CircularScrollbar.cs new file mode 100644 index 0000000..17ed39f --- /dev/null +++ b/src/Tizen.NUI.Wearable/src/public/CircularScrollbar.cs @@ -0,0 +1,412 @@ +/* + * Copyright(c) 2020 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.BaseComponents; +using Tizen.NUI.Binding; +using Tizen.NUI.Components; + +namespace Tizen.NUI.Wearable +{ + /// + /// The CircualrScrollbar is a wearable NUI component that can be linked to the scrollable objects + /// indicating the current scroll position of the scrollable object.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)] + public class CircularScrollbar : ScrollbarBase + { + #region Fields + + /// Bindable property of Thickness + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty ThicknessProperty = BindableProperty.Create(nameof(Thickness), typeof(float), typeof(CircularScrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) => + { + var instance = ((CircularScrollbar)bindable); + var thickness = (float?)newValue; + + ((CircularScrollbarStyle)instance.viewStyle).Thickness = thickness; + instance.UpdateVisualThickness(thickness ?? 0); + }, + defaultValueCreator: (bindable) => + { + return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.Thickness ?? 0; + }); + + /// Bindable property of TrackSweepAngle + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty TrackSweepAngleProperty = BindableProperty.Create(nameof(TrackSweepAngle), typeof(float), typeof(CircularScrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) => + { + var instance = ((CircularScrollbar)bindable); + var angle = (float?)newValue; + + ((CircularScrollbarStyle)instance.viewStyle).TrackSweepAngle = angle; + instance.UpdateTrackVisualSweepAngle(angle ?? 0); + }, + defaultValueCreator: (bindable) => + { + return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.TrackSweepAngle ?? 0; + }); + + /// Bindable property of TrackColor + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) => + { + var instance = ((CircularScrollbar)bindable); + var color = (Color)newValue; + + ((CircularScrollbarStyle)instance.viewStyle).TrackColor = color; + instance.UpdateTrackVisualColor(color); + }, + defaultValueCreator: (bindable) => + { + return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.TrackColor; + }); + + /// Bindable property of ThumbColor + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) => + { + var instance = ((CircularScrollbar)bindable); + var color = (Color)newValue; + + ((CircularScrollbarStyle)instance.viewStyle).ThumbColor = color; + instance.UpdateThumbVisualColor(color); + }, + defaultValueCreator: (bindable) => + { + return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.ThumbColor; + }); + + private ArcVisual trackVisual; + private ArcVisual thumbVisual; + private float contentLength; + private float visibleLength; + private float currentPosition; + private float directionAlpha; + private Animation thumbStartAngleAnimation; + private Animation thumbSweepAngleAnimation; + + #endregion Fields + + + #region Constructors + + /// + /// Create an empty CircularScrollbar. + /// + public CircularScrollbar() : base(new CircularScrollbarStyle()) + { + } + + /// + /// Create a CircularScrollbar and initialize with properties. + /// + /// The total length of the content. + /// The size of View that contains the content to scroll. + /// Scrolled position. + /// Whether the direction of scrolling is horizontal or not. It is vertical if the value is false. + [EditorBrowsable(EditorBrowsableState.Never)] + public CircularScrollbar(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false) : base(new CircularScrollbarStyle()) + { + Initialize(contentLength, viewSize, currentPosition, isHorizontal); + } + + /// + /// Create an empty CircularScrollbar with a CircularScrollbarStyle instance to set style properties. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public CircularScrollbar(CircularScrollbarStyle style) : base(style) + { + } + + /// + /// Static constructor to initialize bindable properties when loading. + /// + static CircularScrollbar() + { + } + + #endregion Constructors + + + #region Properties + + /// + /// The thickness of the scrollbar and track. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public float Thickness + { + get => (float)GetValue(ThicknessProperty); + set => SetValue(ThicknessProperty, value); + } + + /// + /// The sweep angle of track area in degrees. + /// + /// + /// Values below 6 degrees are treated as 6 degrees. + /// Values exceeding 180 degrees are treated as 180 degrees. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public float TrackSweepAngle + { + get => (float)GetValue(TrackSweepAngleProperty); + set => SetValue(TrackSweepAngleProperty, value); + } + + /// + /// The color of the track part. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Color TrackColor + { + get => (Color)GetValue(TrackColorProperty); + set => SetValue(TrackColorProperty, value); + } + + /// + /// The color of the thumb part. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Color ThumbColor + { + get => (Color)GetValue(ThumbColorProperty); + set => SetValue(ThumbColorProperty, value); + } + + #endregion Properties + + + #region Methods + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Initialize(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false) + { + this.contentLength = contentLength > 0.0f ? contentLength : 0.0f; + this.visibleLength = isHorizontal ? viewSize.Width : viewSize.Height; + this.currentPosition = currentPosition; + this.directionAlpha = isHorizontal ? 270.0f : 0.0f; + + Size = viewSize; + + thumbStartAngleAnimation?.Stop(); + thumbStartAngleAnimation = null; + + thumbSweepAngleAnimation?.Stop(); + thumbSweepAngleAnimation = null; + + CreateTrackVisual(); + CreateThumbVisual(currentPosition); + + AddVisual("Track", trackVisual); + AddVisual("Thumb", thumbVisual); + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null) + { + this.currentPosition = position; + this.contentLength = contentLength > 0.0f ? contentLength : 0.0f; + + if (thumbVisual == null) + { + return; + } + + thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle); + thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle); + thumbVisual.Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f; + + if (durationMs == 0) + { + thumbVisual.UpdateVisual(true); + + return; + } + + // TODO Support non built-in alpha function for visual trainsition in DALi. + AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default; + + thumbStartAngleAnimation?.Stop(); + thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction); + thumbStartAngleAnimation.Play(); + + thumbSweepAngleAnimation?.Stop(); + thumbSweepAngleAnimation = AnimateVisual(thumbVisual, "sweepAngle", thumbVisual.SweepAngle, 0, (int)durationMs, builtinAlphaFunction); + thumbSweepAngleAnimation.Play(); + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null) + { + currentPosition = position; + + if (thumbVisual == null) + { + return; + } + + var oldThumbStartAngle = thumbVisual.StartAngle; + + thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle); + + if (durationMs == 0) + { + thumbVisual.UpdateVisual(true); + + return; + } + + // TODO Support non built-in alpha function for visual trainsition in DALi. + AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default; + + thumbStartAngleAnimation?.Stop(); + thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction); + thumbStartAngleAnimation.Play(); + } + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override ViewStyle GetViewStyle() + { + return new CircularScrollbarStyle(); + } + + private void CreateTrackVisual() + { + float sweepAngle = CalculateTrackSweepAngle(TrackSweepAngle); + + trackVisual = new ArcVisual + { + SuppressUpdateVisual = true, + Thickness = this.Thickness, + Cap = ArcVisual.CapType.Round, + MixColor = TrackColor, + Size = new Size(visibleLength - 2, visibleLength - 2), + SizePolicy = VisualTransformPolicyType.Absolute, + SweepAngle = sweepAngle, + StartAngle = CalculateTrackStartAngle(sweepAngle), + }; + } + + private void CreateThumbVisual(float position) + { + float sweepAngle = CalculateThumbSweepAngle(TrackSweepAngle); + + thumbVisual = new ArcVisual + { + SuppressUpdateVisual = true, + Thickness = trackVisual.Thickness, + Cap = ArcVisual.CapType.Round, + MixColor = ThumbColor, + Size = new Size(visibleLength - 2, visibleLength - 2), + SizePolicy = VisualTransformPolicyType.Absolute, + SweepAngle = sweepAngle, + StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, sweepAngle), + Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f, + }; + } + + private float CalculateTrackStartAngle(float currentTrackSweepAngle) + { + return ((180.0f - currentTrackSweepAngle) / 2.0f) + directionAlpha; + } + + private float CalculateTrackSweepAngle(float inputTrackSweepAngle) + { + return Math.Min(Math.Max(inputTrackSweepAngle, 3), 180); + } + + private float CalculateThumbStartAngle(float position, float trackStartAngle, float trackSweepAngle, float thumbSweepAngle) + { + float minAngle = trackStartAngle; + float maxAngle = trackStartAngle + trackSweepAngle - thumbSweepAngle; + float resultAngle = trackStartAngle + (trackSweepAngle * (position < 0.0f ? 0.0f : position) / contentLength); + + return Math.Min(Math.Max(resultAngle, minAngle), maxAngle); + } + + private float CalculateThumbSweepAngle(float trackSweepAngle) + { + return trackSweepAngle * visibleLength / contentLength; + } + + private bool CalculateThumbVisibility() + { + return contentLength > visibleLength; + } + + private void UpdateVisualThickness(float thickness) + { + if (trackVisual == null) + { + return; + } + + trackVisual.Thickness = thickness; + thumbVisual.Thickness = thickness; + + trackVisual.UpdateVisual(true); + thumbVisual.UpdateVisual(true); + } + + private void UpdateTrackVisualSweepAngle(float trackSweepAngle) + { + if (trackVisual == null || thumbVisual == null) + { + return; + } + + trackVisual.SweepAngle = CalculateTrackSweepAngle(trackSweepAngle); + trackVisual.StartAngle = CalculateTrackStartAngle(trackVisual.SweepAngle); + + thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle); + thumbVisual.StartAngle = CalculateThumbStartAngle(currentPosition, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle); + + trackVisual.UpdateVisual(true); + thumbVisual.UpdateVisual(true); + } + + private void UpdateTrackVisualColor(Color trackColor) + { + if (trackVisual == null) + { + return; + } + + trackVisual.MixColor = trackColor; + trackVisual.UpdateVisual(true); + } + + private void UpdateThumbVisualColor(Color thumbColor) + { + if (thumbVisual == null) + { + return; + } + + thumbVisual.MixColor = thumbColor; + thumbVisual.UpdateVisual(true); + } + + #endregion Methods + } +} diff --git a/src/Tizen.NUI.Wearable/src/public/WearableStyle/CircularScrollbarStyle.cs b/src/Tizen.NUI.Wearable/src/public/WearableStyle/CircularScrollbarStyle.cs new file mode 100644 index 0000000..dae9f94 --- /dev/null +++ b/src/Tizen.NUI.Wearable/src/public/WearableStyle/CircularScrollbarStyle.cs @@ -0,0 +1,192 @@ +/* + * Copyright(c) 2019 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.ComponentModel; +using Tizen.NUI.Components; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Wearable +{ + /// + /// CircularScrollbarStyle is a class which saves CircularScrollbar's ux data. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public class CircularScrollbarStyle : ControlStyle + { + #region Fields + + /// Bindable property of Thickness + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty ThicknessProperty = BindableProperty.Create(nameof(Thickness), typeof(float?), typeof(CircularScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((CircularScrollbarStyle)bindable).thickness = (float?)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((CircularScrollbarStyle)bindable).thickness; + }); + + /// Bindable property of TrackSweepAngle + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty TrackSweepAngleProperty = BindableProperty.Create(nameof(TrackSweepAngle), typeof(float?), typeof(CircularScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((CircularScrollbarStyle)bindable).trackSweepAngle = (float?)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((CircularScrollbarStyle)bindable).trackSweepAngle; + }); + + /// Bindable property of TrackColor + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(CircularScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((CircularScrollbarStyle)bindable).trackColor = (Color)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((CircularScrollbarStyle)bindable).trackColor; + }); + + /// Bindable property of ThumbColor + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(CircularScrollbarStyle), null, propertyChanged: (bindable, oldValue, newValue) => + { + ((CircularScrollbarStyle)bindable).thumbColor = (Color)newValue; + }, + defaultValueCreator: (bindable) => + { + return ((CircularScrollbarStyle)bindable).thumbColor; + }); + + private float? thickness; + private float? trackSweepAngle; + private Color trackColor; + private Color thumbColor; + + #endregion Fields + + + #region Constructors + + /// + /// Creates a new instance of a CircularScrollbarStyle. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public CircularScrollbarStyle() : base() + { + Initialize(); + } + + /// + /// Copy constructor. + /// + /// Create ScrollbarStyle by style customized by user. + [EditorBrowsable(EditorBrowsableState.Never)] + public CircularScrollbarStyle(CircularScrollbarStyle style) : base(style) + { + this.CopyFrom(style); + } + + /// + /// Static constructor to initialize bindable properties when loading. + /// + static CircularScrollbarStyle() + { + } + + #endregion Constructors + + + #region Properties + + /// + /// The thickness of the scrollbar and track. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public float? Thickness + { + get => (float?)GetValue(ThicknessProperty); + set => SetValue(ThicknessProperty, value); + } + + /// + /// The sweep angle of track area in degrees. + /// + /// + /// Values below 6 degrees are treated as 6 degrees. + /// Values exceeding 180 degrees are treated as 180 degrees. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public float? TrackSweepAngle + { + get => (float?)GetValue(TrackSweepAngleProperty); + set => SetValue(TrackSweepAngleProperty, value); + } + + /// + /// The color of the track part. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Color TrackColor + { + get => (Color)GetValue(TrackColorProperty); + set => SetValue(TrackColorProperty, value); + } + + /// + /// The color of the thumb part. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Color ThumbColor + { + get => (Color)GetValue(ThumbColorProperty); + set => SetValue(ThumbColorProperty, value); + } + + #endregion Properties + + + #region Methods + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override void CopyFrom(BindableObject bindableObject) + { + base.CopyFrom(bindableObject); + + var style = bindableObject as CircularScrollbarStyle; + + if (null != style) + { + Thickness = style.Thickness; + TrackSweepAngle = style.TrackSweepAngle; + TrackColor = style.TrackColor; + ThumbColor = style.ThumbColor; + } + } + + private void Initialize() + { + Thickness = 10.0f; + TrackSweepAngle = 60.0f; + TrackColor = new Color(1.0f, 1.0f, 1.0f, 0.15f); + ThumbColor = new Color(0.6f, 0.6f, 0.6f, 1.0f); + } + + #endregion Methods + } +} diff --git a/src/Tizen.NUI/src/public/VisualConstants.cs b/src/Tizen.NUI/src/public/VisualConstants.cs index 16c4561..d4a14ec 100755 --- a/src/Tizen.NUI/src/public/VisualConstants.cs +++ b/src/Tizen.NUI/src/public/VisualConstants.cs @@ -409,7 +409,12 @@ namespace Tizen.NUI /// /// Renders a animated image (animated GIF). /// - AnimatedImage + AnimatedImage, + /// + /// Renders an arc. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + Arc = AnimatedImage + 3, } /// @@ -454,7 +459,7 @@ namespace Tizen.NUI /// 5 public static readonly int VisualFittingMode = NDalic.VISUAL_PROPERTY_MIX_COLOR + 2; /// - /// The fitting mode of the visual. + /// The corner radius of the visual. /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly int CornerRadius = NDalic.VISUAL_PROPERTY_MIX_COLOR + 3; @@ -1121,4 +1126,42 @@ namespace Tizen.NUI /// 3 public static readonly int Border = NDalic.IMAGE_VISUAL_WRAP_MODE_V + 1; } + + /// + /// This specifies properties of the ArcVisual. + /// + internal struct ArcVisualProperty + { + /// + /// The thickness of the arc. + /// + /// The value is float type. + /// This is mandatory property. + internal static readonly int Thickness = NDalic.IMAGE_VISUAL_URL; + + /// + /// The start angle where the arc begins in degrees. + /// + /// The value is float type. + /// The property of optional. The default value is 0. + internal static readonly int StartAngle = Thickness + 1; + + /// + /// The sweep angle of the arc in degrees. + /// + /// The value is float type. + /// The property of optional. The default value is 360. + internal static readonly int SweepAngle = Thickness + 2; + + /// + /// The cap style of the arc. + /// + /// + /// The value is integer type. + /// The value 0 means butt, the arc does not extend beyond its two endpoints. + /// The value 1 means round, the arc will be extended by a half circle with the center at the end. + /// + /// The property of optional. The default value is 0 (butt). + internal static readonly int Cap = Thickness + 3; + } } diff --git a/src/Tizen.NUI/src/public/VisualMaps.cs b/src/Tizen.NUI/src/public/VisualMaps.cs index d3b3924..c5d63a3 100755 --- a/src/Tizen.NUI/src/public/VisualMaps.cs +++ b/src/Tizen.NUI/src/public/VisualMaps.cs @@ -14,6 +14,7 @@ * limitations under the License. * */ +using System.ComponentModel; using Tizen.NUI.BaseComponents; namespace Tizen.NUI @@ -62,6 +63,12 @@ namespace Tizen.NUI protected VisualFittingModeType? _visualFittingMode = null; /// + /// The corner radius value of the visual. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected float? _cornerRadius = null; + + /// /// The map for visual. /// /// 3 @@ -594,6 +601,23 @@ namespace Tizen.NUI } } + /// + /// The corner radius of the visual. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public float CornerRadius + { + get + { + return _cornerRadius ?? (0.0f); + } + set + { + _cornerRadius = value; + UpdateVisual(); + } + } + internal string Name { set; @@ -612,9 +636,15 @@ namespace Tizen.NUI get; } - internal void UpdateVisual() + /// + /// Suppress UpdateVisual() to update properties to Parent. + /// If it is set to true, UpdateVisual() is ignored unless it is called with force. + /// + internal bool SuppressUpdateVisual { get; set; } = false; + + internal void UpdateVisual(bool force = false) { - if (VisualIndex > 0) + if (VisualIndex > 0 && (!SuppressUpdateVisual || force)) { NUILog.Debug("UpdateVisual()! VisualIndex=" + VisualIndex); Parent.UpdateVisual(VisualIndex, Name, this); @@ -641,6 +671,7 @@ namespace Tizen.NUI if (_mixColor != null) { _outputVisualMap.Add(Visual.Property.MixColor, new PropertyValue(_mixColor)); } if (_opacity != null) { _outputVisualMap.Add(Visual.Property.Opacity, new PropertyValue((float)_opacity)); } if (_visualFittingMode != null) { _outputVisualMap.Add(Visual.Property.VisualFittingMode, new PropertyValue((int)_visualFittingMode)); } + if (_cornerRadius != null) { _outputVisualMap.Add(Visual.Property.CornerRadius, new PropertyValue((int)_cornerRadius)); } } private void ComposingTransformMap() diff --git a/src/Tizen.NUI/src/public/Visuals/ArcVisual.cs b/src/Tizen.NUI/src/public/Visuals/ArcVisual.cs new file mode 100644 index 0000000..7506ae2 --- /dev/null +++ b/src/Tizen.NUI/src/public/Visuals/ArcVisual.cs @@ -0,0 +1,154 @@ +/* + * Copyright(c) 2020 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.ComponentModel; + +namespace Tizen.NUI +{ + /// + /// A class encapsulating the property map of the arc visual. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public class ArcVisual : VisualMap + { + #region Fields + + private float thickness; + private float startAngle; + private float sweepAngle; + private CapType cap; + + #endregion Fields + + + #region Constructors + + /// + /// Constructor. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ArcVisual() : base() + { + } + + #endregion Constructors + + + #region Enums + + /// + /// Enumeration for the cap style of the arc line. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public enum CapType + { + /// + /// The arc does not extend beyond its two endpoints. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + Butt, + + /// + /// The arc will be extended by a half circle with the center at the end. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + Round, + } + + #endregion Enums + + + #region Properties + + /// + /// The thickness of the arc. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public float Thickness + { + get => thickness; + set + { + thickness = value; + UpdateVisual(); + } + } + + /// + /// The start angle where the arc begins in degrees. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public float StartAngle + { + get => startAngle; + set + { + startAngle = value; + UpdateVisual(); + } + } + + /// + /// The sweep angle of the arc in degrees. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public float SweepAngle + { + get => sweepAngle; + set + { + sweepAngle = value; + UpdateVisual(); + } + } + + /// + /// The cap style of the arc. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public CapType Cap + { + get => cap; + set + { + cap = value; + UpdateVisual(); + } + } + + #endregion Properties + + + #region Methods + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void ComposingPropertyMap() + { + _outputVisualMap = null; + + base.ComposingPropertyMap(); + + _outputVisualMap.Add(Visual.Property.Type, new PropertyValue((int)Visual.Type.Arc)); + _outputVisualMap.Add(ArcVisualProperty.Thickness, new PropertyValue(Thickness < 0.0f ? 0.0f : Thickness)); + _outputVisualMap.Add(ArcVisualProperty.StartAngle, new PropertyValue(StartAngle)); + _outputVisualMap.Add(ArcVisualProperty.SweepAngle, new PropertyValue(SweepAngle)); + _outputVisualMap.Add(ArcVisualProperty.Cap, new PropertyValue((int)Cap)); + } + + #endregion Methods + } +} diff --git a/src/Tizen.NUI/src/public/Visuals/ColorVisual.cs b/src/Tizen.NUI/src/public/Visuals/ColorVisual.cs index 5906fcd..bacde98 100755 --- a/src/Tizen.NUI/src/public/Visuals/ColorVisual.cs +++ b/src/Tizen.NUI/src/public/Visuals/ColorVisual.cs @@ -76,16 +76,20 @@ namespace Tizen.NUI /// 3 protected override void ComposingPropertyMap() { - if (_mixColorForColorVisual != null) + Color color = _mixColorForColorVisual ?? _mixColor; + + if (color != null) { - _outputVisualMap = new PropertyMap(); + _outputVisualMap = null; + + base.ComposingPropertyMap(); + _outputVisualMap.Add(Visual.Property.Type, new PropertyValue((int)Visual.Type.Color)); - _outputVisualMap.Add(ColorVisualProperty.MixColor, new PropertyValue(_mixColorForColorVisual)); - if (_shader != null) { _outputVisualMap.Add(Visual.Property.Shader, new PropertyValue(_shader)); } - if (_premultipliedAlpha != null) { _outputVisualMap.Add(Visual.Property.PremultipliedAlpha, new PropertyValue((bool)_premultipliedAlpha)); } - if (_opacity != null) { _outputVisualMap.Add(Visual.Property.Opacity, new PropertyValue((float)_opacity)); } - if (_renderIfTransparent != null) { _outputVisualMap.Add(ColorVisualProperty.RenderIfTransparent, new PropertyValue((bool)_renderIfTransparent)); } - if (_visualFittingMode != null) { _outputVisualMap.Add(Visual.Property.VisualFittingMode, new PropertyValue((int)_visualFittingMode)); } + _outputVisualMap.Add(ColorVisualProperty.MixColor, new PropertyValue(color)); + } + else + { + _outputVisualMap = new PropertyMap(); } } }