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 float value = (float?)newValue ?? 0;
40 instance.CurrentStyle.Thickness = value;
41 instance.UpdateVisualThickness(value);
43 defaultValueCreator: (bindable) =>
45 var instance = (CircularScrollbar)bindable;
46 return instance.CurrentStyle.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 float value = (float?)newValue ?? 0;
55 instance.CurrentStyle.TrackSweepAngle = value;
56 instance.UpdateTrackVisualSweepAngle(value);
58 defaultValueCreator: (bindable) =>
60 var instance = (CircularScrollbar)bindable;
61 return instance.CurrentStyle.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 instance.CurrentStyle.TrackColor = (Color)newValue;
70 instance.UpdateTrackVisualColor((Color)newValue);
72 defaultValueCreator: (bindable) =>
74 return ((CircularScrollbar)bindable).CurrentStyle.TrackColor;
77 /// <summary>Bindable property of ThumbColor</summary>
78 [EditorBrowsable(EditorBrowsableState.Never)]
79 public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
81 var instance = ((CircularScrollbar)bindable);
82 instance.CurrentStyle.ThumbColor = (Color)newValue;
83 instance.UpdateThumbVisualColor((Color)newValue);
85 defaultValueCreator: (bindable) =>
87 return ((CircularScrollbar)bindable).CurrentStyle.ThumbColor;
90 private ArcVisual trackVisual;
91 private ArcVisual thumbVisual;
92 private float contentLength;
93 private float visibleLength;
94 private float currentPosition;
95 private float directionAlpha;
96 private Size containerSize = new Size(0, 0);
97 private Animation thumbStartAngleAnimation;
98 private Animation thumbSweepAngleAnimation;
99 private bool mScrollEnabled = true;
107 /// Create an empty CircularScrollbar.
109 public CircularScrollbar() : base(new CircularScrollbarStyle())
114 /// Create a CircularScrollbar and initialize with properties.
116 /// <param name="contentLength">The length of the scrollable content area.</param>
117 /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
118 /// <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>
119 /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
120 [EditorBrowsable(EditorBrowsableState.Never)]
121 public CircularScrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : base(new CircularScrollbarStyle())
123 Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
127 /// Create an empty CircularScrollbar with a CircularScrollbarStyle instance to set style properties.
129 [EditorBrowsable(EditorBrowsableState.Never)]
130 public CircularScrollbar(CircularScrollbarStyle style) : base(style)
135 /// Static constructor to initialize bindable properties when loading.
137 static CircularScrollbar()
141 #endregion Constructors
147 /// Return a copied Style instance of CircularScrollbar
150 /// It returns copied Style instance and changing it does not effect to the CircularScrollbar.
151 /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
153 [EditorBrowsable(EditorBrowsableState.Never)]
154 public CircularScrollbarStyle Style
158 var result = new CircularScrollbarStyle(ViewStyle as CircularScrollbarStyle);
159 result.CopyPropertiesFromView(this);
165 /// The thickness of the scrollbar and track.
167 [EditorBrowsable(EditorBrowsableState.Never)]
168 public float Thickness
170 get => (float)GetValue(ThicknessProperty);
171 set => SetValue(ThicknessProperty, value);
175 /// The sweep angle of track area in degrees.
178 /// Values below 6 degrees are treated as 6 degrees.
179 /// Values exceeding 180 degrees are treated as 180 degrees.
181 [EditorBrowsable(EditorBrowsableState.Never)]
182 public float TrackSweepAngle
184 get => (float)GetValue(TrackSweepAngleProperty);
185 set => SetValue(TrackSweepAngleProperty, value);
189 /// The color of the track part.
191 [EditorBrowsable(EditorBrowsableState.Never)]
192 public Color TrackColor
194 get => (Color)GetValue(TrackColorProperty);
195 set => SetValue(TrackColorProperty, value);
199 /// The color of the thumb part.
201 [EditorBrowsable(EditorBrowsableState.Never)]
202 public Color ThumbColor
204 get => (Color)GetValue(ThumbColorProperty);
205 set => SetValue(ThumbColorProperty, value);
208 private CircularScrollbarStyle CurrentStyle => ViewStyle as CircularScrollbarStyle;
210 #endregion Properties
216 [EditorBrowsable(EditorBrowsableState.Never)]
217 public override void Initialize(float contentLength, float viewportLenth, float currentPosition, bool isHorizontal = false)
219 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
220 this.visibleLength = viewportLenth;
221 this.currentPosition = currentPosition;
222 this.directionAlpha = isHorizontal ? 270.0f : 0.0f;
224 thumbStartAngleAnimation?.Stop();
225 thumbStartAngleAnimation = null;
227 thumbSweepAngleAnimation?.Stop();
228 thumbSweepAngleAnimation = null;
231 float trackSweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
232 float trackStartAngle = CalculateTrackStartAngle(trackSweepAngle);
233 float thumbSweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
234 float thumbStartAngle = CalculateThumbStartAngle(currentPosition, trackStartAngle, trackSweepAngle, thumbSweepAngle);
236 if (trackVisual == null)
238 trackVisual = new ArcVisual
240 SuppressUpdateVisual = true,
241 Thickness = this.Thickness,
242 Cap = ArcVisual.CapType.Round,
243 MixColor = TrackColor,
244 Size = containerSize - new Size(2, 2),
245 SizePolicy = VisualTransformPolicyType.Absolute,
246 SweepAngle = trackSweepAngle,
247 StartAngle = trackStartAngle,
250 AddVisual("Track", trackVisual);
254 trackVisual.SweepAngle = trackSweepAngle;
255 trackVisual.StartAngle = trackStartAngle;
256 trackVisual.UpdateVisual(true);
259 if (thumbVisual == null)
261 thumbVisual = new ArcVisual
263 SuppressUpdateVisual = true,
264 Thickness = trackVisual.Thickness,
265 Cap = ArcVisual.CapType.Round,
266 MixColor = ThumbColor,
267 Size = containerSize - new Size(2, 2),
268 SizePolicy = VisualTransformPolicyType.Absolute,
269 SweepAngle = thumbSweepAngle,
270 StartAngle = thumbStartAngle,
271 Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f,
274 AddVisual("Thumb", thumbVisual);
278 thumbVisual.SweepAngle = thumbSweepAngle;
279 thumbVisual.StartAngle = thumbStartAngle;
280 thumbVisual.UpdateVisual(true);
285 [EditorBrowsable(EditorBrowsableState.Never)]
286 public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
288 this.currentPosition = position;
289 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
291 if (thumbVisual == null)
296 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
297 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
298 thumbVisual.Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f;
302 thumbVisual.UpdateVisual(true);
307 // TODO Support non built-in alpha function for visual trainsition in DALi.
308 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
310 thumbStartAngleAnimation?.Stop();
311 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
312 thumbStartAngleAnimation.Play();
314 thumbSweepAngleAnimation?.Stop();
315 thumbSweepAngleAnimation = AnimateVisual(thumbVisual, "sweepAngle", thumbVisual.SweepAngle, 0, (int)durationMs, builtinAlphaFunction);
316 thumbSweepAngleAnimation.Play();
320 [EditorBrowsable(EditorBrowsableState.Never)]
321 public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
323 currentPosition = position;
325 if (mScrollEnabled == false)
330 if (thumbVisual == null)
335 var oldThumbStartAngle = thumbVisual.StartAngle;
337 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
341 thumbVisual.UpdateVisual(true);
346 // TODO Support non built-in alpha function for visual trainsition in DALi.
347 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
349 thumbStartAngleAnimation?.Stop();
350 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
351 thumbStartAngleAnimation.Play();
355 [EditorBrowsable(EditorBrowsableState.Never)]
356 public override void OnRelayout(Vector2 size, RelayoutContainer container)
358 base.OnRelayout(size, container);
360 if (size.Width == containerSize?.Width && size.Height == containerSize.Height)
365 containerSize = new Size(size.Width, size.Height);
367 if (trackVisual == null)
372 trackVisual.Size = containerSize - new Size(2, 2);
373 thumbVisual.Size = containerSize - new Size(2, 2);
375 trackVisual.UpdateVisual(true);
376 thumbVisual.UpdateVisual(true);
380 [EditorBrowsable(EditorBrowsableState.Never)]
381 protected override ViewStyle CreateViewStyle()
383 return new CircularScrollbarStyle();
386 private float CalculateTrackStartAngle(float currentTrackSweepAngle)
388 return ((180.0f - currentTrackSweepAngle) / 2.0f) + directionAlpha;
391 private float CalculateTrackSweepAngle(float inputTrackSweepAngle)
393 return Math.Min(Math.Max(inputTrackSweepAngle, 3), 180);
396 private float CalculateThumbStartAngle(float position, float trackStartAngle, float trackSweepAngle, float thumbSweepAngle)
398 float minAngle = trackStartAngle;
399 float maxAngle = trackStartAngle + trackSweepAngle - thumbSweepAngle;
400 float resultAngle = trackStartAngle + (trackSweepAngle * (position < 0.0f ? 0.0f : position) / contentLength);
402 return Math.Min(Math.Max(resultAngle, minAngle), maxAngle);
405 private float CalculateThumbSweepAngle(float trackSweepAngle)
407 return trackSweepAngle * visibleLength / contentLength;
410 private bool CalculateThumbVisibility()
412 return contentLength > visibleLength;
415 private void UpdateVisualThickness(float thickness)
417 if (trackVisual == null)
422 trackVisual.Thickness = thickness;
423 thumbVisual.Thickness = thickness;
425 trackVisual.UpdateVisual(true);
426 thumbVisual.UpdateVisual(true);
429 private void UpdateTrackVisualSweepAngle(float trackSweepAngle)
431 if (trackVisual == null || thumbVisual == null)
436 trackVisual.SweepAngle = CalculateTrackSweepAngle(trackSweepAngle);
437 trackVisual.StartAngle = CalculateTrackStartAngle(trackVisual.SweepAngle);
439 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
440 thumbVisual.StartAngle = CalculateThumbStartAngle(currentPosition, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
442 trackVisual.UpdateVisual(true);
443 thumbVisual.UpdateVisual(true);
446 private void UpdateTrackVisualColor(Color trackColor)
448 if (trackVisual == null)
453 trackVisual.MixColor = trackColor;
454 trackVisual.UpdateVisual(true);
457 private void UpdateThumbVisualColor(Color thumbColor)
459 if (thumbVisual == null)
464 thumbVisual.MixColor = thumbColor;
465 thumbVisual.UpdateVisual(true);
469 [EditorBrowsable(EditorBrowsableState.Never)]
470 public override bool ScrollEnabled
474 return mScrollEnabled;
478 if (value != mScrollEnabled)
480 mScrollEnabled = value;