[NUI] Add ThemeManager (#2034)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Scrollbar.cs
1 /*
2  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17 using System;
18 using System.ComponentModel;
19 using Tizen.NUI.BaseComponents;
20 using Tizen.NUI.Binding;
21
22 namespace Tizen.NUI.Components
23 {
24     /// <summary>
25     /// The Scrollbar is a component that contains track and thumb to indicate the current scrolled position of a scrollable object.
26     /// </summary>
27     [EditorBrowsable(EditorBrowsableState.Never)]
28     public class Scrollbar : ScrollbarBase
29     {
30         #region Fields
31
32         /// <summary>Bindable property of TrackThickness</summary>
33         [EditorBrowsable(EditorBrowsableState.Never)]
34         public static readonly BindableProperty TrackThicknessProperty = BindableProperty.Create(nameof(TrackThickness), typeof(float), typeof(Scrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
35         {
36             var instance = ((Scrollbar)bindable);
37             var thickness = (float?)newValue;
38
39             instance.scrollbarStyle.TrackThickness = thickness;
40             instance.UpdateTrackThickness(thickness ?? 0);
41         },
42         defaultValueCreator: (bindable) =>
43         {
44             return ((Scrollbar)bindable).scrollbarStyle.TrackThickness ?? 0;
45         });
46
47         /// <summary>Bindable property of ThumbThickness</summary>
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         public static readonly BindableProperty ThumbThicknessProperty = BindableProperty.Create(nameof(ThumbThickness), typeof(float), typeof(Scrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
50         {
51             var instance = ((Scrollbar)bindable);
52             var thickness = (float?)newValue;
53
54             instance.scrollbarStyle.ThumbThickness = thickness;
55             instance.UpdateThumbThickness(thickness ?? 0);
56         },
57         defaultValueCreator: (bindable) =>
58         {
59             return ((Scrollbar)bindable).scrollbarStyle.ThumbThickness ?? 0;
60         });
61
62         /// <summary>Bindable property of TrackColor</summary>
63         [EditorBrowsable(EditorBrowsableState.Never)]
64         public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
65         {
66             var instance = ((Scrollbar)bindable);
67             var color = (Color)newValue;
68
69             instance.scrollbarStyle.TrackColor = color;
70             instance.UpdateTrackColor(color);
71         },
72         defaultValueCreator: (bindable) =>
73         {
74             return ((Scrollbar)bindable).scrollbarStyle.TrackColor;
75         });
76
77         /// <summary>Bindable property of ThumbColor</summary>
78         [EditorBrowsable(EditorBrowsableState.Never)]
79         public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
80         {
81             var instance = ((Scrollbar)bindable);
82             var color = (Color)newValue;
83
84             instance.scrollbarStyle.ThumbColor = color;
85             instance.UpdateThumbColor(color);
86         },
87         defaultValueCreator: (bindable) =>
88         {
89             return ((Scrollbar)bindable).scrollbarStyle.ThumbColor;
90         });
91
92         /// <summary>Bindable property of TrackPadding</summary>
93         [EditorBrowsable(EditorBrowsableState.Never)]
94         public static readonly BindableProperty TrackPaddingProperty = BindableProperty.Create(nameof(TrackPadding), typeof(Extents), typeof(Scrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
95         {
96             var instance = ((Scrollbar)bindable);
97             var trackPadding = (Extents)newValue;
98
99             instance.scrollbarStyle.TrackPadding = trackPadding;
100             instance.UpdateTrackPadding(trackPadding);
101         },
102         defaultValueCreator: (bindable) =>
103         {
104             return ((Scrollbar)bindable).scrollbarStyle.TrackPadding;
105         });
106
107         private ColorVisual trackVisual;
108         private ColorVisual thumbVisual;
109         private Animation thumbPositionAnimation;
110         private Animation thumbSizeAnimation;
111         private Calculator calculator;
112         private Size containerSize = new Size(0, 0);
113         private ScrollbarStyle scrollbarStyle => ViewStyle as ScrollbarStyle;
114         private bool mScrollEnabled = true;
115         private float previousPosition;
116
117         #endregion Fields
118
119
120         #region Constructors
121
122         /// <summary>
123         /// Create an empty Scrollbar.
124         /// </summary>
125         public Scrollbar() : base()
126         {
127         }
128
129         /// <summary>
130         /// Create a Scrollbar and initialize with properties.
131         /// </summary>
132         /// <param name="contentLength">The length of the scrollable content area.</param>
133         /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
134         /// <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>
135         /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
136         [EditorBrowsable(EditorBrowsableState.Never)]
137         public Scrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : this()
138         {
139             Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
140         }
141
142         /// <summary>
143         /// Create an empty Scrollbar with a ScrollbarStyle instance to set style properties.
144         /// </summary>
145         [EditorBrowsable(EditorBrowsableState.Never)]
146         public Scrollbar(ScrollbarStyle style) : base(style)
147         {
148         }
149
150         /// <summary>
151         /// Static constructor to initialize bindable properties when loading.
152         /// </summary>
153         static Scrollbar()
154         {
155         }
156
157         #endregion Constructors
158
159
160         #region Properties
161
162         /// <summary>
163         /// Return a copied Style instance of Scrollbar
164         /// </summary>
165         /// <remarks>
166         /// It returns copied Style instance and changing it does not effect to the Scrollbar.
167         /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
168         /// </remarks>
169         [EditorBrowsable(EditorBrowsableState.Never)]
170         public new ScrollbarStyle Style
171         {
172             get
173             {
174                 var result = new ScrollbarStyle(scrollbarStyle);
175                 result.CopyPropertiesFromView(this);
176                 return result;
177             }
178         }
179
180         /// <summary>
181         /// The thickness of the track.
182         /// </summary>
183         [EditorBrowsable(EditorBrowsableState.Never)]
184         public float TrackThickness
185         {
186             get => (float)GetValue(TrackThicknessProperty);
187             set => SetValue(TrackThicknessProperty, value);
188         }
189
190         /// <summary>
191         /// The thickness of the thumb.
192         /// </summary>
193         [EditorBrowsable(EditorBrowsableState.Never)]
194         public float ThumbThickness
195         {
196             get => (float)GetValue(ThumbThicknessProperty);
197             set => SetValue(ThumbThicknessProperty, value);
198         }
199
200         /// <summary>
201         /// The color of the track part.
202         /// </summary>
203         [EditorBrowsable(EditorBrowsableState.Never)]
204         public Color TrackColor
205         {
206             get => (Color)GetValue(TrackColorProperty);
207             set => SetValue(TrackColorProperty, value);
208         }
209
210         /// <summary>
211         /// The color of the thumb part.
212         /// </summary>
213         [EditorBrowsable(EditorBrowsableState.Never)]
214         public Color ThumbColor
215         {
216             get => (Color)GetValue(ThumbColorProperty);
217             set => SetValue(ThumbColorProperty, value);
218         }
219
220         /// <summary>
221         /// The padding value of the track.
222         /// </summary>
223         [EditorBrowsable(EditorBrowsableState.Never)]
224         public Extents TrackPadding
225         {
226             get => (Extents)GetValue(TrackPaddingProperty);
227             set => SetValue(TrackPaddingProperty, value);
228         }
229
230         #endregion Properties
231
232
233         #region Methods
234
235         /// <inheritdoc/>
236         [EditorBrowsable(EditorBrowsableState.Never)]
237         public override void Initialize(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false)
238         {
239             if (isHorizontal)
240             {
241                 calculator = new HorizontalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewportLength, currentPosition);
242             }
243             else
244             {
245                 calculator = new VerticalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewportLength, currentPosition);
246             }
247
248             thumbPositionAnimation?.Stop();
249             thumbPositionAnimation = null;
250
251             thumbSizeAnimation?.Stop();
252             thumbSizeAnimation = null;
253
254             Size trackSize = calculator.CalculateTrackSize(TrackThickness, containerSize, TrackPadding);
255             Vector2 trackPosition = calculator.CalculateTrackPosition(TrackPadding);
256             Size thumbSize = calculator.CalculateThumbSize(ThumbThickness, trackSize);
257             Vector2 thumbPosition = calculator.CalculateThumbPosition(trackSize, thumbSize, TrackPadding);
258
259             if (trackVisual == null)
260             {
261                 trackVisual = new ColorVisual
262                 {
263                     SuppressUpdateVisual = true,
264                     Color = TrackColor,
265                     SizePolicy = VisualTransformPolicyType.Absolute,
266                     Origin = calculator.CalculatorTrackAlign(),
267                     AnchorPoint = calculator.CalculatorTrackAlign(),
268                     Size = trackSize,
269                     Position = trackPosition,
270                 };
271
272                 AddVisual("Track", trackVisual);
273             }
274             else
275             {
276                 trackVisual.Size = trackSize;
277                 trackVisual.Position = trackPosition;
278                 trackVisual.UpdateVisual(true);
279             }
280
281             if (thumbVisual == null)
282             {
283                 thumbVisual = new ColorVisual
284                 {
285                     SuppressUpdateVisual = true,
286                     MixColor = ThumbColor,
287                     SizePolicy = VisualTransformPolicyType.Absolute,
288                     Origin = calculator.CalculatorThumbAlign(),
289                     AnchorPoint = calculator.CalculatorThumbAlign(),
290                     Opacity = calculator.CalculateThumbVisibility() ? 1.0f : 0.0f,
291                     Size = thumbSize,
292                     Position = thumbPosition,
293                 };
294
295                 AddVisual("Thumb", thumbVisual);
296             }
297             else
298             {
299                 thumbVisual.Size = thumbSize;
300                 thumbVisual.Position = thumbPosition;
301                 thumbVisual.UpdateVisual(true);
302             }
303         }
304
305         /// <inheritdoc/>
306         /// <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>
307         [EditorBrowsable(EditorBrowsableState.Never)]
308         public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
309         {
310             if (calculator == null)
311             {
312                 return;
313             }
314
315             calculator.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
316             previousPosition = calculator.currentPosition;
317             calculator.currentPosition = position;
318
319             thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
320             thumbVisual.Position = calculator.CalculateThumbScrollPosition(trackVisual.Size, thumbVisual.Position, TrackPadding);
321             thumbVisual.Opacity = calculator.CalculateThumbVisibility() ? 1.0f : 0.0f;
322
323             if (durationMs == 0)
324             {
325                 thumbVisual.UpdateVisual(true);
326
327                 return;
328             }
329
330             // TODO Support non built-in alpha function for visual trainsition in DALi.
331             AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
332
333             thumbPositionAnimation?.Stop();
334             thumbPositionAnimation = AnimateVisual(thumbVisual, "position", thumbVisual.Position, 0, (int)durationMs, builtinAlphaFunction);
335             thumbPositionAnimation.Play();
336
337             thumbSizeAnimation?.Stop();
338             thumbSizeAnimation = AnimateVisual(thumbVisual, "size", thumbVisual.Size, 0, (int)durationMs, builtinAlphaFunction);
339             thumbSizeAnimation.Play();
340         }
341
342         /// <inheritdoc/>
343         /// <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>
344         [EditorBrowsable(EditorBrowsableState.Never)]
345         public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
346         {
347             if (mScrollEnabled == false)
348             {
349                 return;
350             }
351
352             if (calculator == null)
353             {
354                 return;
355             }
356
357             previousPosition = calculator.currentPosition;
358             calculator.currentPosition = position;
359             thumbVisual.Position = calculator.CalculateThumbScrollPosition(trackVisual.Size, thumbVisual.Position, TrackPadding);
360
361             if (durationMs == 0)
362             {
363                 thumbVisual.UpdateVisual(true);
364
365                 return;
366             }
367
368             // TODO Support non built-in alpha function for visual trainsition in DALi.
369             AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
370
371             thumbPositionAnimation?.Stop();
372             thumbPositionAnimation = AnimateVisual(thumbVisual, "position", thumbVisual.Position, 0, (int)durationMs, builtinAlphaFunction);
373             thumbPositionAnimation.Play();
374         }
375
376         /// <inheritdoc/>
377         [EditorBrowsable(EditorBrowsableState.Never)]
378         public override void OnRelayout(Vector2 size, RelayoutContainer container)
379         {
380             base.OnRelayout(size, container);
381
382             if (size.Width == containerSize.Width && size.Height == containerSize.Height)
383             {
384                 return;
385             }
386
387             containerSize = new Size(size.Width, size.Height);
388
389             if (calculator == null)
390             {
391                 return;
392             }
393
394             trackVisual.Size = calculator.CalculateTrackSize(TrackThickness, containerSize, TrackPadding);
395             trackVisual.Position = calculator.CalculateTrackPosition(TrackPadding);
396             thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
397             thumbVisual.Position = calculator.CalculateThumbPosition(trackVisual.Size, thumbVisual.Size, TrackPadding);
398
399             trackVisual.UpdateVisual(true);
400             thumbVisual.UpdateVisual(true);
401         }
402
403         /// <inheritdoc/>
404         [EditorBrowsable(EditorBrowsableState.Never)]
405         public override void ApplyStyle(ViewStyle viewStyle)
406         {
407             base.ApplyStyle(viewStyle);
408
409             if (viewStyle is ScrollbarStyle scrollbarStyle)
410             {
411                 // Apply essential look.
412                 if (scrollbarStyle.TrackThickness == null) scrollbarStyle.TrackThickness = 6.0f;
413                 if (scrollbarStyle.ThumbThickness == null) scrollbarStyle.ThumbThickness = 6.0f;
414                 if (scrollbarStyle.TrackColor == null) scrollbarStyle.TrackColor = new Color(1.0f, 1.0f, 1.0f, 0.15f);
415                 if (scrollbarStyle.ThumbColor == null) scrollbarStyle.ThumbColor = new Color(0.6f, 0.6f, 0.6f, 1.0f);
416                 if (scrollbarStyle.TrackPadding == null) scrollbarStyle.TrackPadding = 4;
417                 if (scrollbarStyle.WidthResizePolicy == null) scrollbarStyle.WidthResizePolicy = ResizePolicyType.FillToParent;
418                 if (scrollbarStyle.HeightResizePolicy == null) scrollbarStyle.HeightResizePolicy = ResizePolicyType.FillToParent;
419             }
420         }
421
422         /// <inheritdoc/>
423         [EditorBrowsable(EditorBrowsableState.Never)]
424         protected override ViewStyle CreateViewStyle()
425         {
426             return new ScrollbarStyle();
427         }
428
429         /// <summary>
430         /// Update TrackThickness property of the scrollbar.
431         /// </summary>
432         /// <param name="thickness">The width of the track.</param>
433         [EditorBrowsable(EditorBrowsableState.Never)]
434         protected virtual void UpdateTrackThickness(float thickness)
435         {
436             if (trackVisual == null)
437             {
438                 return;
439             }
440
441             trackVisual.Size = calculator.CalculateTrackSize(thickness, containerSize, TrackPadding);
442             trackVisual.UpdateVisual(true);
443         }
444
445         /// <summary>
446         /// Update ThumbThickness property of the scrollbar.
447         /// </summary>
448         /// <param name="thickness">The width of the track.</param>
449         [EditorBrowsable(EditorBrowsableState.Never)]
450         protected virtual void UpdateThumbThickness(float thickness)
451         {
452             if (thumbVisual == null)
453             {
454                 return;
455             }
456
457             thumbVisual.Size = calculator.CalculateThumbSize(thickness, trackVisual.Size);
458             thumbVisual.UpdateVisual(true);
459         }
460
461         /// <summary>
462         /// Update TrackColor property of the scrollbar.
463         /// </summary>
464         /// <param name="color">The color of the track.</param>
465         [EditorBrowsable(EditorBrowsableState.Never)]
466         protected virtual void UpdateTrackColor(Color color)
467         {
468             if (trackVisual == null)
469             {
470                 return;
471             }
472
473             trackVisual.MixColor = color;
474             trackVisual.UpdateVisual(true);
475         }
476
477         /// <summary>
478         /// Update ThumbColor property of the scrollbar.
479         /// </summary>
480         /// <param name="color">The color of the thumb.</param>
481         [EditorBrowsable(EditorBrowsableState.Never)]
482         protected virtual void UpdateThumbColor(Color color)
483         {
484             if (thumbVisual == null)
485             {
486                 return;
487             }
488
489             thumbVisual.MixColor = color;
490             thumbVisual.UpdateVisual(true);
491         }
492
493         /// <summary>
494         /// Update TrackPadding property of the scrollbar.
495         /// </summary>
496         /// <param name="trackPadding">The padding of the track.</param>
497         protected virtual void UpdateTrackPadding(Extents trackPadding)
498         {
499             if (calculator == null)
500             {
501                 return;
502             }
503
504             trackVisual.Size = calculator.CalculateTrackSize(TrackThickness, containerSize, trackPadding); 
505             trackVisual.Position = calculator.CalculateTrackPosition(trackPadding);
506             thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
507             thumbVisual.Position = calculator.CalculateThumbPaddingPosition(trackVisual.Size, thumbVisual.Size, thumbVisual.Position, trackPadding);
508
509             trackVisual.UpdateVisual(true);
510             thumbVisual.UpdateVisual(true);
511         }
512
513         /// <inheritdoc/>
514         [EditorBrowsable(EditorBrowsableState.Never)]
515         public override bool ScrollEnabled
516         {
517             get
518             {
519                 return mScrollEnabled;
520             }
521             set
522             {
523                 if (value != mScrollEnabled)
524                 {
525                     mScrollEnabled = value;
526                 }
527             }
528         }
529
530         /// <inheritdoc/>
531         [EditorBrowsable(EditorBrowsableState.Never)]
532         public override Position ScrollPosition
533         {
534             get
535             {
536                 if (calculator == null)
537                 {
538                     return new Position(0.0f, 0.0f);
539                 }
540
541                 float length = Math.Min(Math.Max(calculator.currentPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
542
543                 if (calculator is HorizontalCalculator)
544                 {
545                     return new Position(length, 0.0f);
546                 }
547                 else
548                 {
549                     return new Position(0.0f, length);
550                 }
551             }
552         }
553
554         /// <inheritdoc/>
555         [EditorBrowsable(EditorBrowsableState.Never)]
556         public override Position ScrollCurrentPosition
557         {
558             get
559             {
560                 if (calculator == null)
561                 {
562                     return new Position(0.0f, 0.0f);
563                 }
564
565                 float length = Math.Min(Math.Max(calculator.currentPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
566
567                 if (thumbPositionAnimation != null)
568                 {
569                     float progress = thumbPositionAnimation.CurrentProgress;
570                     float previousLength = Math.Min(Math.Max(previousPosition, 0.0f), calculator.contentLength - calculator.visibleLength);
571
572                     length = ((1.0f - progress) * previousLength) + (progress * length);
573                 }
574
575                 if (calculator is HorizontalCalculator)
576                 {
577                     return new Position(length, 0.0f);
578                 }
579                 else
580                 {
581                     return new Position(0.0f, length);
582                 }
583             }
584         }
585
586         #endregion Methods
587
588
589         #region Classes
590
591         private abstract class Calculator
592         {
593             public float contentLength;
594             public float visibleLength;
595             public float currentPosition;
596
597             public Calculator(float contentLength, float visibleLength, float currentPosition)
598             {
599                 this.contentLength = contentLength;
600                 this.visibleLength = visibleLength;
601                 this.currentPosition = currentPosition;
602             }
603
604             public bool CalculateThumbVisibility()
605             {
606                 return contentLength > visibleLength;
607             }
608
609             public abstract Visual.AlignType CalculatorTrackAlign();
610             public abstract Visual.AlignType CalculatorThumbAlign();
611             public abstract Size CalculateTrackSize(float thickness, Size containerSize, Extents trackPadding);
612             public abstract Vector2 CalculateTrackPosition(Extents trackPadding);
613             public abstract Size CalculateThumbSize(float thickness, Size trackSize);
614             public abstract Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, Extents trackPadding);
615             public abstract Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding);
616             public abstract Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbCurrentPosition, Extents trackPadding);
617         }
618
619         private class HorizontalCalculator : Calculator
620         {
621             public HorizontalCalculator(float contentLength, float visibleLength, float currentPosition) : base(contentLength, visibleLength, currentPosition)
622             {
623             }
624
625             public override Visual.AlignType CalculatorTrackAlign()
626             {
627                 return Visual.AlignType.BottomCenter;
628             }
629
630             public override Visual.AlignType CalculatorThumbAlign()
631             {
632                 return Visual.AlignType.BottomBegin;
633             }
634
635             public override Size CalculateTrackSize(float thickness, Size containerSize, Extents trackPadding)
636             {
637                 return new Size(containerSize.Width - trackPadding?.Start??0 - trackPadding?.End??0, thickness);
638             }
639
640             public override Vector2 CalculateTrackPosition(Extents trackPadding)
641             {
642                 return new Vector2(0, -trackPadding?.Bottom??0);
643             }
644
645             public override Size CalculateThumbSize(float thickness, Size trackSize)
646             {
647                 return new Size(trackSize.Width * visibleLength / contentLength, thickness);
648             }
649
650             public override Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, Extents trackPadding)
651             {
652                 float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding?.Bottom??0;
653                 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
654                 return new Vector2(trackPadding?.Start??0 + trackSize.Width * pos / contentLength, -padding);
655             }
656
657             public override Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding)
658             {
659                 float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding?.Bottom??0;
660                 return new Vector2(thumbCurrentPosition.X, -padding);
661             }
662
663             public override Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbCurrentPosition, Extents trackPadding)
664             {
665                 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
666                 return new Vector2(trackPadding?.Start??0 + trackSize.Width * pos / contentLength, thumbCurrentPosition.Y);
667             }
668         }
669
670         private class VerticalCalculator : Calculator
671         {
672             public VerticalCalculator(float contentLength, float visibleLength, float currentPosition) : base(contentLength, visibleLength, currentPosition)
673             {
674             }
675
676             public override Visual.AlignType CalculatorTrackAlign()
677             {
678                 return Visual.AlignType.CenterEnd;
679             }
680
681             public override Visual.AlignType CalculatorThumbAlign()
682             {
683                 return Visual.AlignType.TopEnd;
684             }
685
686             public override Size CalculateTrackSize(float thickness, Size containerSize, Extents trackPadding)
687             {
688                 return new Size(thickness, containerSize.Height - trackPadding?.Top??0 - trackPadding?.Bottom??0);
689             }
690
691             public override Vector2 CalculateTrackPosition(Extents trackPadding)
692             {
693                 return new Vector2(-trackPadding?.End??0, 0);
694             }
695
696             public override Size CalculateThumbSize(float thickness, Size trackSize)
697             {
698                 return new Size(thickness, trackSize.Height * visibleLength / contentLength);
699             }
700
701             public override Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, Extents trackPadding)
702             {
703                 float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding?.End??0;
704                 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
705                 return new Vector2(-padding, trackPadding?.Top??0 + trackSize.Height * pos / contentLength);
706             }
707
708             public override Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding)
709             {
710                 float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding?.End??0;
711                 return new Vector2(-padding, thumbCurrentPosition.Y);
712             }
713
714             public override Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbPosition, Extents trackPadding)
715             {
716                 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
717                 return new Vector2(thumbPosition.X, trackPadding?.Top??0 + trackSize.Height * pos / contentLength);
718             }
719         }
720
721         #endregion Classes
722     }
723 }