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;
93 private bool mScrollEnabled = true;
101 /// Create an empty CircularScrollbar.
103 public CircularScrollbar() : base(new CircularScrollbarStyle())
108 /// Create a CircularScrollbar and initialize with properties.
110 /// <param name="contentLength">The length of the scrollable content area.</param>
111 /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
112 /// <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>
113 /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
114 [EditorBrowsable(EditorBrowsableState.Never)]
115 public CircularScrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : base(new CircularScrollbarStyle())
117 Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
121 /// Create an empty CircularScrollbar with a CircularScrollbarStyle instance to set style properties.
123 [EditorBrowsable(EditorBrowsableState.Never)]
124 public CircularScrollbar(CircularScrollbarStyle style) : base(style)
129 /// Static constructor to initialize bindable properties when loading.
131 static CircularScrollbar()
135 #endregion Constructors
141 /// Return a copied Style instance of CircularScrollbar
144 /// It returns copied Style instance and changing it does not effect to the CircularScrollbar.
145 /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
147 [EditorBrowsable(EditorBrowsableState.Never)]
148 public new CircularScrollbarStyle Style
152 var result = new CircularScrollbarStyle(ViewStyle as CircularScrollbarStyle);
153 result.CopyPropertiesFromView(this);
159 /// The thickness of the scrollbar and track.
161 [EditorBrowsable(EditorBrowsableState.Never)]
162 public float Thickness
164 get => (float)GetValue(ThicknessProperty);
165 set => SetValue(ThicknessProperty, value);
169 /// The sweep angle of track area in degrees.
172 /// Values below 6 degrees are treated as 6 degrees.
173 /// Values exceeding 180 degrees are treated as 180 degrees.
175 [EditorBrowsable(EditorBrowsableState.Never)]
176 public float TrackSweepAngle
178 get => (float)GetValue(TrackSweepAngleProperty);
179 set => SetValue(TrackSweepAngleProperty, value);
183 /// The color of the track part.
185 [EditorBrowsable(EditorBrowsableState.Never)]
186 public Color TrackColor
188 get => (Color)GetValue(TrackColorProperty);
189 set => SetValue(TrackColorProperty, value);
193 /// The color of the thumb part.
195 [EditorBrowsable(EditorBrowsableState.Never)]
196 public Color ThumbColor
198 get => (Color)GetValue(ThumbColorProperty);
199 set => SetValue(ThumbColorProperty, value);
202 #endregion Properties
208 [EditorBrowsable(EditorBrowsableState.Never)]
209 public override void Initialize(float contentLength, float viewportLenth, float currentPosition, bool isHorizontal = false)
211 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
212 this.visibleLength = viewportLenth;
213 this.currentPosition = currentPosition;
214 this.directionAlpha = isHorizontal ? 270.0f : 0.0f;
216 thumbStartAngleAnimation?.Stop();
217 thumbStartAngleAnimation = null;
219 thumbSweepAngleAnimation?.Stop();
220 thumbSweepAngleAnimation = null;
223 float trackSweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
224 float trackStartAngle = CalculateTrackStartAngle(trackSweepAngle);
225 float thumbSweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
226 float thumbStartAngle = CalculateThumbStartAngle(currentPosition, trackStartAngle, trackSweepAngle, thumbSweepAngle);
228 if (trackVisual == null)
230 trackVisual = new ArcVisual
232 SuppressUpdateVisual = true,
233 Thickness = this.Thickness,
234 Cap = ArcVisual.CapType.Round,
235 MixColor = TrackColor,
236 Size = containerSize - new Size(2, 2),
237 SizePolicy = VisualTransformPolicyType.Absolute,
238 SweepAngle = trackSweepAngle,
239 StartAngle = trackStartAngle,
242 AddVisual("Track", trackVisual);
246 trackVisual.SweepAngle = trackSweepAngle;
247 trackVisual.StartAngle = trackStartAngle;
248 trackVisual.UpdateVisual(true);
251 if (thumbVisual == null)
253 thumbVisual = new ArcVisual
255 SuppressUpdateVisual = true,
256 Thickness = trackVisual.Thickness,
257 Cap = ArcVisual.CapType.Round,
258 MixColor = ThumbColor,
259 Size = containerSize - new Size(2, 2),
260 SizePolicy = VisualTransformPolicyType.Absolute,
261 SweepAngle = thumbSweepAngle,
262 StartAngle = thumbStartAngle,
263 Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f,
266 AddVisual("Thumb", thumbVisual);
270 thumbVisual.SweepAngle = thumbSweepAngle;
271 thumbVisual.StartAngle = thumbStartAngle;
272 thumbVisual.UpdateVisual(true);
277 [EditorBrowsable(EditorBrowsableState.Never)]
278 public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
280 this.currentPosition = position;
281 this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
283 if (thumbVisual == null)
288 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
289 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
290 thumbVisual.Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f;
294 thumbVisual.UpdateVisual(true);
299 // TODO Support non built-in alpha function for visual trainsition in DALi.
300 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
302 thumbStartAngleAnimation?.Stop();
303 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
304 thumbStartAngleAnimation.Play();
306 thumbSweepAngleAnimation?.Stop();
307 thumbSweepAngleAnimation = AnimateVisual(thumbVisual, "sweepAngle", thumbVisual.SweepAngle, 0, (int)durationMs, builtinAlphaFunction);
308 thumbSweepAngleAnimation.Play();
312 [EditorBrowsable(EditorBrowsableState.Never)]
313 public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
315 currentPosition = position;
317 if (mScrollEnabled == false)
322 if (thumbVisual == null)
327 var oldThumbStartAngle = thumbVisual.StartAngle;
329 thumbVisual.StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
333 thumbVisual.UpdateVisual(true);
338 // TODO Support non built-in alpha function for visual trainsition in DALi.
339 AlphaFunction.BuiltinFunctions builtinAlphaFunction = alphaFunction?.GetBuiltinFunction() ?? AlphaFunction.BuiltinFunctions.Default;
341 thumbStartAngleAnimation?.Stop();
342 thumbStartAngleAnimation = AnimateVisual(thumbVisual, "startAngle", thumbVisual.StartAngle, 0, (int)durationMs, builtinAlphaFunction);
343 thumbStartAngleAnimation.Play();
347 [EditorBrowsable(EditorBrowsableState.Never)]
348 public override void OnRelayout(Vector2 size, RelayoutContainer container)
350 base.OnRelayout(size, container);
352 if (size.Width == containerSize?.Width && size.Height == containerSize.Height)
357 containerSize = new Size(size.Width, size.Height);
359 if (trackVisual == null)
364 trackVisual.Size = containerSize - new Size(2, 2);
365 thumbVisual.Size = containerSize - new Size(2, 2);
367 trackVisual.UpdateVisual(true);
368 thumbVisual.UpdateVisual(true);
372 [EditorBrowsable(EditorBrowsableState.Never)]
373 protected override ViewStyle CreateViewStyle()
375 return new CircularScrollbarStyle();
378 private float CalculateTrackStartAngle(float currentTrackSweepAngle)
380 return ((180.0f - currentTrackSweepAngle) / 2.0f) + directionAlpha;
383 private float CalculateTrackSweepAngle(float inputTrackSweepAngle)
385 return Math.Min(Math.Max(inputTrackSweepAngle, 3), 180);
388 private float CalculateThumbStartAngle(float position, float trackStartAngle, float trackSweepAngle, float thumbSweepAngle)
390 float minAngle = trackStartAngle;
391 float maxAngle = trackStartAngle + trackSweepAngle - thumbSweepAngle;
392 float resultAngle = trackStartAngle + (trackSweepAngle * (position < 0.0f ? 0.0f : position) / contentLength);
394 return Math.Min(Math.Max(resultAngle, minAngle), maxAngle);
397 private float CalculateThumbSweepAngle(float trackSweepAngle)
399 return trackSweepAngle * visibleLength / contentLength;
402 private bool CalculateThumbVisibility()
404 return contentLength > visibleLength;
407 private void UpdateVisualThickness(float thickness)
409 if (trackVisual == null)
414 trackVisual.Thickness = thickness;
415 thumbVisual.Thickness = thickness;
417 trackVisual.UpdateVisual(true);
418 thumbVisual.UpdateVisual(true);
421 private void UpdateTrackVisualSweepAngle(float trackSweepAngle)
423 if (trackVisual == null || thumbVisual == null)
428 trackVisual.SweepAngle = CalculateTrackSweepAngle(trackSweepAngle);
429 trackVisual.StartAngle = CalculateTrackStartAngle(trackVisual.SweepAngle);
431 thumbVisual.SweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
432 thumbVisual.StartAngle = CalculateThumbStartAngle(currentPosition, trackVisual.StartAngle, trackVisual.SweepAngle, thumbVisual.SweepAngle);
434 trackVisual.UpdateVisual(true);
435 thumbVisual.UpdateVisual(true);
438 private void UpdateTrackVisualColor(Color trackColor)
440 if (trackVisual == null)
445 trackVisual.MixColor = trackColor;
446 trackVisual.UpdateVisual(true);
449 private void UpdateThumbVisualColor(Color thumbColor)
451 if (thumbVisual == null)
456 thumbVisual.MixColor = thumbColor;
457 thumbVisual.UpdateVisual(true);
461 [EditorBrowsable(EditorBrowsableState.Never)]
462 public override bool ScrollEnabled
466 return mScrollEnabled;
470 if (value != mScrollEnabled)
472 mScrollEnabled = value;