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 instance.UpdateVisualThickness((float?)newValue ?? 0);
41 defaultValueCreator: (bindable) =>
43 var instance = (CircularScrollbar)bindable;
44 return instance.trackVisual == null ? 0 : instance.trackVisual.Thickness;
47 /// <summary>Bindable property of TrackSweepAngle</summary>
48 [EditorBrowsable(EditorBrowsableState.Never)]
49 public static readonly BindableProperty TrackSweepAngleProperty = BindableProperty.Create(nameof(TrackSweepAngle), typeof(float), typeof(CircularScrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
51 var instance = ((CircularScrollbar)bindable);
52 instance.UpdateTrackVisualSweepAngle((float?)newValue ?? 0);
54 defaultValueCreator: (bindable) =>
56 var instance = (CircularScrollbar)bindable;
57 return instance.trackVisual == null ? 0 : instance.trackVisual.SweepAngle;
60 /// <summary>Bindable property of TrackColor</summary>
61 [EditorBrowsable(EditorBrowsableState.Never)]
62 public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
64 var instance = ((CircularScrollbar)bindable);
65 instance.UpdateTrackVisualColor((Color)newValue);
67 defaultValueCreator: (bindable) =>
69 return ((CircularScrollbar)bindable).trackVisual?.MixColor;
72 /// <summary>Bindable property of ThumbColor</summary>
73 [EditorBrowsable(EditorBrowsableState.Never)]
74 public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
76 var instance = ((CircularScrollbar)bindable);
77 instance.UpdateThumbVisualColor((Color)newValue);
79 defaultValueCreator: (bindable) =>
81 return ((CircularScrollbar)bindable).thumbVisual?.MixColor;
84 private ArcVisual trackVisual;
85 private ArcVisual thumbVisual;
86 private float contentLength;
87 private float visibleLength;
88 private float currentPosition;
89 private float directionAlpha;
90 private Size containerSize = new Size(0, 0);
91 private Animation thumbStartAngleAnimation;
92 private Animation thumbSweepAngleAnimation;
100 /// Create an empty CircularScrollbar.
102 public CircularScrollbar() : base(new CircularScrollbarStyle())
107 /// Create a CircularScrollbar and initialize with properties.
109 /// <param name="contentLength">The length of the scrollable content area.</param>
110 /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
111 /// <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>
112 /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
113 [EditorBrowsable(EditorBrowsableState.Never)]
114 public CircularScrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : base(new CircularScrollbarStyle())
116 Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
120 /// Create an empty CircularScrollbar with a CircularScrollbarStyle instance to set style properties.
122 [EditorBrowsable(EditorBrowsableState.Never)]
123 public CircularScrollbar(CircularScrollbarStyle style) : base(style)
128 /// Static constructor to initialize bindable properties when loading.
130 static CircularScrollbar()
134 #endregion Constructors
140 /// Return a copied Style instance of CircularScrollbar
143 /// It returns copied Style instance and changing it does not effect to the CircularScrollbar.
144 /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
146 [EditorBrowsable(EditorBrowsableState.Never)]
147 public new CircularScrollbarStyle Style
151 var result = new CircularScrollbarStyle(ViewStyle as CircularScrollbarStyle);
152 result.CopyPropertiesFromView(this);
158 /// The thickness of the scrollbar and track.
160 [EditorBrowsable(EditorBrowsableState.Never)]
161 public float Thickness
163 get => (float)GetValue(ThicknessProperty);
164 set => SetValue(ThicknessProperty, value);
168 /// The sweep angle of track area in degrees.
171 /// Values below 6 degrees are treated as 6 degrees.
172 /// Values exceeding 180 degrees are treated as 180 degrees.
174 [EditorBrowsable(EditorBrowsableState.Never)]
175 public float TrackSweepAngle
177 get => (float)GetValue(TrackSweepAngleProperty);
178 set => SetValue(TrackSweepAngleProperty, value);
182 /// The color of the track part.
184 [EditorBrowsable(EditorBrowsableState.Never)]
185 public Color TrackColor
187 get => (Color)GetValue(TrackColorProperty);
188 set => SetValue(TrackColorProperty, value);
192 /// The color of the thumb part.
194 [EditorBrowsable(EditorBrowsableState.Never)]
195 public Color ThumbColor
197 get => (Color)GetValue(ThumbColorProperty);
198 set => SetValue(ThumbColorProperty, value);
201 #endregion Properties
207 [EditorBrowsable(EditorBrowsableState.Never)]
208 public override void Initialize(float contentLength, float viewportLenth, float currentPosition, bool isHorizontal = false)
210 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
211 this.visibleLength = viewportLenth;
212 this.currentPosition = currentPosition;
213 this.directionAlpha = isHorizontal ? 270.0f : 0.0f;
215 thumbStartAngleAnimation?.Stop();
216 thumbStartAngleAnimation = null;
218 thumbSweepAngleAnimation?.Stop();
219 thumbSweepAngleAnimation = null;
222 float trackSweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
223 float trackStartAngle = CalculateTrackStartAngle(trackSweepAngle);
224 float thumbSweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
225 float thumbStartAngle = CalculateThumbStartAngle(currentPosition, trackStartAngle, trackSweepAngle, thumbSweepAngle);
227 if (trackVisual == null)
229 trackVisual = new ArcVisual
231 SuppressUpdateVisual = true,
232 Thickness = this.Thickness,
233 Cap = ArcVisual.CapType.Round,
234 MixColor = TrackColor,
235 Size = containerSize - new Size(2, 2),
236 SizePolicy = VisualTransformPolicyType.Absolute,
237 SweepAngle = trackSweepAngle,
238 StartAngle = trackStartAngle,
241 AddVisual("Track", trackVisual);
245 trackVisual.SweepAngle = trackSweepAngle;
246 trackVisual.StartAngle = trackStartAngle;
247 trackVisual.UpdateVisual(true);
250 if (thumbVisual == null)
252 thumbVisual = new ArcVisual
254 SuppressUpdateVisual = true,
255 Thickness = trackVisual.Thickness,
256 Cap = ArcVisual.CapType.Round,
257 MixColor = ThumbColor,
258 Size = containerSize - new Size(2, 2),
259 SizePolicy = VisualTransformPolicyType.Absolute,
260 SweepAngle = thumbSweepAngle,
261 StartAngle = thumbStartAngle,
262 Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f,
265 AddVisual("Thumb", thumbVisual);
269 thumbVisual.SweepAngle = thumbSweepAngle;
270 thumbVisual.StartAngle = thumbStartAngle;
271 thumbVisual.UpdateVisual(true);
276 [EditorBrowsable(EditorBrowsableState.Never)]
277 public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
279 this.currentPosition = position;
280 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
282 if (thumbVisual == null)
287 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
288 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
289 thumbVisual.Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f;
293 thumbVisual.UpdateVisual(true);
298 // TODO Support non built-in alpha function for visual trainsition in DALi.
299 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
301 thumbStartAngleAnimation?.Stop();
302 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
303 thumbStartAngleAnimation.Play();
305 thumbSweepAngleAnimation?.Stop();
306 thumbSweepAngleAnimation = AnimateVisual(thumbVisual, "sweepAngle", thumbVisual.SweepAngle, 0, (int)durationMs, builtinAlphaFunction);
307 thumbSweepAngleAnimation.Play();
311 [EditorBrowsable(EditorBrowsableState.Never)]
312 public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
314 currentPosition = position;
316 if (thumbVisual == null)
321 var oldThumbStartAngle = thumbVisual.StartAngle;
323 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
327 thumbVisual.UpdateVisual(true);
332 // TODO Support non built-in alpha function for visual trainsition in DALi.
333 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
335 thumbStartAngleAnimation?.Stop();
336 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
337 thumbStartAngleAnimation.Play();
341 [EditorBrowsable(EditorBrowsableState.Never)]
342 public override void OnRelayout(Vector2 size, RelayoutContainer container)
344 base.OnRelayout(size, container);
346 if (size.Width == containerSize?.Width && size.Height == containerSize.Height)
351 containerSize = new Size(size.Width, size.Height);
353 if (trackVisual == null)
358 trackVisual.Size = containerSize - new Size(2, 2);
359 thumbVisual.Size = containerSize - new Size(2, 2);
361 trackVisual.UpdateVisual(true);
362 thumbVisual.UpdateVisual(true);
366 [EditorBrowsable(EditorBrowsableState.Never)]
367 protected override ViewStyle CreateViewStyle()
369 return new CircularScrollbarStyle();
372 private float CalculateTrackStartAngle(float currentTrackSweepAngle)
374 return ((180.0f - currentTrackSweepAngle) / 2.0f) + directionAlpha;
377 private float CalculateTrackSweepAngle(float inputTrackSweepAngle)
379 return Math.Min(Math.Max(inputTrackSweepAngle, 3), 180);
382 private float CalculateThumbStartAngle(float position, float trackStartAngle, float trackSweepAngle, float thumbSweepAngle)
384 float minAngle = trackStartAngle;
385 float maxAngle = trackStartAngle + trackSweepAngle - thumbSweepAngle;
386 float resultAngle = trackStartAngle + (trackSweepAngle * (position < 0.0f ? 0.0f : position) / contentLength);
388 return Math.Min(Math.Max(resultAngle, minAngle), maxAngle);
391 private float CalculateThumbSweepAngle(float trackSweepAngle)
393 return trackSweepAngle * visibleLength / contentLength;
396 private bool CalculateThumbVisibility()
398 return contentLength > visibleLength;
401 private void UpdateVisualThickness(float thickness)
403 if (trackVisual == null)
408 trackVisual.Thickness = thickness;
409 thumbVisual.Thickness = thickness;
411 trackVisual.UpdateVisual(true);
412 thumbVisual.UpdateVisual(true);
415 private void UpdateTrackVisualSweepAngle(float trackSweepAngle)
417 if (trackVisual == null || thumbVisual == null)
422 trackVisual.SweepAngle = CalculateTrackSweepAngle(trackSweepAngle);
423 trackVisual.StartAngle = CalculateTrackStartAngle(trackVisual.SweepAngle);
425 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
426 thumbVisual.StartAngle = CalculateThumbStartAngle(currentPosition, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
428 trackVisual.UpdateVisual(true);
429 thumbVisual.UpdateVisual(true);
432 private void UpdateTrackVisualColor(Color trackColor)
434 if (trackVisual == null)
439 trackVisual.MixColor = trackColor;
440 trackVisual.UpdateVisual(true);
443 private void UpdateThumbVisualColor(Color thumbColor)
445 if (thumbVisual == null)
450 thumbVisual.MixColor = thumbColor;
451 thumbVisual.UpdateVisual(true);