public static readonly BindableProperty ThicknessProperty = BindableProperty.Create(nameof(Thickness), typeof(float), typeof(CircularScrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
{
var instance = ((CircularScrollbar)bindable);
- var thickness = (float?)newValue;
-
- ((CircularScrollbarStyle)instance.viewStyle).Thickness = thickness;
- instance.UpdateVisualThickness(thickness ?? 0);
+ float value = (float?)newValue ?? 0;
+ instance.CurrentStyle.Thickness = value;
+ instance.UpdateVisualThickness(value);
},
defaultValueCreator: (bindable) =>
{
- return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.Thickness ?? 0;
+ var instance = (CircularScrollbar)bindable;
+ return instance.CurrentStyle.Thickness ?? 0;
});
/// <summary>Bindable property of TrackSweepAngle</summary>
public static readonly BindableProperty TrackSweepAngleProperty = BindableProperty.Create(nameof(TrackSweepAngle), typeof(float), typeof(CircularScrollbar), default(float), propertyChanged: (bindable, oldValue, newValue) =>
{
var instance = ((CircularScrollbar)bindable);
- var angle = (float?)newValue;
-
- ((CircularScrollbarStyle)instance.viewStyle).TrackSweepAngle = angle;
- instance.UpdateTrackVisualSweepAngle(angle ?? 0);
+ float value = (float?)newValue ?? 0;
+ instance.CurrentStyle.TrackSweepAngle = value;
+ instance.UpdateTrackVisualSweepAngle(value);
},
defaultValueCreator: (bindable) =>
{
- return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.TrackSweepAngle ?? 0;
+ var instance = (CircularScrollbar)bindable;
+ return instance.CurrentStyle.TrackSweepAngle ?? 0;
});
/// <summary>Bindable property of TrackColor</summary>
public static readonly BindableProperty TrackColorProperty = BindableProperty.Create(nameof(TrackColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
{
var instance = ((CircularScrollbar)bindable);
- var color = (Color)newValue;
-
- ((CircularScrollbarStyle)instance.viewStyle).TrackColor = color;
- instance.UpdateTrackVisualColor(color);
+ instance.CurrentStyle.TrackColor = (Color)newValue;
+ instance.UpdateTrackVisualColor((Color)newValue);
},
defaultValueCreator: (bindable) =>
{
- return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.TrackColor;
+ return ((CircularScrollbar)bindable).CurrentStyle.TrackColor;
});
/// <summary>Bindable property of ThumbColor</summary>
public static readonly BindableProperty ThumbColorProperty = BindableProperty.Create(nameof(ThumbColor), typeof(Color), typeof(CircularScrollbar), null, propertyChanged: (bindable, oldValue, newValue) =>
{
var instance = ((CircularScrollbar)bindable);
- var color = (Color)newValue;
-
- ((CircularScrollbarStyle)instance.viewStyle).ThumbColor = color;
- instance.UpdateThumbVisualColor(color);
+ instance.CurrentStyle.ThumbColor = (Color)newValue;
+ instance.UpdateThumbVisualColor((Color)newValue);
},
defaultValueCreator: (bindable) =>
{
- return ((CircularScrollbarStyle)((CircularScrollbar)bindable).viewStyle)?.ThumbColor;
+ return ((CircularScrollbar)bindable).CurrentStyle.ThumbColor;
});
private ArcVisual trackVisual;
private ArcVisual thumbVisual;
private float contentLength;
private float visibleLength;
+ private float previousPosition;
private float currentPosition;
private float directionAlpha;
+ private Size containerSize = new Size(0, 0);
private Animation thumbStartAngleAnimation;
private Animation thumbSweepAngleAnimation;
+ private bool mScrollEnabled = true;
#endregion Fields
/// <summary>
/// Create a CircularScrollbar and initialize with properties.
/// </summary>
- /// <param name="contentLength">The total length of the content.</param>
- /// <param name="viewSize">The size of View that contains the content to scroll.</param>
- /// <param name="currentPosition">Scrolled position.</param>
- /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical if the value is false.</param>
+ /// <param name="contentLength">The length of the scrollable content area.</param>
+ /// <param name="viewportLength">The length of the viewport representing the amount of visible content.</param>
+ /// <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>
+ /// <param name="isHorizontal">Whether the direction of scrolling is horizontal or not. It is vertical by default.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
- public CircularScrollbar(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false) : base(new CircularScrollbarStyle())
+ public CircularScrollbar(float contentLength, float viewportLength, float currentPosition, bool isHorizontal = false) : base(new CircularScrollbarStyle())
{
- Initialize(contentLength, viewSize, currentPosition, isHorizontal);
+ Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
}
/// <summary>
#region Properties
/// <summary>
+ /// Return a copied Style instance of CircularScrollbar
+ /// </summary>
+ /// <remarks>
+ /// It returns copied Style instance and changing it does not effect to the CircularScrollbar.
+ /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public CircularScrollbarStyle Style
+ {
+ get
+ {
+ var result = new CircularScrollbarStyle(ViewStyle as CircularScrollbarStyle);
+ result.CopyPropertiesFromView(this);
+ return result;
+ }
+ }
+
+ /// <summary>
/// The thickness of the scrollbar and track.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
set => SetValue(ThumbColorProperty, value);
}
+ private CircularScrollbarStyle CurrentStyle => ViewStyle as CircularScrollbarStyle;
+
#endregion Properties
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
- public override void Initialize(float contentLength, Size viewSize, float currentPosition, bool isHorizontal = false)
+ public override void Initialize(float contentLength, float viewportLenth, float currentPosition, bool isHorizontal = false)
{
this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
- this.visibleLength = isHorizontal ? viewSize.Width : viewSize.Height;
+ this.visibleLength = viewportLenth;
this.currentPosition = currentPosition;
this.directionAlpha = isHorizontal ? 270.0f : 0.0f;
- Size = viewSize;
-
thumbStartAngleAnimation?.Stop();
thumbStartAngleAnimation = null;
thumbSweepAngleAnimation?.Stop();
thumbSweepAngleAnimation = null;
- CreateTrackVisual();
- CreateThumbVisual(currentPosition);
- AddVisual("Track", trackVisual);
- AddVisual("Thumb", thumbVisual);
+ float trackSweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
+ float trackStartAngle = CalculateTrackStartAngle(trackSweepAngle);
+ float thumbSweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
+ float thumbStartAngle = CalculateThumbStartAngle(currentPosition, trackStartAngle, trackSweepAngle, thumbSweepAngle);
+
+ if (trackVisual == null)
+ {
+ trackVisual = new ArcVisual
+ {
+ SuppressUpdateVisual = true,
+ Thickness = this.Thickness,
+ Cap = ArcVisual.CapType.Round,
+ MixColor = TrackColor,
+ Size = containerSize - new Size(2, 2),
+ SizePolicy = VisualTransformPolicyType.Absolute,
+ SweepAngle = trackSweepAngle,
+ StartAngle = trackStartAngle,
+ };
+
+ AddVisual("Track", trackVisual);
+ }
+ else
+ {
+ trackVisual.SweepAngle = trackSweepAngle;
+ trackVisual.StartAngle = trackStartAngle;
+ trackVisual.UpdateVisual(true);
+ }
+
+ if (thumbVisual == null)
+ {
+ thumbVisual = new ArcVisual
+ {
+ SuppressUpdateVisual = true,
+ Thickness = trackVisual.Thickness,
+ Cap = ArcVisual.CapType.Round,
+ MixColor = ThumbColor,
+ Size = containerSize - new Size(2, 2),
+ SizePolicy = VisualTransformPolicyType.Absolute,
+ SweepAngle = thumbSweepAngle,
+ StartAngle = thumbStartAngle,
+ Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f,
+ };
+
+ AddVisual("Thumb", thumbVisual);
+ }
+ else
+ {
+ thumbVisual.SweepAngle = thumbSweepAngle;
+ thumbVisual.StartAngle = thumbStartAngle;
+ thumbVisual.UpdateVisual(true);
+ }
}
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override void Update(float contentLength, float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
{
+ this.previousPosition = this.currentPosition;
this.currentPosition = position;
this.contentLength = contentLength > 0.0f ? contentLength : 0.0f;
[EditorBrowsable(EditorBrowsableState.Never)]
public override void ScrollTo(float position, uint durationMs = 0, AlphaFunction alphaFunction = null)
{
+ previousPosition = currentPosition;
currentPosition = position;
+ if (mScrollEnabled == false)
+ {
+ return;
+ }
+
if (thumbVisual == null)
{
return;
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
- protected override ViewStyle GetViewStyle()
+ public override void OnRelayout(Vector2 size, RelayoutContainer container)
{
- return new CircularScrollbarStyle();
- }
+ base.OnRelayout(size, container);
- private void CreateTrackVisual()
- {
- float sweepAngle = CalculateTrackSweepAngle(TrackSweepAngle);
+ if (size.Width == containerSize?.Width && size.Height == containerSize.Height)
+ {
+ return;
+ }
+
+ containerSize = new Size(size.Width, size.Height);
- trackVisual = new ArcVisual
+ if (trackVisual == null)
{
- SuppressUpdateVisual = true,
- Thickness = this.Thickness,
- Cap = ArcVisual.CapType.Round,
- MixColor = TrackColor,
- Size = new Size(visibleLength - 2, visibleLength - 2),
- SizePolicy = VisualTransformPolicyType.Absolute,
- SweepAngle = sweepAngle,
- StartAngle = CalculateTrackStartAngle(sweepAngle),
- };
+ return;
+ }
+
+ trackVisual.Size = containerSize - new Size(2, 2);
+ thumbVisual.Size = containerSize - new Size(2, 2);
+
+ trackVisual.UpdateVisual(true);
+ thumbVisual.UpdateVisual(true);
}
- private void CreateThumbVisual(float position)
+ /// <inheritdoc/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override ViewStyle CreateViewStyle()
{
- float sweepAngle = CalculateThumbSweepAngle(TrackSweepAngle);
-
- thumbVisual = new ArcVisual
- {
- SuppressUpdateVisual = true,
- Thickness = trackVisual.Thickness,
- Cap = ArcVisual.CapType.Round,
- MixColor = ThumbColor,
- Size = new Size(visibleLength - 2, visibleLength - 2),
- SizePolicy = VisualTransformPolicyType.Absolute,
- SweepAngle = sweepAngle,
- StartAngle = CalculateThumbStartAngle(position, trackVisual.StartAngle, trackVisual.SweepAngle, sweepAngle),
- Opacity = CalculateThumbVisibility() ? 1.0f : 0.0f,
- };
+ return new CircularScrollbarStyle();
}
private float CalculateTrackStartAngle(float currentTrackSweepAngle)
thumbVisual.UpdateVisual(true);
}
+ /// <inheritdoc/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool ScrollEnabled
+ {
+ get
+ {
+ return mScrollEnabled;
+ }
+ set
+ {
+ if (value != mScrollEnabled)
+ {
+ mScrollEnabled = value;
+ }
+ }
+ }
+
+ /// <inheritdoc/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override Position ScrollPosition
+ {
+ get
+ {
+ bool isHorizontal = (directionAlpha == 270.0f) ? true : false;
+ float length = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
+
+ return (isHorizontal ? new Position(length, 0.0f) : new Position(0.0f, length));
+ }
+ }
+
+ /// <inheritdoc/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override Position ScrollCurrentPosition
+ {
+ get
+ {
+ bool isHorizontal = (directionAlpha == 270.0f) ? true : false;
+ float length = Math.Min(Math.Max(currentPosition, 0.0f), contentLength - visibleLength);
+
+ if (thumbStartAngleAnimation != null)
+ {
+ float progress = thumbStartAngleAnimation.CurrentProgress;
+ float previousLength = Math.Min(Math.Max(previousPosition, 0.0f), contentLength - visibleLength);
+
+ length = ((1.0f - progress) * previousLength) + (progress * length);
+ }
+
+ return (isHorizontal ? new Position(length, 0.0f) : new Position(0.0f, length));
+ }
+ }
+
#endregion Methods
}
}