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