Release 8.0.0.15408
[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
115         #endregion Fields
116
117
118         #region Constructors
119
120         /// <summary>
121         /// Create an empty Scrollbar.
122         /// </summary>
123         public Scrollbar() : base(new ScrollbarStyle())
124         {
125         }
126
127         /// <summary>
128         /// Create a Scrollbar and initialize with properties.
129         /// </summary>
130         /// <param name="contentLength">The length of the scrollable content area.</param>
131         /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
132         /// <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>
133         /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
134         [EditorBrowsable(EditorBrowsableState.Never)]
135         public Scrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : base(new ScrollbarStyle())
136         {
137             Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
138         }
139
140         /// <summary>
141         /// Create an empty Scrollbar with a ScrollbarStyle instance to set style properties.
142         /// </summary>
143         [EditorBrowsable(EditorBrowsableState.Never)]
144         public Scrollbar(ScrollbarStyle style) : base(style)
145         {
146         }
147
148         /// <summary>
149         /// Static constructor to initialize bindable properties when loading.
150         /// </summary>
151         static Scrollbar()
152         {
153         }
154
155         #endregion Constructors
156
157
158         #region Properties
159
160         /// <summary>
161         /// Return a copied Style instance of Scrollbar
162         /// </summary>
163         /// <remarks>
164         /// It returns copied Style instance and changing it does not effect to the Scrollbar.
165         /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
166         /// </remarks>
167         [EditorBrowsable(EditorBrowsableState.Never)]
168         public new ScrollbarStyle Style
169         {
170             get
171             {
172                 var result = new ScrollbarStyle(scrollbarStyle);
173                 result.CopyPropertiesFromView(this);
174                 return result;
175             }
176         }
177
178         /// <summary>
179         /// The thickness of the track.
180         /// </summary>
181         [EditorBrowsable(EditorBrowsableState.Never)]
182         public float TrackThickness
183         {
184             get => (float)GetValue(TrackThicknessProperty);
185             set => SetValue(TrackThicknessProperty, value);
186         }
187
188         /// <summary>
189         /// The thickness of the thumb.
190         /// </summary>
191         [EditorBrowsable(EditorBrowsableState.Never)]
192         public float ThumbThickness
193         {
194             get => (float)GetValue(ThumbThicknessProperty);
195             set => SetValue(ThumbThicknessProperty, value);
196         }
197
198         /// <summary>
199         /// The color of the track part.
200         /// </summary>
201         [EditorBrowsable(EditorBrowsableState.Never)]
202         public Color TrackColor
203         {
204             get => (Color)GetValue(TrackColorProperty);
205             set => SetValue(TrackColorProperty, value);
206         }
207
208         /// <summary>
209         /// The color of the thumb part.
210         /// </summary>
211         [EditorBrowsable(EditorBrowsableState.Never)]
212         public Color ThumbColor
213         {
214             get => (Color)GetValue(ThumbColorProperty);
215             set => SetValue(ThumbColorProperty, value);
216         }
217
218         /// <summary>
219         /// The padding value of the track.
220         /// </summary>
221         [EditorBrowsable(EditorBrowsableState.Never)]
222         public Extents TrackPadding
223         {
224             get => (Extents)GetValue(TrackPaddingProperty);
225             set => SetValue(TrackPaddingProperty, value);
226         }
227
228         #endregion Properties
229
230
231         #region Methods
232
233         /// <inheritdoc/>
234         [EditorBrowsable(EditorBrowsableState.Never)]
235         public override void Initialize(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false)
236         {
237             if (isHorizontal)
238             {
239                 calculator = new HorizontalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewportLength, currentPosition);
240             }
241             else
242             {
243                 calculator = new VerticalCalculator(contentLength > 0.0f ? contentLength : 0.0f, viewportLength, currentPosition);
244             }
245
246             thumbPositionAnimation?.Stop();
247             thumbPositionAnimation = null;
248
249             thumbSizeAnimation?.Stop();
250             thumbSizeAnimation = null;
251
252             Size trackSize = calculator.CalculateTrackSize(TrackThickness, containerSize, TrackPadding);
253             Vector2 trackPosition = calculator.CalculateTrackPosition(TrackPadding);
254             Size thumbSize = calculator.CalculateThumbSize(ThumbThickness, trackSize);
255             Vector2 thumbPosition = calculator.CalculateThumbPosition(trackSize, thumbSize, TrackPadding);
256
257             if (trackVisual == null)
258             {
259                 trackVisual = new ColorVisual
260                 {
261                     SuppressUpdateVisual = true,
262                     Color = TrackColor,
263                     SizePolicy = VisualTransformPolicyType.Absolute,
264                     Origin = calculator.CalculatorTrackAlign(),
265                     AnchorPoint = calculator.CalculatorTrackAlign(),
266                     Size = trackSize,
267                     Position = trackPosition,
268                 };
269
270                 AddVisual("Track", trackVisual);
271             }
272             else
273             {
274                 trackVisual.Size = trackSize;
275                 trackVisual.Position = trackPosition;
276                 trackVisual.UpdateVisual(true);
277             }
278
279             if (thumbVisual == null)
280             {
281                 thumbVisual = new ColorVisual
282                 {
283                     SuppressUpdateVisual = true,
284                     MixColor = ThumbColor,
285                     SizePolicy = VisualTransformPolicyType.Absolute,
286                     Origin = calculator.CalculatorThumbAlign(),
287                     AnchorPoint = calculator.CalculatorThumbAlign(),
288                     Opacity = calculator.CalculateThumbVisibility() ? 1.0f : 0.0f,
289                     Size = thumbSize,
290                     Position = thumbPosition,
291                 };
292
293                 AddVisual("Thumb", thumbVisual);
294             }
295             else
296             {
297                 thumbVisual.Size = thumbSize;
298                 thumbVisual.Position = thumbPosition;
299                 thumbVisual.UpdateVisual(true);
300             }
301         }
302
303         /// <inheritdoc/>
304         /// <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>
305         [EditorBrowsable(EditorBrowsableState.Never)]
306         public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
307         {
308             if (calculator == null)
309             {
310                 return;
311             }
312
313             calculator.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
314             calculator.currentPosition = position;
315
316             thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
317             thumbVisual.Position = calculator.CalculateThumbScrollPosition(trackVisual.Size, thumbVisual.Position, TrackPadding);
318             thumbVisual.Opacity = calculator.CalculateThumbVisibility() ? 1.0f : 0.0f;
319
320             if (durationMs == 0)
321             {
322                 thumbVisual.UpdateVisual(true);
323
324                 return;
325             }
326
327             // TODO Support non built-in alpha function for visual trainsition in DALi.
328             AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
329
330             thumbPositionAnimation?.Stop();
331             thumbPositionAnimation = AnimateVisual(thumbVisual, "position", thumbVisual.Position, 0, (int)durationMs, builtinAlphaFunction);
332             thumbPositionAnimation.Play();
333
334             thumbSizeAnimation?.Stop();
335             thumbSizeAnimation = AnimateVisual(thumbVisual, "size", thumbVisual.Size, 0, (int)durationMs, builtinAlphaFunction);
336             thumbSizeAnimation.Play();
337         }
338
339         /// <inheritdoc/>
340         /// <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>
341         [EditorBrowsable(EditorBrowsableState.Never)]
342         public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
343         {
344             if (calculator == null)
345             {
346                 return;
347             }
348
349             calculator.currentPosition = position;
350             thumbVisual.Position = calculator.CalculateThumbScrollPosition(trackVisual.Size, thumbVisual.Position, TrackPadding);
351
352             if (durationMs == 0)
353             {
354                 thumbVisual.UpdateVisual(true);
355
356                 return;
357             }
358
359             // TODO Support non built-in alpha function for visual trainsition in DALi.
360             AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
361
362             thumbPositionAnimation?.Stop();
363             thumbPositionAnimation = AnimateVisual(thumbVisual, "position", thumbVisual.Position, 0, (int)durationMs, builtinAlphaFunction);
364             thumbPositionAnimation.Play();
365         }
366
367         /// <inheritdoc/>
368         [EditorBrowsable(EditorBrowsableState.Never)]
369         public override void OnRelayout(Vector2 size, RelayoutContainer container)
370         {
371             base.OnRelayout(size, container);
372
373             if (size.Width == containerSize.Width && size.Height == containerSize.Height)
374             {
375                 return;
376             }
377
378             containerSize = new Size(size.Width, size.Height);
379
380             if (calculator == null)
381             {
382                 return;
383             }
384
385             trackVisual.Size = calculator.CalculateTrackSize(TrackThickness, containerSize, TrackPadding);
386             trackVisual.Position = calculator.CalculateTrackPosition(TrackPadding);
387             thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
388             thumbVisual.Position = calculator.CalculateThumbPosition(trackVisual.Size, thumbVisual.Size, TrackPadding);
389
390             trackVisual.UpdateVisual(true);
391             thumbVisual.UpdateVisual(true);
392         }
393
394         /// <inheritdoc/>
395         [EditorBrowsable(EditorBrowsableState.Never)]
396         protected override ViewStyle CreateViewStyle()
397         {
398             return new ScrollbarStyle();
399         }
400
401         /// <summary>
402         /// Update TrackThickness property of the scrollbar.
403         /// </summary>
404         /// <param name="thickness">The width of the track.</param>
405         [EditorBrowsable(EditorBrowsableState.Never)]
406         protected virtual void UpdateTrackThickness(float thickness)
407         {
408             if (trackVisual == null)
409             {
410                 return;
411             }
412
413             trackVisual.Size = calculator.CalculateTrackSize(thickness, containerSize, TrackPadding);
414             trackVisual.UpdateVisual(true);
415         }
416
417         /// <summary>
418         /// Update ThumbThickness property of the scrollbar.
419         /// </summary>
420         /// <param name="thickness">The width of the track.</param>
421         [EditorBrowsable(EditorBrowsableState.Never)]
422         protected virtual void UpdateThumbThickness(float thickness)
423         {
424             if (thumbVisual == null)
425             {
426                 return;
427             }
428
429             thumbVisual.Size = calculator.CalculateThumbSize(thickness, trackVisual.Size);
430             thumbVisual.UpdateVisual(true);
431         }
432
433         /// <summary>
434         /// Update TrackColor property of the scrollbar.
435         /// </summary>
436         /// <param name="color">The color of the track.</param>
437         [EditorBrowsable(EditorBrowsableState.Never)]
438         protected virtual void UpdateTrackColor(Color color)
439         {
440             if (trackVisual == null)
441             {
442                 return;
443             }
444
445             trackVisual.MixColor = color;
446             trackVisual.UpdateVisual(true);
447         }
448
449         /// <summary>
450         /// Update ThumbColor property of the scrollbar.
451         /// </summary>
452         /// <param name="color">The color of the thumb.</param>
453         [EditorBrowsable(EditorBrowsableState.Never)]
454         protected virtual void UpdateThumbColor(Color color)
455         {
456             if (thumbVisual == null)
457             {
458                 return;
459             }
460
461             thumbVisual.MixColor = color;
462             thumbVisual.UpdateVisual(true);
463         }
464
465         /// <summary>
466         /// Update TrackPadding property of the scrollbar.
467         /// </summary>
468         /// <param name="trackPadding">The padding of the track.</param>
469         protected virtual void UpdateTrackPadding(Extents trackPadding)
470         {
471             if (calculator == null)
472             {
473                 return;
474             }
475
476             trackVisual.Size = calculator.CalculateTrackSize(TrackThickness, containerSize, trackPadding); 
477             trackVisual.Position = calculator.CalculateTrackPosition(trackPadding);
478             thumbVisual.Size = calculator.CalculateThumbSize(ThumbThickness, trackVisual.Size);
479             thumbVisual.Position = calculator.CalculateThumbPaddingPosition(trackVisual.Size, thumbVisual.Size, thumbVisual.Position, trackPadding);
480
481             trackVisual.UpdateVisual(true);
482             thumbVisual.UpdateVisual(true);
483         }
484
485         #endregion Methods
486
487
488         #region Classes
489
490         private abstract class Calculator
491         {
492             public float contentLength;
493             public float visibleLength;
494             public float currentPosition;
495
496             public Calculator(float contentLength, float visibleLength, float currentPosition)
497             {
498                 this.contentLength = contentLength;
499                 this.visibleLength = visibleLength;
500                 this.currentPosition = currentPosition;
501             }
502
503             public bool CalculateThumbVisibility()
504             {
505                 return contentLength > visibleLength;
506             }
507
508             public abstract Visual.AlignType CalculatorTrackAlign();
509             public abstract Visual.AlignType CalculatorThumbAlign();
510             public abstract Size CalculateTrackSize(float thickness, Size containerSize, Extents trackPadding);
511             public abstract Vector2 CalculateTrackPosition(Extents trackPadding);
512             public abstract Size CalculateThumbSize(float thickness, Size trackSize);
513             public abstract Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, Extents trackPadding);
514             public abstract Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding);
515             public abstract Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbCurrentPosition, Extents trackPadding);
516         }
517
518         private class HorizontalCalculator : Calculator
519         {
520             public HorizontalCalculator(float contentLength, float visibleLength, float currentPosition) : base(contentLength, visibleLength, currentPosition)
521             {
522             }
523
524             public override Visual.AlignType CalculatorTrackAlign()
525             {
526                 return Visual.AlignType.BottomCenter;
527             }
528
529             public override Visual.AlignType CalculatorThumbAlign()
530             {
531                 return Visual.AlignType.BottomBegin;
532             }
533
534             public override Size CalculateTrackSize(float thickness, Size containerSize, Extents trackPadding)
535             {
536                 return new Size(containerSize.Width - trackPadding.Start - trackPadding.End, thickness);
537             }
538
539             public override Vector2 CalculateTrackPosition(Extents trackPadding)
540             {
541                 return new Vector2(0, -trackPadding.Bottom);
542             }
543
544             public override Size CalculateThumbSize(float thickness, Size trackSize)
545             {
546                 return new Size(trackSize.Width * visibleLength / contentLength, thickness);
547             }
548
549             public override Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, Extents trackPadding)
550             {
551                 float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding.Bottom;
552                 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
553                 return new Vector2(trackPadding.Start + trackSize.Width * pos / contentLength, -padding);
554             }
555
556             public override Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding)
557             {
558                 float padding = ((trackSize.Height - thumbSize.Height) / 2.0f) + trackPadding.Bottom;
559                 return new Vector2(thumbCurrentPosition.X, -padding);
560             }
561
562             public override Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbCurrentPosition, Extents trackPadding)
563             {
564                 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
565                 return new Vector2(trackPadding.Start + trackSize.Width * pos / contentLength, thumbCurrentPosition.Y);
566             }
567         }
568
569         private class VerticalCalculator : Calculator
570         {
571             public VerticalCalculator(float contentLength, float visibleLength, float currentPosition) : base(contentLength, visibleLength, currentPosition)
572             {
573             }
574
575             public override Visual.AlignType CalculatorTrackAlign()
576             {
577                 return Visual.AlignType.CenterEnd;
578             }
579
580             public override Visual.AlignType CalculatorThumbAlign()
581             {
582                 return Visual.AlignType.TopEnd;
583             }
584
585             public override Size CalculateTrackSize(float thickness, Size containerSize, Extents trackPadding)
586             {
587                 return new Size(thickness, containerSize.Height - trackPadding.Top - trackPadding.Bottom);
588             }
589
590             public override Vector2 CalculateTrackPosition(Extents trackPadding)
591             {
592                 return new Vector2(-trackPadding.End, 0);
593             }
594
595             public override Size CalculateThumbSize(float thickness, Size trackSize)
596             {
597                 return new Size(thickness, trackSize.Height * visibleLength / contentLength);
598             }
599
600             public override Vector2 CalculateThumbPosition(Size trackSize, Size thumbSize, Extents trackPadding)
601             {
602                 float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding.End;
603                 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
604                 return new Vector2(-padding, trackPadding.Top + trackSize.Height * pos / contentLength);
605             }
606
607             public override Vector2 CalculateThumbPaddingPosition(Size trackSize, Size thumbSize, Vector2 thumbCurrentPosition, Extents trackPadding)
608             {
609                 float padding = ((trackSize.Width - thumbSize.Width) / 2.0f) + trackPadding.End;
610                 return new Vector2(-padding, thumbCurrentPosition.Y);
611             }
612
613             public override Vector2 CalculateThumbScrollPosition(Size trackSize, Vector2 thumbPosition, Extents trackPadding)
614             {
615                 float pos = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
616                 return new Vector2(thumbPosition.X, trackPadding.Top + trackSize.Height * pos / contentLength);
617             }
618         }
619
620         #endregion Classes
621     }
622 }