[NUI] replace viewStlye with Style (#1679)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Wearable / src / public / CircularScrollbar.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 using Tizen.NUI.Components;
22
23 namespace Tizen.NUI.Wearable
24 {
25     /// <summary>
26     /// The CircualrScrollbar is a wearable NUI component that can be linked to the scrollable objects
27     /// indicating the current scroll position of the scrollable object.<br />
28     /// </summary>
29     [EditorBrowsable(EditorBrowsableState.Never)]
30     public class CircularScrollbar : ScrollbarBase
31     {
32         #region Fields
33
34         /// <summary>Bindable property of Thickness</summary>
35         [EditorBrowsable(EditorBrowsableState.Never)]
36         public static readonly BindableProperty ThicknessProperty = BindableProperty.Create(nameof(Thickness), typeof(float), typeof(CircularScrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
37         {
38             var instance = ((CircularScrollbar)bindable);
39             var thickness = (float?)newValue;
40
41             instance.Style.Thickness = thickness;
42             instance.UpdateVisualThickness(thickness ?? 0);
43         },
44         defaultValueCreator: (bindable) =>
45         {
46             return ((CircularScrollbar)bindable).Style.Thickness ?? 0;
47         });
48
49         /// <summary>Bindable property of TrackSweepAngle</summary>
50         [EditorBrowsable(EditorBrowsableState.Never)]
51         public static readonly BindableProperty TrackSweepAngleProperty = BindableProperty.Create(nameof(TrackSweepAngle), typeof(float), typeof(CircularScrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
52         {
53             var instance = ((CircularScrollbar)bindable);
54             var angle = (float?)newValue;
55
56             instance.Style.TrackSweepAngle = angle;
57             instance.UpdateTrackVisualSweepAngle(angle ?? 0);
58         },
59         defaultValueCreator: (bindable) =>
60         {
61             return ((CircularScrollbar)bindable).Style.TrackSweepAngle ?? 0;
62         });
63
64         /// <summary>Bindable property of TrackColor</summary>
65         [EditorBrowsable(EditorBrowsableState.Never)]
66         public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
67         {
68             var instance = ((CircularScrollbar)bindable);
69             var color = (Color)newValue;
70
71             instance.Style.TrackColor = color;
72             instance.UpdateTrackVisualColor(color);
73         },
74         defaultValueCreator: (bindable) =>
75         {
76             return ((CircularScrollbar)bindable).Style.TrackColor;
77         });
78
79         /// <summary>Bindable property of ThumbColor</summary>
80         [EditorBrowsable(EditorBrowsableState.Never)]
81         public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
82         {
83             var instance = ((CircularScrollbar)bindable);
84             var color = (Color)newValue;
85
86             instance.Style.ThumbColor = color;
87             instance.UpdateThumbVisualColor(color);
88         },
89         defaultValueCreator: (bindable) =>
90         {
91             return ((CircularScrollbar)bindable).Style.ThumbColor;
92         });
93
94         private ArcVisual trackVisual;
95         private ArcVisual thumbVisual;
96         private float contentLength;
97         private float visibleLength;
98         private float currentPosition;
99         private float directionAlpha;
100         private Size containerSize = new Size(0, 0);
101         private Animation thumbStartAngleAnimation;
102         private Animation thumbSweepAngleAnimation;
103
104         #endregion Fields
105
106
107         #region Constructors
108
109         /// <summary>
110         /// Create an empty CircularScrollbar.
111         /// </summary>
112         public CircularScrollbar() : base(new CircularScrollbarStyle())
113         {
114         }
115
116         /// <summary>
117         /// Create a CircularScrollbar and initialize with properties.
118         /// </summary>
119         /// <param name="contentLength">The length of the scrollable content area.</param>
120         /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
121         /// <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>
122         /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
123         [EditorBrowsable(EditorBrowsableState.Never)]
124         public CircularScrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : base(new CircularScrollbarStyle())
125         {
126             Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
127         }
128
129         /// <summary>
130         /// Create an empty CircularScrollbar with a CircularScrollbarStyle instance to set style properties.
131         /// </summary>
132         [EditorBrowsable(EditorBrowsableState.Never)]
133         public CircularScrollbar(CircularScrollbarStyle style) : base(style)
134         {
135         }
136
137         /// <summary>
138         /// Static constructor to initialize bindable properties when loading.
139         /// </summary>
140         static CircularScrollbar()
141         {
142         }
143
144         #endregion Constructors
145
146
147         #region Properties
148
149         /// <summary>
150         /// Return a copied Style instance of CircularScrollbar
151         /// </summary>
152         /// <remarks>
153         /// It returns copied Style instance and changing it does not effect to the CircularScrollbar.
154         /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
155         /// </remarks>
156         [EditorBrowsable(EditorBrowsableState.Never)]
157         public new CircularScrollbarStyle Style => ViewStyle as CircularScrollbarStyle;
158
159         /// <summary>
160         /// The thickness of the scrollbar and track.
161         /// </summary>
162         [EditorBrowsable(EditorBrowsableState.Never)]
163         public float Thickness
164         {
165             get => (float)GetValue(ThicknessProperty);
166             set => SetValue(ThicknessProperty, value);
167         }
168
169         /// <summary>
170         /// The sweep angle of track area in degrees.
171         /// </summary>
172         /// <remarks>
173         /// Values below 6 degrees are treated as 6 degrees.
174         /// Values exceeding 180 degrees are treated as 180 degrees.
175         /// </remarks>
176         [EditorBrowsable(EditorBrowsableState.Never)]
177         public float TrackSweepAngle
178         {
179             get => (float)GetValue(TrackSweepAngleProperty);
180             set => SetValue(TrackSweepAngleProperty, value);
181         }
182
183         /// <summary>
184         /// The color of the track part.
185         /// </summary>
186         [EditorBrowsable(EditorBrowsableState.Never)]
187         public Color TrackColor
188         {
189             get => (Color)GetValue(TrackColorProperty);
190             set => SetValue(TrackColorProperty, value);
191         }
192
193         /// <summary>
194         /// The color of the thumb part.
195         /// </summary>
196         [EditorBrowsable(EditorBrowsableState.Never)]
197         public Color ThumbColor
198         {
199             get => (Color)GetValue(ThumbColorProperty);
200             set => SetValue(ThumbColorProperty, value);
201         }
202
203         #endregion Properties
204
205
206         #region Methods
207
208         /// <inheritdoc/>
209         [EditorBrowsable(EditorBrowsableState.Never)]
210         public override void Initialize(float contentLength, float viewportLenth, float currentPosition, bool isHorizontal = false)
211         {
212             this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
213             this.visibleLength = viewportLenth;
214             this.currentPosition = currentPosition;
215             this.directionAlpha = isHorizontal ? 270.0f : 0.0f;
216
217             thumbStartAngleAnimation?.Stop();
218             thumbStartAngleAnimation = null;
219
220             thumbSweepAngleAnimation?.Stop();
221             thumbSweepAngleAnimation = null;
222
223
224             float trackSweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
225             float trackStartAngle = CalculateTrackStartAngle(trackSweepAngle);
226             float thumbSweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
227             float thumbStartAngle = CalculateThumbStartAngle(currentPosition, trackStartAngle, trackSweepAngle, thumbSweepAngle);
228
229             if (trackVisual == null)
230             {
231                 trackVisual = new ArcVisual
232                 {
233                     SuppressUpdateVisual = true,
234                     Thickness = this.Thickness,
235                     Cap = ArcVisual.CapType.Round,
236                     MixColor = TrackColor,
237                     Size = containerSize - new Size(2, 2),
238                     SizePolicy = VisualTransformPolicyType.Absolute,
239                     SweepAngle = trackSweepAngle,
240                     StartAngle = trackStartAngle,
241                 };
242
243                 AddVisual("Track", trackVisual);
244             }
245             else
246             {
247                 trackVisual.SweepAngle = trackSweepAngle;
248                 trackVisual.StartAngle = trackStartAngle;
249                 trackVisual.UpdateVisual(true);
250             }
251
252             if (thumbVisual == null)
253             {
254                 thumbVisual = new ArcVisual
255                 {
256                     SuppressUpdateVisual = true,
257                     Thickness = trackVisual.Thickness,
258                     Cap = ArcVisual.CapType.Round,
259                     MixColor = ThumbColor,
260                     Size = containerSize - new Size(2, 2),
261                     SizePolicy = VisualTransformPolicyType.Absolute,
262                     SweepAngle = thumbSweepAngle,
263                     StartAngle = thumbStartAngle,
264                     Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f,
265                 };
266
267                 AddVisual("Thumb", thumbVisual);
268             }
269             else
270             {
271                 thumbVisual.SweepAngle = thumbSweepAngle;
272                 thumbVisual.StartAngle = thumbStartAngle;
273                 thumbVisual.UpdateVisual(true);
274             }
275         }
276
277         /// <inheritdoc/>
278         [EditorBrowsable(EditorBrowsableState.Never)]
279         public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
280         {
281             this.currentPosition = position;
282             this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
283
284             if (thumbVisual == null)
285             {
286                 return;
287             }
288
289             thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
290             thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
291             thumbVisual.Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f;
292
293             if (durationMs == 0)
294             {
295                 thumbVisual.UpdateVisual(true);
296
297                 return;
298             }
299
300             // TODO Support non built-in alpha function for visual trainsition in DALi.
301             AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
302
303             thumbStartAngleAnimation?.Stop();
304             thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
305             thumbStartAngleAnimation.Play();
306
307             thumbSweepAngleAnimation?.Stop();
308             thumbSweepAngleAnimation = AnimateVisual(thumbVisual, "sweepAngle", thumbVisual.SweepAngle, 0, (int)durationMs, builtinAlphaFunction);
309             thumbSweepAngleAnimation.Play();
310         }
311
312         /// <inheritdoc/>
313         [EditorBrowsable(EditorBrowsableState.Never)]
314         public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
315         {
316             currentPosition = position;
317
318             if (thumbVisual == null)
319             {
320                 return;
321             }
322
323             var oldThumbStartAngle = thumbVisual.StartAngle;
324
325             thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
326
327             if (durationMs == 0)
328             {
329                 thumbVisual.UpdateVisual(true);
330
331                 return;
332             }
333
334             // TODO Support non built-in alpha function for visual trainsition in DALi.
335             AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
336
337             thumbStartAngleAnimation?.Stop();
338             thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
339             thumbStartAngleAnimation.Play();
340         }
341
342         /// <inheritdoc/>
343         [EditorBrowsable(EditorBrowsableState.Never)]
344         public override void OnRelayout(Vector2 size, RelayoutContainer container)
345         {
346             base.OnRelayout(size, container);
347
348             if (size.Width == containerSize?.Width && size.Height == containerSize.Height)
349             {
350                 return;
351             }
352
353             containerSize = new Size(size.Width, size.Height);
354
355             if (trackVisual == null)
356             {
357                 return;
358             }
359
360             trackVisual.Size = containerSize - new Size(2, 2);
361             thumbVisual.Size = containerSize - new Size(2, 2);
362             
363             trackVisual.UpdateVisual(true);
364             thumbVisual.UpdateVisual(true);
365         }
366
367         /// <inheritdoc/>
368         [EditorBrowsable(EditorBrowsableState.Never)]
369         protected override ViewStyle CreateViewStyle()
370         {
371             return new CircularScrollbarStyle();
372         }
373
374         private float CalculateTrackStartAngle(float currentTrackSweepAngle)
375         {
376             return ((180.0f - currentTrackSweepAngle) / 2.0f) + directionAlpha;
377         }
378
379         private float CalculateTrackSweepAngle(float inputTrackSweepAngle)
380         {
381             return Math.Min(Math.Max(inputTrackSweepAngle, 3), 180);
382         }
383
384         private float CalculateThumbStartAngle(float position, float trackStartAngle, float trackSweepAngle, float thumbSweepAngle)
385         {
386             float minAngle = trackStartAngle;
387             float maxAngle = trackStartAngle + trackSweepAngle - thumbSweepAngle;
388             float resultAngle = trackStartAngle + (trackSweepAngle * (position < 0.0f ? 0.0f : position) / contentLength);
389
390             return Math.Min(Math.Max(resultAngle, minAngle), maxAngle);
391         }
392
393         private float CalculateThumbSweepAngle(float trackSweepAngle)
394         {
395             return trackSweepAngle * visibleLength / contentLength;
396         }
397
398         private bool CalculateThumbVisibility()
399         {
400             return contentLength > visibleLength;
401         }
402
403         private void UpdateVisualThickness(float thickness)
404         {
405             if (trackVisual == null)
406             {
407                 return;
408             }
409
410             trackVisual.Thickness = thickness;
411             thumbVisual.Thickness = thickness;
412
413             trackVisual.UpdateVisual(true);
414             thumbVisual.UpdateVisual(true);
415         }
416
417         private void UpdateTrackVisualSweepAngle(float trackSweepAngle)
418         {
419             if (trackVisual == null || thumbVisual == null)
420             {
421                 return;
422             }
423
424             trackVisual.SweepAngle = CalculateTrackSweepAngle(trackSweepAngle);
425             trackVisual.StartAngle = CalculateTrackStartAngle(trackVisual.SweepAngle);
426
427             thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
428             thumbVisual.StartAngle = CalculateThumbStartAngle(currentPosition, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
429
430             trackVisual.UpdateVisual(true);
431             thumbVisual.UpdateVisual(true);
432         }
433
434         private void UpdateTrackVisualColor(Color trackColor)
435         {
436             if (trackVisual == null)
437             {
438                 return;
439             }
440
441             trackVisual.MixColor = trackColor;
442             trackVisual.UpdateVisual(true);
443         }
444
445         private void UpdateThumbVisualColor(Color thumbColor)
446         {
447             if (thumbVisual == null)
448             {
449                 return;
450             }
451
452             thumbVisual.MixColor = thumbColor;
453             thumbVisual.UpdateVisual(true);
454         }
455
456         #endregion Methods
457     }
458 }