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 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 /// The thickness of the scrollbar and track.
152 [EditorBrowsable(EditorBrowsableState.Never)]
153 public float Thickness
155 get => (float)GetValue(ThicknessProperty);
156 set => SetValue(ThicknessProperty, value);
160 /// The sweep angle of track area in degrees.
163 /// Values below 6 degrees are treated as 6 degrees.
164 /// Values exceeding 180 degrees are treated as 180 degrees.
166 [EditorBrowsable(EditorBrowsableState.Never)]
167 public float TrackSweepAngle
169 get => (float)GetValue(TrackSweepAngleProperty);
170 set => SetValue(TrackSweepAngleProperty, value);
174 /// The color of the track part.
176 [EditorBrowsable(EditorBrowsableState.Never)]
177 public Color TrackColor
179 get => (Color)GetValue(TrackColorProperty);
180 set => SetValue(TrackColorProperty, value);
184 /// The color of the thumb part.
186 [EditorBrowsable(EditorBrowsableState.Never)]
187 public Color ThumbColor
189 get => (Color)GetValue(ThumbColorProperty);
190 set => SetValue(ThumbColorProperty, value);
193 #endregion Properties
199 [EditorBrowsable(EditorBrowsableState.Never)]
200 public override void Initialize(float contentLength, float viewportLenth, float currentPosition, bool isHorizontal = false)
202 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
203 this.visibleLength = viewportLenth;
204 this.currentPosition = currentPosition;
205 this.directionAlpha = isHorizontal ? 270.0f : 0.0f;
207 thumbStartAngleAnimation?.Stop();
208 thumbStartAngleAnimation = null;
210 thumbSweepAngleAnimation?.Stop();
211 thumbSweepAngleAnimation = null;
214 float trackSweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
215 float trackStartAngle = CalculateTrackStartAngle(trackSweepAngle);
216 float thumbSweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
217 float thumbStartAngle = CalculateThumbStartAngle(currentPosition, trackStartAngle, trackSweepAngle, thumbSweepAngle);
219 if (trackVisual == null)
221 trackVisual = new ArcVisual
223 SuppressUpdateVisual = true,
224 Thickness = this.Thickness,
225 Cap = ArcVisual.CapType.Round,
226 MixColor = TrackColor,
227 Size = containerSize - new Size(2, 2),
228 SizePolicy = VisualTransformPolicyType.Absolute,
229 SweepAngle = trackSweepAngle,
230 StartAngle = trackStartAngle,
233 AddVisual("Track", trackVisual);
237 trackVisual.SweepAngle = trackSweepAngle;
238 trackVisual.StartAngle = trackStartAngle;
239 trackVisual.UpdateVisual(true);
242 if (thumbVisual == null)
244 thumbVisual = new ArcVisual
246 SuppressUpdateVisual = true,
247 Thickness = trackVisual.Thickness,
248 Cap = ArcVisual.CapType.Round,
249 MixColor = ThumbColor,
250 Size = containerSize - new Size(2, 2),
251 SizePolicy = VisualTransformPolicyType.Absolute,
252 SweepAngle = thumbSweepAngle,
253 StartAngle = thumbStartAngle,
254 Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f,
257 AddVisual("Thumb", thumbVisual);
261 thumbVisual.SweepAngle = thumbSweepAngle;
262 thumbVisual.StartAngle = thumbStartAngle;
263 thumbVisual.UpdateVisual(true);
268 [EditorBrowsable(EditorBrowsableState.Never)]
269 public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
271 this.currentPosition = position;
272 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
274 if (thumbVisual == null)
279 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
280 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
281 thumbVisual.Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f;
285 thumbVisual.UpdateVisual(true);
290 // TODO Support non built-in alpha function for visual trainsition in DALi.
291 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
293 thumbStartAngleAnimation?.Stop();
294 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
295 thumbStartAngleAnimation.Play();
297 thumbSweepAngleAnimation?.Stop();
298 thumbSweepAngleAnimation = AnimateVisual(thumbVisual, "sweepAngle", thumbVisual.SweepAngle, 0, (int)durationMs, builtinAlphaFunction);
299 thumbSweepAngleAnimation.Play();
303 [EditorBrowsable(EditorBrowsableState.Never)]
304 public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
306 currentPosition = position;
308 if (thumbVisual == null)
313 var oldThumbStartAngle = thumbVisual.StartAngle;
315 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
319 thumbVisual.UpdateVisual(true);
324 // TODO Support non built-in alpha function for visual trainsition in DALi.
325 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
327 thumbStartAngleAnimation?.Stop();
328 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
329 thumbStartAngleAnimation.Play();
333 [EditorBrowsable(EditorBrowsableState.Never)]
334 public override void OnRelayout(Vector2 size, RelayoutContainer container)
336 base.OnRelayout(size, container);
338 if (size.Width == containerSize?.Width && size.Height == containerSize.Height)
343 containerSize = new Size(size.Width, size.Height);
345 if (trackVisual == null)
350 trackVisual.Size = containerSize - new Size(2, 2);
351 thumbVisual.Size = containerSize - new Size(2, 2);
353 trackVisual.UpdateVisual(true);
354 thumbVisual.UpdateVisual(true);
358 [EditorBrowsable(EditorBrowsableState.Never)]
359 protected override ViewStyle GetViewStyle()
361 return new CircularScrollbarStyle();
364 private float CalculateTrackStartAngle(float currentTrackSweepAngle)
366 return ((180.0f - currentTrackSweepAngle) / 2.0f) + directionAlpha;
369 private float CalculateTrackSweepAngle(float inputTrackSweepAngle)
371 return Math.Min(Math.Max(inputTrackSweepAngle, 3), 180);
374 private float CalculateThumbStartAngle(float position, float trackStartAngle, float trackSweepAngle, float thumbSweepAngle)
376 float minAngle = trackStartAngle;
377 float maxAngle = trackStartAngle + trackSweepAngle - thumbSweepAngle;
378 float resultAngle = trackStartAngle + (trackSweepAngle * (position < 0.0f ? 0.0f : position) / contentLength);
380 return Math.Min(Math.Max(resultAngle, minAngle), maxAngle);
383 private float CalculateThumbSweepAngle(float trackSweepAngle)
385 return trackSweepAngle * visibleLength / contentLength;
388 private bool CalculateThumbVisibility()
390 return contentLength > visibleLength;
393 private void UpdateVisualThickness(float thickness)
395 if (trackVisual == null)
400 trackVisual.Thickness = thickness;
401 thumbVisual.Thickness = thickness;
403 trackVisual.UpdateVisual(true);
404 thumbVisual.UpdateVisual(true);
407 private void UpdateTrackVisualSweepAngle(float trackSweepAngle)
409 if (trackVisual == null || thumbVisual == null)
414 trackVisual.SweepAngle = CalculateTrackSweepAngle(trackSweepAngle);
415 trackVisual.StartAngle = CalculateTrackStartAngle(trackVisual.SweepAngle);
417 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
418 thumbVisual.StartAngle = CalculateThumbStartAngle(currentPosition, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
420 trackVisual.UpdateVisual(true);
421 thumbVisual.UpdateVisual(true);
424 private void UpdateTrackVisualColor(Color trackColor)
426 if (trackVisual == null)
431 trackVisual.MixColor = trackColor;
432 trackVisual.UpdateVisual(true);
435 private void UpdateThumbVisualColor(Color thumbColor)
437 if (thumbVisual == null)
442 thumbVisual.MixColor = thumbColor;
443 thumbVisual.UpdateVisual(true);