2 * Copyright(c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.ComponentModel;
19 using Tizen.NUI.BaseComponents;
20 using Tizen.NUI.Binding;
22 namespace Tizen.NUI.Components
24 // Represents padding data : Start, End, Top, Bottom
25 using PaddingType = ValueTuple<ushort, ushort, ushort, ushort>;
28 /// The Scrollbar is a component that contains track and thumb to indicate the current scrolled position of a scrollable object.
30 [EditorBrowsable(EditorBrowsableState.Never)]
31 public class Scrollbar : ScrollbarBase
35 /// <summary>Bindable property of TrackThickness</summary>
36 [EditorBrowsable(EditorBrowsableState.Never)]
37 public static readonly BindableProperty TrackThicknessProperty = BindableProperty.Create(nameof(TrackThickness), typeof(float), typeof(Scrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
39 var instance = ((Scrollbar)bindable);
40 var thickness = (float?)newValue;
42 instance.scrollbarStyle.TrackThickness = thickness;
43 instance.UpdateTrackThickness(thickness ?? 0);
45 defaultValueCreator: (bindable) =>
47 return ((Scrollbar)bindable).scrollbarStyle.TrackThickness ?? 0;
50 /// <summary>Bindable property of ThumbThickness</summary>
51 [EditorBrowsable(EditorBrowsableState.Never)]
52 public static readonly BindableProperty ThumbThicknessProperty = BindableProperty.Create(nameof(ThumbThickness), typeof(float), typeof(Scrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
54 var instance = ((Scrollbar)bindable);
55 var thickness = (float?)newValue;
57 instance.scrollbarStyle.ThumbThickness = thickness;
58 instance.UpdateThumbThickness(thickness ?? 0);
60 defaultValueCreator: (bindable) =>
62 return ((Scrollbar)bindable).scrollbarStyle.ThumbThickness ?? 0;
65 /// <summary>Bindable property of TrackColor</summary>
66 [EditorBrowsable(EditorBrowsableState.Never)]
67 public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
69 var instance = ((Scrollbar)bindable);
70 var color = (Color)newValue;
72 instance.scrollbarStyle.TrackColor = color;
73 instance.UpdateTrackColor(color);
75 defaultValueCreator: (bindable) =>
77 return ((Scrollbar)bindable).scrollbarStyle.TrackColor;
80 /// <summary>Bindable property of ThumbColor</summary>
81 [EditorBrowsable(EditorBrowsableState.Never)]
82 public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
84 var instance = ((Scrollbar)bindable);
85 var color = (Color)newValue;
87 instance.scrollbarStyle.ThumbColor = color;
88 instance.UpdateThumbColor(color);
90 defaultValueCreator: (bindable) =>
92 return ((Scrollbar)bindable).scrollbarStyle.ThumbColor;
95 /// <summary>Bindable property of TrackPadding</summary>
96 [EditorBrowsable(EditorBrowsableState.Never)]
97 public static readonly BindableProperty TrackPaddingProperty = BindableProperty.Create(nameof(TrackPadding), typeof(Extents), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
99 var instance = ((Scrollbar)bindable);
100 var trackPadding = (Extents)newValue;
102 instance.scrollbarStyle.TrackPadding = trackPadding;
103 instance.UpdateTrackPadding(trackPadding);
105 defaultValueCreator: (bindable) =>
107 return ((Scrollbar)bindable).scrollbarStyle.TrackPadding;
110 private ColorVisual trackVisual;
111 private ColorVisual thumbVisual;
112 private Animation thumbPositionAnimation;
113 private Animation thumbSizeAnimation;
114 private Calculator calculator;
115 private Size containerSize = new Size(0, 0);
116 private ScrollbarStyle scrollbarStyle => ViewStyle as ScrollbarStyle;
117 private bool mScrollEnabled = true;
118 private float previousPosition;
126 /// Create an empty Scrollbar.
128 public Scrollbar() : base()
133 /// Create a Scrollbar and initialize with properties.
135 /// <param name="contentLength">The length of the scrollable content area.</param>
136 /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
137 /// <param name="currentPosition">The current position of the viewport in scrollable content area. This is the viewport's top position if the scroller is vertical, otherwise, left.</param>
138 /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
139 [EditorBrowsable(EditorBrowsableState.Never)]
140 public Scrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : this()
142 Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
146 /// Create an empty Scrollbar with a ScrollbarStyle instance to set style properties.
148 [EditorBrowsable(EditorBrowsableState.Never)]
149 public Scrollbar(ScrollbarStyle style) : base(style)
154 /// Static constructor to initialize bindable properties when loading.
160 #endregion Constructors
166 /// Return a copied Style instance of Scrollbar
169 /// It returns copied Style instance and changing it does not effect to the Scrollbar.
170 /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
172 [EditorBrowsable(EditorBrowsableState.Never)]
173 public new ScrollbarStyle Style
177 var result = new ScrollbarStyle(scrollbarStyle);
178 result.CopyPropertiesFromView(this);
184 /// The thickness of the track.
186 [EditorBrowsable(EditorBrowsableState.Never)]
187 public float TrackThickness
189 get => (float)GetValue(TrackThicknessProperty);
190 set => SetValue(TrackThicknessProperty, value);
194 /// The thickness of the thumb.
196 [EditorBrowsable(EditorBrowsableState.Never)]
197 public float ThumbThickness
199 get => (float)GetValue(ThumbThicknessProperty);
200 set => SetValue(ThumbThicknessProperty, value);
204 /// The color of the track part.
206 [EditorBrowsable(EditorBrowsableState.Never)]
207 public Color TrackColor
209 get => (Color)GetValue(TrackColorProperty);
210 set => SetValue(TrackColorProperty, value);
214 /// The color of the thumb part.
216 [EditorBrowsable(EditorBrowsableState.Never)]
217 public Color ThumbColor
219 get => (Color)GetValue(ThumbColorProperty);
220 set => SetValue(ThumbColorProperty, value);
224 /// The padding value of the track.
225 /// Note that when the scrollbar is for vertical direction, Start value is ignored.
226 /// In case of horizontal direction, Top value is ignored.
228 [EditorBrowsable(EditorBrowsableState.Never)]
229 public Extents TrackPadding
231 get => (Extents)GetValue(TrackPaddingProperty);
232 set => SetValue(TrackPaddingProperty, value);
235 #endregion Properties
241 [EditorBrowsable(EditorBrowsableState.Never)]
242 public override void Initialize(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false)
246 calculator = new HorizontalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewportLength, currentPosition);
250 calculator = new VerticalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewportLength, currentPosition);
253 thumbPositionAnimation?.Stop();
254 thumbPositionAnimation = null;
256 thumbSizeAnimation?.Stop();
257 thumbSizeAnimation = null;
259 PaddingType ensuredPadding = EnsurePadding(TrackPadding);
260 Size trackSize = calculator.CalculateTrackSize(TrackThickness, containerSize, ensuredPadding);
261 Vector2 trackPosition = calculator.CalculateTrackPosition(ensuredPadding);
262 Size thumbSize = calculator.CalculateThumbSize(ThumbThickness, trackSize);
263 Vector2 thumbPosition = calculator.CalculateThumbPosition(trackSize, thumbSize, ensuredPadding);
265 trackVisual = new ColorVisual
267 SuppressUpdateVisual = true,
268 MixColor = TrackColor,
269 SizePolicy = VisualTransformPolicyType.Absolute,
270 Origin = calculator.CalculatorTrackAlign(),
271 AnchorPoint = calculator.CalculatorTrackAlign(),
273 Position = trackPosition,
276 AddVisual("Track", trackVisual);
278 thumbVisual = new ColorVisual
280 SuppressUpdateVisual = true,
281 MixColor = ThumbColor,
282 SizePolicy = VisualTransformPolicyType.Absolute,
283 Origin = calculator.CalculatorThumbAlign(),
284 AnchorPoint = calculator.CalculatorThumbAlign(),
285 Opacity = calculator.CalculateThumbVisibility() ? 1.0f : 0.0f,
287 Position = thumbPosition,
290 AddVisual("Thumb", thumbVisual);
294 /// <remarks>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. </remarks>
295 [EditorBrowsable(EditorBrowsableState.Never)]
296 public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
298 if (calculator == null)
303 calculator.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
304 previousPosition = calculator.currentPosition;
305 calculator.currentPosition = position;
307 thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
308 thumbVisual.Position = calculator.CalculateThumbScrollPosition(trackVisual.Size, thumbVisual.Position, EnsurePadding(TrackPadding));
309 thumbVisual.Opacity = calculator.CalculateThumbVisibility() ? 1.0f : 0.0f;
313 thumbVisual.UpdateVisual(true);
318 // TODO Support non built-in alpha function for visual trainsition in DALi.
319 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
321 thumbPositionAnimation?.Stop();
322 thumbPositionAnimation = AnimateVisual(thumbVisual, "position", thumbVisual.Position, 0, (int)durationMs, builtinAlphaFunction);
323 thumbPositionAnimation.Play();
325 thumbSizeAnimation?.Stop();
326 thumbSizeAnimation = AnimateVisual(thumbVisual, "size", thumbVisual.Size, 0, (int)durationMs, builtinAlphaFunction);
327 thumbSizeAnimation.Play();
331 /// <remarks>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. </remarks>
332 [EditorBrowsable(EditorBrowsableState.Never)]
333 public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
335 if (mScrollEnabled == false)
340 if (calculator == null)
345 previousPosition = calculator.currentPosition;
346 calculator.currentPosition = position;
347 thumbVisual.Position = calculator.CalculateThumbScrollPosition(trackVisual.Size, thumbVisual.Position, EnsurePadding(TrackPadding));
351 thumbVisual.UpdateVisual(true);
356 // TODO Support non built-in alpha function for visual trainsition in DALi.
357 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
359 thumbPositionAnimation?.Stop();
360 thumbPositionAnimation = AnimateVisual(thumbVisual, "position", thumbVisual.Position, 0, (int)durationMs, builtinAlphaFunction);
361 thumbPositionAnimation.Play();
365 [EditorBrowsable(EditorBrowsableState.Never)]
366 public override void OnRelayout(Vector2 size, RelayoutContainer container)
368 base.OnRelayout(size, container);
370 if (size.Width == containerSize.Width && size.Height == containerSize.Height)
375 containerSize = new Size(size.Width, size.Height);
377 if (calculator == null)
382 PaddingType ensuredPadding = EnsurePadding(TrackPadding);
383 trackVisual.Size = calculator.CalculateTrackSize(TrackThickness, containerSize, ensuredPadding);
384 trackVisual.Position = calculator.CalculateTrackPosition(ensuredPadding);
385 thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
386 thumbVisual.Position = calculator.CalculateThumbPosition(trackVisual.Size, thumbVisual.Size, ensuredPadding);
388 trackVisual.UpdateVisual(true);
389 thumbVisual.UpdateVisual(true);
393 [EditorBrowsable(EditorBrowsableState.Never)]
394 public override void ApplyStyle(ViewStyle viewStyle)
396 if (viewStyle is ScrollbarStyle scrollbarStyle)
398 // Apply essential look.
399 if (scrollbarStyle.TrackThickness == null) scrollbarStyle.TrackThickness = 6.0f;
400 if (scrollbarStyle.ThumbThickness == null) scrollbarStyle.ThumbThickness = 6.0f;
401 if (scrollbarStyle.TrackColor == null) scrollbarStyle.TrackColor = new Color(1.0f, 1.0f, 1.0f, 0.15f);
402 if (scrollbarStyle.ThumbColor == null) scrollbarStyle.ThumbColor = new Color(0.6f, 0.6f, 0.6f, 1.0f);
403 if (scrollbarStyle.TrackPadding == null) scrollbarStyle.TrackPadding = 4;
404 if (scrollbarStyle.WidthResizePolicy == null) scrollbarStyle.WidthResizePolicy = ResizePolicyType.FillToParent;
405 if (scrollbarStyle.HeightResizePolicy == null) scrollbarStyle.HeightResizePolicy = ResizePolicyType.FillToParent;
408 base.ApplyStyle(viewStyle);
412 [EditorBrowsable(EditorBrowsableState.Never)]
413 protected override ViewStyle CreateViewStyle()
415 return new ScrollbarStyle();
419 /// Update TrackThickness property of the scrollbar.
421 /// <param name="thickness">The width of the track.</param>
422 [EditorBrowsable(EditorBrowsableState.Never)]
423 protected virtual void UpdateTrackThickness(float thickness)
425 if (trackVisual == null)
430 trackVisual.Size = calculator.CalculateTrackSize(thickness, containerSize, EnsurePadding(TrackPadding));
431 trackVisual.UpdateVisual(true);
435 /// Update ThumbThickness property of the scrollbar.
437 /// <param name="thickness">The width of the track.</param>
438 [EditorBrowsable(EditorBrowsableState.Never)]
439 protected virtual void UpdateThumbThickness(float thickness)
441 if (thumbVisual == null)
446 thumbVisual.Size = calculator.CalculateThumbSize(thickness, trackVisual.Size);
447 thumbVisual.UpdateVisual(true);
451 /// Update TrackColor property of the scrollbar.
453 /// <param name="color">The color of the track.</param>
454 [EditorBrowsable(EditorBrowsableState.Never)]
455 protected virtual void UpdateTrackColor(Color color)
457 if (trackVisual == null)
462 trackVisual.MixColor = color;
463 trackVisual.UpdateVisual(true);
467 /// Update ThumbColor property of the scrollbar.
469 /// <param name="color">The color of the thumb.</param>
470 [EditorBrowsable(EditorBrowsableState.Never)]
471 protected virtual void UpdateThumbColor(Color color)
473 if (thumbVisual == null)
478 thumbVisual.MixColor = color;
479 thumbVisual.UpdateVisual(true);
483 /// Update TrackPadding property of the scrollbar.
485 /// <param name="trackPadding">The padding of the track.</param>
486 protected virtual void UpdateTrackPadding(Extents trackPadding)
488 if (calculator == null || trackVisual == null || thumbVisual == null)
493 PaddingType ensuredPadding = EnsurePadding(trackPadding);
494 trackVisual.Size = calculator.CalculateTrackSize(TrackThickness, containerSize, ensuredPadding);
495 trackVisual.Position = calculator.CalculateTrackPosition(ensuredPadding);
496 thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
497 thumbVisual.Position = calculator.CalculateThumbPaddingPosition(trackVisual.Size, thumbVisual.Size, thumbVisual.Position, ensuredPadding);
499 trackVisual.UpdateVisual(true);
500 thumbVisual.UpdateVisual(true);
504 [EditorBrowsable(EditorBrowsableState.Never)]
505 public override bool ScrollEnabled
509 return mScrollEnabled;
513 if (value != mScrollEnabled)
515 mScrollEnabled = value;
521 [EditorBrowsable(EditorBrowsableState.Never)]
522 public override Position ScrollPosition
526 if (calculator == null)
528 return new Position(0.0f, 0.0f);
531 float length = Math.Min(Math.Max(calculator.currentPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
533 if (calculator is HorizontalCalculator)
535 return new Position(length, 0.0f);
539 return new Position(0.0f, length);
545 [EditorBrowsable(EditorBrowsableState.Never)]
546 public override Position ScrollCurrentPosition
550 if (calculator == null)
552 return new Position(0.0f, 0.0f);
555 float length = Math.Min(Math.Max(calculator.currentPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
557 if (thumbPositionAnimation != null)
559 float progress = thumbPositionAnimation.CurrentProgress;
560 float previousLength = Math.Min(Math.Max(previousPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
562 length = ((1.0f - progress) * previousLength) + (progress * length);
565 if (calculator is HorizontalCalculator)
567 return new Position(length, 0.0f);
571 return new Position(0.0f, length);
576 private PaddingType EnsurePadding(Extents padding) => padding == null ? new PaddingType(0, 0, 0 ,0) : new PaddingType(padding.Start, padding.End, padding.Top, padding.Bottom);
583 private abstract class Calculator
585 public float contentLength;
586 public float visibleLength;
587 public float currentPosition;
589 public Calculator(float contentLength, float visibleLength, float currentPosition)
591 this.contentLength = contentLength;
592 this.visibleLength = visibleLength;
593 this.currentPosition = currentPosition;
596 public bool CalculateThumbVisibility()
598 return contentLength > visibleLength;
601 public abstract Visual.AlignType CalculatorTrackAlign();
602 public abstract Visual.AlignType CalculatorThumbAlign();
603 public abstract Size CalculateTrackSize(float thickness, Size containerSize, PaddingType trackPadding);
604 public abstract Vector2 CalculateTrackPosition(PaddingType trackPadding);
605 public abstract Size CalculateThumbSize(float thickness, Size trackSize);
606 public abstract Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, PaddingType trackPadding);
607 public abstract Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, PaddingType trackPadding);
608 public abstract Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbCurrentPosition, PaddingType trackPadding);
611 private class HorizontalCalculator : Calculator
613 public HorizontalCalculator(float contentLength, float visibleLength, float currentPosition) : base(contentLength, visibleLength, currentPosition)
617 public override Visual.AlignType CalculatorTrackAlign()
619 return Visual.AlignType.BottomBegin;
622 public override Visual.AlignType CalculatorThumbAlign()
624 return Visual.AlignType.BottomBegin;
627 public override Size CalculateTrackSize(float thickness, Size containerSize, PaddingType trackPadding)
629 return new Size(containerSize.Width - trackPadding.Item1 - trackPadding.Item2, thickness);
632 public override Vector2 CalculateTrackPosition(PaddingType trackPadding)
634 return new Vector2(trackPadding.Item1, -trackPadding.Item4);
637 public override Size CalculateThumbSize(float thickness, Size trackSize)
639 return new Size(trackSize.Width * visibleLength / contentLength, thickness);
642 public override Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, PaddingType trackPadding)
644 float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding.Item4;
645 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
646 return new Vector2(trackPadding.Item1 + trackSize.Width * pos / contentLength, -padding);
649 public override Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, PaddingType trackPadding)
651 float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding.Item4;
652 return new Vector2(thumbCurrentPosition.X, -padding);
655 public override Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbCurrentPosition, PaddingType trackPadding)
657 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
658 return new Vector2(trackPadding.Item1 + trackSize.Width * pos / contentLength, thumbCurrentPosition.Y);
662 private class VerticalCalculator : Calculator
664 public VerticalCalculator(float contentLength, float visibleLength, float currentPosition) : base(contentLength, visibleLength, currentPosition)
668 public override Visual.AlignType CalculatorTrackAlign()
670 return Visual.AlignType.TopEnd;
673 public override Visual.AlignType CalculatorThumbAlign()
675 return Visual.AlignType.TopEnd;
678 public override Size CalculateTrackSize(float thickness, Size containerSize, PaddingType trackPadding)
680 return new Size(thickness, containerSize.Height - trackPadding.Item3 - trackPadding.Item4);
683 public override Vector2 CalculateTrackPosition(PaddingType trackPadding)
685 return new Vector2(-trackPadding.Item2, trackPadding.Item3);
688 public override Size CalculateThumbSize(float thickness, Size trackSize)
690 return new Size(thickness, trackSize.Height * visibleLength / contentLength);
693 public override Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, PaddingType trackPadding)
695 float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding.Item2;
696 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
697 return new Vector2(-padding, trackPadding.Item3 + trackSize.Height * pos / contentLength);
700 public override Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, PaddingType trackPadding)
702 float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding.Item2;
703 return new Vector2(-padding, thumbCurrentPosition.Y);
706 public override Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbPosition, PaddingType trackPadding)
708 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
709 return new Vector2(thumbPosition.X, trackPadding.Item3 + trackSize.Height * pos / contentLength);