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 instance.Style.Thickness = thickness;
42 instance.UpdateVisualThickness(thickness ?? 0);
44 defaultValueCreator: (bindable) =>
46 return ((CircularScrollbar)bindable).Style.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 instance.Style.TrackSweepAngle = angle;
57 instance.UpdateTrackVisualSweepAngle(angle ?? 0);
59 defaultValueCreator: (bindable) =>
61 return ((CircularScrollbar)bindable).Style.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 instance.Style.TrackColor = color;
72 instance.UpdateTrackVisualColor(color);
74 defaultValueCreator: (bindable) =>
76 return ((CircularScrollbar)bindable).Style.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 instance.Style.ThumbColor = color;
87 instance.UpdateThumbVisualColor(color);
89 defaultValueCreator: (bindable) =>
91 return ((CircularScrollbar)bindable).Style.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 Size containerSize = new Size(0, 0);
101 private Animation thumbStartAngleAnimation;
102 private Animation thumbSweepAngleAnimation;
110 /// Create an empty CircularScrollbar.
112 public CircularScrollbar() : base(new CircularScrollbarStyle())
117 /// Create a CircularScrollbar and initialize with properties.
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())
126 Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
130 /// Create an empty CircularScrollbar with a CircularScrollbarStyle instance to set style properties.
132 [EditorBrowsable(EditorBrowsableState.Never)]
133 public CircularScrollbar(CircularScrollbarStyle style) : base(style)
138 /// Static constructor to initialize bindable properties when loading.
140 static CircularScrollbar()
144 #endregion Constructors
150 /// Return a copied Style instance of CircularScrollbar
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)
156 [EditorBrowsable(EditorBrowsableState.Never)]
157 public new CircularScrollbarStyle Style => ViewStyle as CircularScrollbarStyle;
160 /// The thickness of the scrollbar and track.
162 [EditorBrowsable(EditorBrowsableState.Never)]
163 public float Thickness
165 get => (float)GetValue(ThicknessProperty);
166 set => SetValue(ThicknessProperty, value);
170 /// The sweep angle of track area in degrees.
173 /// Values below 6 degrees are treated as 6 degrees.
174 /// Values exceeding 180 degrees are treated as 180 degrees.
176 [EditorBrowsable(EditorBrowsableState.Never)]
177 public float TrackSweepAngle
179 get => (float)GetValue(TrackSweepAngleProperty);
180 set => SetValue(TrackSweepAngleProperty, value);
184 /// The color of the track part.
186 [EditorBrowsable(EditorBrowsableState.Never)]
187 public Color TrackColor
189 get => (Color)GetValue(TrackColorProperty);
190 set => SetValue(TrackColorProperty, value);
194 /// The color of the thumb part.
196 [EditorBrowsable(EditorBrowsableState.Never)]
197 public Color ThumbColor
199 get => (Color)GetValue(ThumbColorProperty);
200 set => SetValue(ThumbColorProperty, value);
203 #endregion Properties
209 [EditorBrowsable(EditorBrowsableState.Never)]
210 public override void Initialize(float contentLength, float viewportLenth, float currentPosition, bool isHorizontal = false)
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;
217 thumbStartAngleAnimation?.Stop();
218 thumbStartAngleAnimation = null;
220 thumbSweepAngleAnimation?.Stop();
221 thumbSweepAngleAnimation = null;
224 float trackSweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
225 float trackStartAngle = CalculateTrackStartAngle(trackSweepAngle);
226 float thumbSweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
227 float thumbStartAngle = CalculateThumbStartAngle(currentPosition, trackStartAngle, trackSweepAngle, thumbSweepAngle);
229 if (trackVisual == null)
231 trackVisual = new ArcVisual
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,
243 AddVisual("Track", trackVisual);
247 trackVisual.SweepAngle = trackSweepAngle;
248 trackVisual.StartAngle = trackStartAngle;
249 trackVisual.UpdateVisual(true);
252 if (thumbVisual == null)
254 thumbVisual = new ArcVisual
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,
267 AddVisual("Thumb", thumbVisual);
271 thumbVisual.SweepAngle = thumbSweepAngle;
272 thumbVisual.StartAngle = thumbStartAngle;
273 thumbVisual.UpdateVisual(true);
278 [EditorBrowsable(EditorBrowsableState.Never)]
279 public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
281 this.currentPosition = position;
282 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
284 if (thumbVisual == null)
289 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
290 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
291 thumbVisual.Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f;
295 thumbVisual.UpdateVisual(true);
300 // TODO Support non built-in alpha function for visual trainsition in DALi.
301 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
303 thumbStartAngleAnimation?.Stop();
304 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
305 thumbStartAngleAnimation.Play();
307 thumbSweepAngleAnimation?.Stop();
308 thumbSweepAngleAnimation = AnimateVisual(thumbVisual, "sweepAngle", thumbVisual.SweepAngle, 0, (int)durationMs, builtinAlphaFunction);
309 thumbSweepAngleAnimation.Play();
313 [EditorBrowsable(EditorBrowsableState.Never)]
314 public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
316 currentPosition = position;
318 if (thumbVisual == null)
323 var oldThumbStartAngle = thumbVisual.StartAngle;
325 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
329 thumbVisual.UpdateVisual(true);
334 // TODO Support non built-in alpha function for visual trainsition in DALi.
335 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
337 thumbStartAngleAnimation?.Stop();
338 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
339 thumbStartAngleAnimation.Play();
343 [EditorBrowsable(EditorBrowsableState.Never)]
344 public override void OnRelayout(Vector2 size, RelayoutContainer container)
346 base.OnRelayout(size, container);
348 if (size.Width == containerSize?.Width && size.Height == containerSize.Height)
353 containerSize = new Size(size.Width, size.Height);
355 if (trackVisual == null)
360 trackVisual.Size = containerSize - new Size(2, 2);
361 thumbVisual.Size = containerSize - new Size(2, 2);
363 trackVisual.UpdateVisual(true);
364 thumbVisual.UpdateVisual(true);
368 [EditorBrowsable(EditorBrowsableState.Never)]
369 protected override ViewStyle CreateViewStyle()
371 return new CircularScrollbarStyle();
374 private float CalculateTrackStartAngle(float currentTrackSweepAngle)
376 return ((180.0f - currentTrackSweepAngle) / 2.0f) + directionAlpha;
379 private float CalculateTrackSweepAngle(float inputTrackSweepAngle)
381 return Math.Min(Math.Max(inputTrackSweepAngle, 3), 180);
384 private float CalculateThumbStartAngle(float position, float trackStartAngle, float trackSweepAngle, float thumbSweepAngle)
386 float minAngle = trackStartAngle;
387 float maxAngle = trackStartAngle + trackSweepAngle - thumbSweepAngle;
388 float resultAngle = trackStartAngle + (trackSweepAngle * (position < 0.0f ? 0.0f : position) / contentLength);
390 return Math.Min(Math.Max(resultAngle, minAngle), maxAngle);
393 private float CalculateThumbSweepAngle(float trackSweepAngle)
395 return trackSweepAngle * visibleLength / contentLength;
398 private bool CalculateThumbVisibility()
400 return contentLength > visibleLength;
403 private void UpdateVisualThickness(float thickness)
405 if (trackVisual == null)
410 trackVisual.Thickness = thickness;
411 thumbVisual.Thickness = thickness;
413 trackVisual.UpdateVisual(true);
414 thumbVisual.UpdateVisual(true);
417 private void UpdateTrackVisualSweepAngle(float trackSweepAngle)
419 if (trackVisual == null || thumbVisual == null)
424 trackVisual.SweepAngle = CalculateTrackSweepAngle(trackSweepAngle);
425 trackVisual.StartAngle = CalculateTrackStartAngle(trackVisual.SweepAngle);
427 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
428 thumbVisual.StartAngle = CalculateThumbStartAngle(currentPosition, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
430 trackVisual.UpdateVisual(true);
431 thumbVisual.UpdateVisual(true);
434 private void UpdateTrackVisualColor(Color trackColor)
436 if (trackVisual == null)
441 trackVisual.MixColor = trackColor;
442 trackVisual.UpdateVisual(true);
445 private void UpdateThumbVisualColor(Color thumbColor)
447 if (thumbVisual == null)
452 thumbVisual.MixColor = thumbColor;
453 thumbVisual.UpdateVisual(true);