2 * Copyright(c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.ComponentModel;
19 using Tizen.NUI.BaseComponents;
20 using Tizen.NUI.Binding;
21 using Tizen.NUI.Components;
23 namespace Tizen.NUI.Wearable
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 />
29 [EditorBrowsable(EditorBrowsableState.Never)]
30 public class CircularScrollbar : ScrollbarBase
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) =>
38 var instance = ((CircularScrollbar)bindable);
39 var thickness = (float?)newValue;
41 ((CircularScrollbarStyle)instance.viewStyle).Thickness = thickness;
42 instance.UpdateVisualThickness(thickness ?? 0);
44 defaultValueCreator: (bindable) =>
46 return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.Thickness ?? 0;
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) =>
53 var instance = ((CircularScrollbar)bindable);
54 var angle = (float?)newValue;
56 ((CircularScrollbarStyle)instance.viewStyle).TrackSweepAngle = angle;
57 instance.UpdateTrackVisualSweepAngle(angle ?? 0);
59 defaultValueCreator: (bindable) =>
61 return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.TrackSweepAngle ?? 0;
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) =>
68 var instance = ((CircularScrollbar)bindable);
69 var color = (Color)newValue;
71 ((CircularScrollbarStyle)instance.viewStyle).TrackColor = color;
72 instance.UpdateTrackVisualColor(color);
74 defaultValueCreator: (bindable) =>
76 return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.TrackColor;
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) =>
83 var instance = ((CircularScrollbar)bindable);
84 var color = (Color)newValue;
86 ((CircularScrollbarStyle)instance.viewStyle).ThumbColor = color;
87 instance.UpdateThumbVisualColor(color);
89 defaultValueCreator: (bindable) =>
91 return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.ThumbColor;
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 Animation thumbStartAngleAnimation;
101 private Animation thumbSweepAngleAnimation;
109 /// Create an empty CircularScrollbar.
111 public CircularScrollbar() : base(new CircularScrollbarStyle())
116 /// Create a CircularScrollbar and initialize with properties.
118 /// <param name="contentLength">The total length of the content.</param>
119 /// <param name="viewSize">The size of View that contains the content to scroll.</param>
120 /// <param name="currentPosition">Scrolled position.</param>
121 /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical if the value is false.</param>
122 [EditorBrowsable(EditorBrowsableState.Never)]
123 public CircularScrollbar(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false) : base(new CircularScrollbarStyle())
125 Initialize(contentLength, viewSize, currentPosition, isHorizontal);
129 /// Create an empty CircularScrollbar with a CircularScrollbarStyle instance to set style properties.
131 [EditorBrowsable(EditorBrowsableState.Never)]
132 public CircularScrollbar(CircularScrollbarStyle style) : base(style)
137 /// Static constructor to initialize bindable properties when loading.
139 static CircularScrollbar()
143 #endregion Constructors
149 /// The thickness of the scrollbar and track.
151 [EditorBrowsable(EditorBrowsableState.Never)]
152 public float Thickness
154 get => (float)GetValue(ThicknessProperty);
155 set => SetValue(ThicknessProperty, value);
159 /// The sweep angle of track area in degrees.
162 /// Values below 6 degrees are treated as 6 degrees.
163 /// Values exceeding 180 degrees are treated as 180 degrees.
165 [EditorBrowsable(EditorBrowsableState.Never)]
166 public float TrackSweepAngle
168 get => (float)GetValue(TrackSweepAngleProperty);
169 set => SetValue(TrackSweepAngleProperty, value);
173 /// The color of the track part.
175 [EditorBrowsable(EditorBrowsableState.Never)]
176 public Color TrackColor
178 get => (Color)GetValue(TrackColorProperty);
179 set => SetValue(TrackColorProperty, value);
183 /// The color of the thumb part.
185 [EditorBrowsable(EditorBrowsableState.Never)]
186 public Color ThumbColor
188 get => (Color)GetValue(ThumbColorProperty);
189 set => SetValue(ThumbColorProperty, value);
192 #endregion Properties
198 [EditorBrowsable(EditorBrowsableState.Never)]
199 public override void Initialize(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false)
201 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
202 this.visibleLength = isHorizontal ? viewSize.Width : viewSize.Height;
203 this.currentPosition = currentPosition;
204 this.directionAlpha = isHorizontal ? 270.0f : 0.0f;
208 thumbStartAngleAnimation?.Stop();
209 thumbStartAngleAnimation = null;
211 thumbSweepAngleAnimation?.Stop();
212 thumbSweepAngleAnimation = null;
215 CreateThumbVisual(currentPosition);
217 AddVisual("Track", trackVisual);
218 AddVisual("Thumb", thumbVisual);
222 [EditorBrowsable(EditorBrowsableState.Never)]
223 public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
225 this.currentPosition = position;
226 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
228 if (thumbVisual == null)
233 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
234 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
235 thumbVisual.Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f;
239 thumbVisual.UpdateVisual(true);
244 // TODO Support non built-in alpha function for visual trainsition in DALi.
245 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
247 thumbStartAngleAnimation?.Stop();
248 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
249 thumbStartAngleAnimation.Play();
251 thumbSweepAngleAnimation?.Stop();
252 thumbSweepAngleAnimation = AnimateVisual(thumbVisual, "sweepAngle", thumbVisual.SweepAngle, 0, (int)durationMs, builtinAlphaFunction);
253 thumbSweepAngleAnimation.Play();
257 [EditorBrowsable(EditorBrowsableState.Never)]
258 public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
260 currentPosition = position;
262 if (thumbVisual == null)
267 var oldThumbStartAngle = thumbVisual.StartAngle;
269 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
273 thumbVisual.UpdateVisual(true);
278 // TODO Support non built-in alpha function for visual trainsition in DALi.
279 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
281 thumbStartAngleAnimation?.Stop();
282 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
283 thumbStartAngleAnimation.Play();
287 [EditorBrowsable(EditorBrowsableState.Never)]
288 protected override ViewStyle GetViewStyle()
290 return new CircularScrollbarStyle();
293 private void CreateTrackVisual()
295 float sweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
297 trackVisual = new ArcVisual
299 SuppressUpdateVisual = true,
300 Thickness = this.Thickness,
301 Cap = ArcVisual.CapType.Round,
302 MixColor = TrackColor,
303 Size = new Size(visibleLength - 2, visibleLength - 2),
304 SizePolicy = VisualTransformPolicyType.Absolute,
305 SweepAngle = sweepAngle,
306 StartAngle = CalculateTrackStartAngle(sweepAngle),
310 private void CreateThumbVisual(float position)
312 float sweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
314 thumbVisual = new ArcVisual
316 SuppressUpdateVisual = true,
317 Thickness = trackVisual.Thickness,
318 Cap = ArcVisual.CapType.Round,
319 MixColor = ThumbColor,
320 Size = new Size(visibleLength - 2, visibleLength - 2),
321 SizePolicy = VisualTransformPolicyType.Absolute,
322 SweepAngle = sweepAngle,
323 StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, sweepAngle),
324 Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f,
328 private float CalculateTrackStartAngle(float currentTrackSweepAngle)
330 return ((180.0f - currentTrackSweepAngle) / 2.0f) + directionAlpha;
333 private float CalculateTrackSweepAngle(float inputTrackSweepAngle)
335 return Math.Min(Math.Max(inputTrackSweepAngle, 3), 180);
338 private float CalculateThumbStartAngle(float position, float trackStartAngle, float trackSweepAngle, float thumbSweepAngle)
340 float minAngle = trackStartAngle;
341 float maxAngle = trackStartAngle + trackSweepAngle - thumbSweepAngle;
342 float resultAngle = trackStartAngle + (trackSweepAngle * (position < 0.0f ? 0.0f : position) / contentLength);
344 return Math.Min(Math.Max(resultAngle, minAngle), maxAngle);
347 private float CalculateThumbSweepAngle(float trackSweepAngle)
349 return trackSweepAngle * visibleLength / contentLength;
352 private bool CalculateThumbVisibility()
354 return contentLength > visibleLength;
357 private void UpdateVisualThickness(float thickness)
359 if (trackVisual == null)
364 trackVisual.Thickness = thickness;
365 thumbVisual.Thickness = thickness;
367 trackVisual.UpdateVisual(true);
368 thumbVisual.UpdateVisual(true);
371 private void UpdateTrackVisualSweepAngle(float trackSweepAngle)
373 if (trackVisual == null || thumbVisual == null)
378 trackVisual.SweepAngle = CalculateTrackSweepAngle(trackSweepAngle);
379 trackVisual.StartAngle = CalculateTrackStartAngle(trackVisual.SweepAngle);
381 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
382 thumbVisual.StartAngle = CalculateThumbStartAngle(currentPosition, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
384 trackVisual.UpdateVisual(true);
385 thumbVisual.UpdateVisual(true);
388 private void UpdateTrackVisualColor(Color trackColor)
390 if (trackVisual == null)
395 trackVisual.MixColor = trackColor;
396 trackVisual.UpdateVisual(true);
399 private void UpdateThumbVisualColor(Color thumbColor)
401 if (thumbVisual == null)
406 thumbVisual.MixColor = thumbColor;
407 thumbVisual.UpdateVisual(true);