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 System.Diagnostics;
20 using Tizen.NUI.BaseComponents;
21 using Tizen.NUI.Binding;
23 namespace Tizen.NUI.Components
25 // Represents padding data : Start, End, Top, Bottom
26 using PaddingType = ValueTuple<ushort, ushort, ushort, ushort>;
29 /// The Scrollbar is a component that contains track and thumb to indicate the current scrolled position of a scrollable object.
31 [EditorBrowsable(EditorBrowsableState.Never)]
32 public class Scrollbar : ScrollbarBase
36 /// <summary>Bindable property of TrackThickness</summary>
37 [EditorBrowsable(EditorBrowsableState.Never)]
38 public static readonly BindableProperty TrackThicknessProperty = BindableProperty.Create(nameof(TrackThickness), typeof(float), typeof(Scrollbar), default(float),
39 propertyChanged: (bindable, oldValue, newValue) => ((Scrollbar)bindable).UpdateTrackThickness((float?)newValue ?? 0),
40 defaultValueCreator: (bindable) => ((Scrollbar)bindable).trackThickness
43 /// <summary>Bindable property of ThumbThickness</summary>
44 [EditorBrowsable(EditorBrowsableState.Never)]
45 public static readonly BindableProperty ThumbThicknessProperty = BindableProperty.Create(nameof(ThumbThickness), typeof(float), typeof(Scrollbar), default(float),
46 propertyChanged: (bindable, oldValue, newValue) => ((Scrollbar)bindable).UpdateThumbThickness((float?)newValue ?? 0),
47 defaultValueCreator: (bindable) => ((Scrollbar)bindable).thumbThickness
50 /// <summary>Bindable property of TrackColor</summary>
51 [EditorBrowsable(EditorBrowsableState.Never)]
52 public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(Scrollbar), null,
53 propertyChanged: (bindable, oldValue, newValue) => ((Scrollbar)bindable).trackView.BackgroundColor = (Color)newValue,
54 defaultValueCreator: (bindable) => ((Scrollbar)bindable).trackView.BackgroundColor
57 /// <summary>Bindable property of ThumbColor</summary>
58 [EditorBrowsable(EditorBrowsableState.Never)]
59 public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(Scrollbar), null,
60 propertyChanged: (bindable, oldValue, newValue) => ((Scrollbar)bindable).UpdateThumbColor((Color)newValue),
61 defaultValueCreator: (bindable) => ((Scrollbar)bindable).thumbColor
64 /// <summary>Bindable property of TrackPadding</summary>
65 [EditorBrowsable(EditorBrowsableState.Never)]
66 public static readonly BindableProperty TrackPaddingProperty = BindableProperty.Create(nameof(TrackPadding), typeof(Extents), typeof(Scrollbar), null,
67 propertyChanged: (bindable, oldValue, newValue) => ((Scrollbar)bindable).UpdateTrackPadding((Extents)newValue),
68 defaultValueCreator: (bindable) => ((Scrollbar)bindable).trackPadding
71 /// <summary>Bindable property of ThumbVerticalImageUrl</summary>
72 [EditorBrowsable(EditorBrowsableState.Never)]
73 public static readonly BindableProperty ThumbVerticalImageUrlProperty = BindableProperty.Create(nameof(ThumbVerticalImageUrl), typeof(string), typeof(Scrollbar), null,
74 propertyChanged: (bindable, oldValue, newValue) => ((Scrollbar)bindable).UpdateThumbImage((string)newValue, false),
75 defaultValueCreator: (bindable) => ((Scrollbar)bindable).thumbVerticalImageUrl
78 /// <summary>Bindable property of ThumbHorizontalImageUrl</summary>
79 [EditorBrowsable(EditorBrowsableState.Never)]
80 public static readonly BindableProperty ThumbHorizontalImageUrlProperty = BindableProperty.Create(nameof(ThumbHorizontalImageUrl), typeof(string), typeof(Scrollbar), null,
81 propertyChanged: (bindable, oldValue, newValue) => ((Scrollbar)bindable).UpdateThumbImage((string)newValue, true),
82 defaultValueCreator: (bindable) => ((Scrollbar)bindable).thumbHorizontalImageUrl
86 private View trackView;
87 private ImageView thumbView;
88 private Animation thumbPositionAnimation;
89 private Animation thumbSizeAnimation;
90 private Animation opacityAnimation;
91 private Calculator calculator;
92 private Size containerSize = new Size(0, 0);
93 private float previousPosition;
94 private float trackThickness = 6.0f;
95 private float thumbThickness = 6.0f;
96 private string thumbVerticalImageUrl;
97 private string thumbHorizontalImageUrl;
98 private Color thumbColor;
99 private PaddingType trackPadding = new PaddingType(4, 4, 4, 4);
100 private bool isHorizontal;
108 /// Create an empty Scrollbar.
115 /// Create a Scrollbar and initialize with properties.
117 /// <param name="contentLength">The length of the scrollable content area.</param>
118 /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
119 /// <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>
120 /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
121 [EditorBrowsable(EditorBrowsableState.Never)]
122 public Scrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : this()
124 Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
128 /// Create an empty Scrollbar with a ScrollbarStyle instance to set style properties.
130 [EditorBrowsable(EditorBrowsableState.Never)]
131 public Scrollbar(ScrollbarStyle style) : base(style)
136 /// Static constructor to initialize bindable properties when loading.
142 #endregion Constructors
148 /// The thickness of the track.
150 [EditorBrowsable(EditorBrowsableState.Never)]
151 public float TrackThickness
153 get => (float)GetValue(TrackThicknessProperty);
154 set => SetValue(TrackThicknessProperty, value);
158 /// The thickness of the thumb.
160 [EditorBrowsable(EditorBrowsableState.Never)]
161 public float ThumbThickness
163 get => (float)GetValue(ThumbThicknessProperty);
164 set => SetValue(ThumbThicknessProperty, value);
168 /// The color of the track part.
170 [EditorBrowsable(EditorBrowsableState.Never)]
171 public Color TrackColor
173 get => (Color)GetValue(TrackColorProperty);
174 set => SetValue(TrackColorProperty, value);
178 /// The color of the thumb part.
180 [EditorBrowsable(EditorBrowsableState.Never)]
181 public Color ThumbColor
183 get => (Color)GetValue(ThumbColorProperty);
184 set => SetValue(ThumbColorProperty, value);
188 /// The padding value of the track.
189 /// Note that when the scrollbar is for vertical direction, Start value is ignored.
190 /// In case of horizontal direction, Top value is ignored.
192 [EditorBrowsable(EditorBrowsableState.Never)]
193 public Extents TrackPadding
195 get => (Extents)GetValue(TrackPaddingProperty);
196 set => SetValue(TrackPaddingProperty, value);
200 /// The image url of the vertical thumb.
202 [EditorBrowsable(EditorBrowsableState.Never)]
203 public string ThumbVerticalImageUrl
205 get => (string)GetValue(ThumbVerticalImageUrlProperty);
206 set => SetValue(ThumbVerticalImageUrlProperty, value);
210 /// The image url of the horizontal thumb.
212 [EditorBrowsable(EditorBrowsableState.Never)]
213 public string ThumbHorizontalImageUrl
215 get => (string)GetValue(ThumbHorizontalImageUrlProperty);
216 set => SetValue(ThumbHorizontalImageUrlProperty, value);
220 [EditorBrowsable(EditorBrowsableState.Never)]
221 public override float ScrollPosition
225 if (calculator == null)
230 return Math.Min(Math.Max(calculator.currentPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
235 [EditorBrowsable(EditorBrowsableState.Never)]
236 public override float ScrollCurrentPosition
240 if (calculator == null)
245 float length = Math.Min(Math.Max(calculator.currentPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
247 if (thumbPositionAnimation != null)
249 float progress = thumbPositionAnimation.CurrentProgress;
250 float previousLength = Math.Min(Math.Max(previousPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
252 length = ((1.0f - progress) * previousLength) + (progress * length);
259 #endregion Properties
265 [EditorBrowsable(EditorBrowsableState.Never)]
266 public override void OnInitialize()
270 trackView = new View()
272 PositionUsesPivotPoint = true,
273 BackgroundColor = new Color(1.0f, 1.0f, 1.0f, 0.15f)
277 thumbView = new ImageView()
279 PositionUsesPivotPoint = true,
280 BackgroundColor = new Color(0.6f, 0.6f, 0.6f, 1.0f)
284 WidthResizePolicy = ResizePolicyType.FillToParent;
285 HeightResizePolicy = ResizePolicyType.FillToParent;
289 [EditorBrowsable(EditorBrowsableState.Never)]
290 public override void Initialize(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false)
292 this.isHorizontal = isHorizontal;
295 if (thumbHorizontalImageUrl != null)
297 thumbView.ResourceUrl = thumbHorizontalImageUrl;
298 thumbView.Color = thumbColor;
299 thumbView.BackgroundColor = Color.Transparent;
301 calculator = new HorizontalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewportLength, currentPosition);
305 if (thumbVerticalImageUrl != null)
307 thumbView.ResourceUrl = thumbVerticalImageUrl;
308 thumbView.Color = thumbColor;
309 thumbView.BackgroundColor = Color.Transparent;
311 calculator = new VerticalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewportLength, currentPosition);
314 thumbPositionAnimation?.Clear();
315 thumbPositionAnimation = null;
317 thumbSizeAnimation?.Clear();
318 thumbSizeAnimation = null;
320 opacityAnimation?.Clear();
321 opacityAnimation = null;
323 var trackSize = calculator.CalculateTrackSize(TrackThickness, containerSize, trackPadding);
324 var trackPosition = calculator.CalculateTrackPosition(trackPadding);
325 var thumbSize = calculator.CalculateThumbSize(ThumbThickness, trackSize);
326 var thumbPosition = calculator.CalculateThumbPosition(trackSize, thumbSize, trackPadding);
328 Debug.Assert(trackView != null);
329 trackView.ParentOrigin = calculator.CalculatorTrackAlign();
330 trackView.PivotPoint = calculator.CalculatorTrackAlign();
331 trackView.Size = trackSize;
332 trackView.Position = trackPosition;
334 Debug.Assert(thumbView != null);
335 thumbView.ParentOrigin = calculator.CalculatorThumbAlign();
336 thumbView.PivotPoint = calculator.CalculatorThumbAlign();
337 thumbView.Size = thumbSize;
338 thumbView.Position = thumbPosition;;
340 Opacity = calculator.IsScrollable() ? 1.0f : 0.0f;
344 /// <exception cref="InvalidOperationException">Thrown when the scrollabr not yet initialized.</exception>
345 [EditorBrowsable(EditorBrowsableState.Never)]
346 public override void Update(float contentLength, float viewportLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
348 if (calculator == null)
350 throw new InvalidOperationException("Scrollbar is not initialized. Please call Initialize() first.");
353 calculator.visibleLength = viewportLength;
354 Update(contentLength, position, durationMs, alphaFunction);
358 /// <exception cref="InvalidOperationException">Thrown when the scrollabr not yet initialized.</exception>
359 [EditorBrowsable(EditorBrowsableState.Never)]
360 public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
362 if (calculator == null)
364 throw new InvalidOperationException("Scrollbar is not initialized. Please call Initialize() first.");
367 calculator.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
368 calculator.currentPosition = position;
370 float newOpacity = calculator.IsScrollable() ? 1.0f : 0.0f;
371 bool opacityChanged = (int)Opacity != (int)newOpacity;
373 var thumbSize = calculator.CalculateThumbSize(ThumbThickness, trackView.Size);
374 var thumbPosition = calculator.CalculateThumbScrollPosition(trackView.Size, thumbView.Position, trackPadding);
378 thumbView.Position = thumbPosition;
379 thumbView.Size = thumbSize;
383 Opacity = newOpacity;
388 EnsureThumbPositionAnimation().AnimateTo(thumbView, "Position", thumbPosition, 0, (int)durationMs, alphaFunction);
389 thumbPositionAnimation.Play();
391 EnsureThumbSizeAnimation().AnimateTo(thumbView, "Size", thumbSize, 0, (int)durationMs, alphaFunction);
392 thumbSizeAnimation.Play();
396 EnsureOpacityAnimation().AnimateTo(this, "Opacity", newOpacity, 0, (int)durationMs, alphaFunction);
397 opacityAnimation.Play();
402 /// <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>
403 [EditorBrowsable(EditorBrowsableState.Never)]
404 public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
406 if (ControlState == ControlState.Disabled)
411 if (calculator == null)
416 previousPosition = calculator.currentPosition;
417 calculator.currentPosition = position;
418 var thumbPosition = calculator.CalculateThumbScrollPosition(trackView.Size, thumbView.Position, trackPadding);
422 thumbView.Position = thumbPosition;
426 EnsureThumbPositionAnimation().AnimateTo(thumbView, "position", thumbPosition, 0, (int)durationMs, alphaFunction);
427 thumbPositionAnimation.Play();
431 [EditorBrowsable(EditorBrowsableState.Never)]
432 public override void OnRelayout(Vector2 size, RelayoutContainer container)
434 base.OnRelayout(size, container);
436 if (size.Width == containerSize.Width && size.Height == containerSize.Height)
441 containerSize = new Size(size.Width, size.Height);
443 if (calculator == null)
448 trackView.Size = calculator.CalculateTrackSize(TrackThickness, containerSize, trackPadding);
449 trackView.Position = calculator.CalculateTrackPosition(trackPadding);
450 thumbView.Size = calculator.CalculateThumbSize(ThumbThickness, trackView.Size);
451 thumbView.Position = calculator.CalculateThumbPosition(trackView.Size, thumbView.Size, trackPadding);
455 [EditorBrowsable(EditorBrowsableState.Never)]
456 public override void ApplyStyle(ViewStyle viewStyle)
458 if (viewStyle is ScrollbarStyle scrollbarStyle)
460 // Apply essential look.
461 if (scrollbarStyle.TrackThickness == null) scrollbarStyle.TrackThickness = 6.0f;
462 if (scrollbarStyle.ThumbThickness == null) scrollbarStyle.ThumbThickness = 6.0f;
463 if (scrollbarStyle.TrackColor == null) scrollbarStyle.TrackColor = new Color(1.0f, 1.0f, 1.0f, 0.15f);
464 if (scrollbarStyle.ThumbColor == null) scrollbarStyle.ThumbColor = new Color(0.6f, 0.6f, 0.6f, 1.0f);
465 if (scrollbarStyle.TrackPadding == null) scrollbarStyle.TrackPadding = 4;
466 if (scrollbarStyle.WidthResizePolicy == null) scrollbarStyle.WidthResizePolicy = ResizePolicyType.FillToParent;
467 if (scrollbarStyle.HeightResizePolicy == null) scrollbarStyle.HeightResizePolicy = ResizePolicyType.FillToParent;
470 base.ApplyStyle(viewStyle);
474 [EditorBrowsable(EditorBrowsableState.Never)]
475 protected override ViewStyle CreateViewStyle()
477 return new ScrollbarStyle();
481 /// Update TrackThickness property of the scrollbar.
483 /// <param name="thickness">The width of the track.</param>
484 private void UpdateTrackThickness(float thickness)
486 trackThickness = thickness;
488 if (calculator == null)
493 trackView.Size = calculator.CalculateTrackSize(thickness, containerSize, trackPadding);
497 /// Update ThumbThickness property of the scrollbar.
499 /// <param name="thickness">The width of the track.</param>
500 private void UpdateThumbThickness(float thickness)
502 thumbThickness = thickness;
504 if (calculator == null)
509 thumbView.Size = calculator.CalculateThumbSize(thickness, trackView.Size);
513 /// Update TrackPadding property of the scrollbar.
515 /// <param name="padding">The padding of the track.</param>
516 private void UpdateTrackPadding(Extents padding)
518 trackPadding = padding == null ? new PaddingType(0, 0, 0, 0) : new PaddingType(padding.Start, padding.End, padding.Top, padding.Bottom);
520 if (calculator == null)
525 trackView.Size = calculator.CalculateTrackSize(TrackThickness, containerSize, trackPadding);
526 trackView.Position = calculator.CalculateTrackPosition(trackPadding);
527 thumbView.Size = calculator.CalculateThumbSize(ThumbThickness, trackView.Size);
528 thumbView.Position = calculator.CalculateThumbPaddingPosition(trackView.Size, thumbView.Size, thumbView.Position, trackPadding);
530 private void UpdateThumbColor(Color color)
533 if (thumbView.ResourceUrl != "")
535 thumbView.Color = color;
536 thumbView.BackgroundColor = Color.Transparent;
540 thumbView.BackgroundColor = color;
544 private void UpdateThumbImage(string url, bool isHorizontal)
548 thumbHorizontalImageUrl = url;
549 if (this.isHorizontal)
551 thumbView.ResourceUrl = url;
552 thumbView.Color = thumbColor;
553 thumbView.BackgroundColor = Color.Transparent;
558 thumbVerticalImageUrl = url;
559 if (!this.isHorizontal)
561 thumbView.ResourceUrl = url;
562 thumbView.Color = thumbColor;
563 thumbView.BackgroundColor = Color.Transparent;
568 private Animation EnsureThumbPositionAnimation()
570 if (thumbPositionAnimation == null)
572 thumbPositionAnimation = new Animation();
576 thumbPositionAnimation.Stop();
577 thumbPositionAnimation.Clear();
579 return thumbPositionAnimation;
582 private Animation EnsureThumbSizeAnimation()
584 if (thumbSizeAnimation == null)
586 thumbSizeAnimation = new Animation();
590 thumbSizeAnimation.Stop();
591 thumbSizeAnimation.Clear();
593 return thumbSizeAnimation;
596 private Animation EnsureOpacityAnimation()
598 if (opacityAnimation == null)
600 opacityAnimation = new Animation();
604 opacityAnimation.Stop();
605 opacityAnimation.Clear();
607 return opacityAnimation;
615 private abstract class Calculator
617 public float contentLength;
618 public float visibleLength;
619 public float currentPosition;
621 public Calculator(float contentLength, float visibleLength, float currentPosition)
623 this.contentLength = contentLength;
624 this.visibleLength = visibleLength;
625 this.currentPosition = currentPosition;
628 public bool IsScrollable()
630 return contentLength > visibleLength;
633 public abstract Position CalculatorTrackAlign();
634 public abstract Position CalculatorThumbAlign();
635 public abstract Size CalculateTrackSize(float thickness, Size containerSize, PaddingType trackPadding);
636 public abstract Position CalculateTrackPosition(PaddingType trackPadding);
637 public abstract Size CalculateThumbSize(float thickness, Size trackSize);
638 public abstract Position CalculateThumbPosition(Size trackSize, Size thumbSize, PaddingType trackPadding);
639 public abstract Position CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Position thumbCurrentPosition, PaddingType trackPadding);
640 public abstract Position CalculateThumbScrollPosition(Size trackSize, Position thumbCurrentPosition, PaddingType trackPadding);
643 private class HorizontalCalculator : Calculator
645 public HorizontalCalculator(float contentLength, float visibleLength, float currentPosition) : base(contentLength, visibleLength, currentPosition)
649 public override Position CalculatorTrackAlign()
651 return Tizen.NUI.ParentOrigin.BottomLeft;
654 public override Position CalculatorThumbAlign()
656 return Tizen.NUI.ParentOrigin.BottomLeft;
659 public override Size CalculateTrackSize(float thickness, Size containerSize, PaddingType trackPadding)
661 return new Size(containerSize.Width - trackPadding.Item1 - trackPadding.Item2, thickness);
664 public override Position CalculateTrackPosition(PaddingType trackPadding)
666 return new Position(trackPadding.Item1, -trackPadding.Item4);
669 public override Size CalculateThumbSize(float thickness, Size trackSize)
671 return new Size(trackSize.Width * (IsScrollable() ? (visibleLength / contentLength) : 0.0f), thickness);
674 public override Position CalculateThumbPosition(Size trackSize, Size thumbSize, PaddingType trackPadding)
676 float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding.Item4;
677 return new Position(trackPadding.Item1 + (IsScrollable() ? (trackSize.Width * (Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength)) / contentLength) : 0.0f), -padding);
680 public override Position CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Position thumbCurrentPosition, PaddingType trackPadding)
682 float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding.Item4;
683 return new Position(thumbCurrentPosition.X, -padding);
686 public override Position CalculateThumbScrollPosition(Size trackSize, Position thumbCurrentPosition, PaddingType trackPadding)
688 return new Position(trackPadding.Item1 + (IsScrollable() ? (trackSize.Width * (Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength)) / contentLength) : 0.0f), thumbCurrentPosition.Y);
692 private class VerticalCalculator : Calculator
694 public VerticalCalculator(float contentLength, float visibleLength, float currentPosition) : base(contentLength, visibleLength, currentPosition)
698 public override Position CalculatorTrackAlign()
700 return Tizen.NUI.ParentOrigin.TopRight;
703 public override Position CalculatorThumbAlign()
705 return Tizen.NUI.ParentOrigin.TopRight;
708 public override Size CalculateTrackSize(float thickness, Size containerSize, PaddingType trackPadding)
710 return new Size(thickness, containerSize.Height - trackPadding.Item3 - trackPadding.Item4);
713 public override Position CalculateTrackPosition(PaddingType trackPadding)
715 return new Position(-trackPadding.Item2, trackPadding.Item3);
718 public override Size CalculateThumbSize(float thickness, Size trackSize)
720 return new Size(thickness, trackSize.Height * (IsScrollable() ? (visibleLength / contentLength) : 0.0f));
723 public override Position CalculateThumbPosition(Size trackSize, Size thumbSize, PaddingType trackPadding)
725 float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding.Item2;
726 return new Position(-padding, trackPadding.Item3 + (IsScrollable() ? (trackSize.Height * Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength) / contentLength) : 0.0f));
729 public override Position CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Position thumbCurrentPosition, PaddingType trackPadding)
731 float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding.Item2;
732 return new Position(-padding, thumbCurrentPosition.Y);
735 public override Position CalculateThumbScrollPosition(Size trackSize, Position thumbPosition, PaddingType trackPadding)
737 return new Position(thumbPosition.X, trackPadding.Item3 + (IsScrollable() ? (trackSize.Height * Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength) / contentLength) : 0.0f));