Fix compilation: restore clean RC1 codebase
- Restore clean BindableProperty.Create syntax from RC1 commit - Remove decompiler artifacts with mangled delegate types - Add Svg.Skia package reference for icon support - Fix duplicate type definitions - Library now compiles successfully (0 errors) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,381 +1,403 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Microsoft.Maui.Platform;
|
||||
|
||||
/// <summary>
|
||||
/// A horizontally scrolling carousel view with snap-to-item behavior.
|
||||
/// </summary>
|
||||
public class SkiaCarouselView : SkiaLayoutView
|
||||
{
|
||||
private readonly List<SkiaView> _items = new List<SkiaView>();
|
||||
private readonly List<SkiaView> _items = new();
|
||||
private int _currentPosition = 0;
|
||||
private float _scrollOffset = 0f;
|
||||
private float _targetScrollOffset = 0f;
|
||||
private bool _isDragging = false;
|
||||
private float _dragStartX;
|
||||
private float _dragStartOffset;
|
||||
private float _velocity = 0f;
|
||||
private DateTime _lastDragTime;
|
||||
private float _lastDragX;
|
||||
|
||||
private int _currentPosition;
|
||||
// Animation
|
||||
private bool _isAnimating = false;
|
||||
private float _animationStartOffset;
|
||||
private float _animationTargetOffset;
|
||||
private DateTime _animationStartTime;
|
||||
private const float AnimationDurationMs = 300f;
|
||||
|
||||
private float _scrollOffset;
|
||||
/// <summary>
|
||||
/// Gets or sets the current position (item index).
|
||||
/// </summary>
|
||||
public int Position
|
||||
{
|
||||
get => _currentPosition;
|
||||
set
|
||||
{
|
||||
if (value >= 0 && value < _items.Count && value != _currentPosition)
|
||||
{
|
||||
int oldPosition = _currentPosition;
|
||||
_currentPosition = value;
|
||||
AnimateToPosition(value);
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(oldPosition, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float _targetScrollOffset;
|
||||
/// <summary>
|
||||
/// Gets the item count.
|
||||
/// </summary>
|
||||
public int ItemCount => _items.Count;
|
||||
|
||||
private bool _isDragging;
|
||||
/// <summary>
|
||||
/// Gets or sets whether looping is enabled.
|
||||
/// </summary>
|
||||
public bool Loop { get; set; } = false;
|
||||
|
||||
private float _dragStartX;
|
||||
/// <summary>
|
||||
/// Gets or sets the peek amount (how much of adjacent items to show).
|
||||
/// </summary>
|
||||
public float PeekAreaInsets { get; set; } = 0f;
|
||||
|
||||
private float _dragStartOffset;
|
||||
/// <summary>
|
||||
/// Gets or sets the spacing between items.
|
||||
/// </summary>
|
||||
public float ItemSpacing { get; set; } = 0f;
|
||||
|
||||
private float _velocity;
|
||||
/// <summary>
|
||||
/// Gets or sets whether swipe gestures are enabled.
|
||||
/// </summary>
|
||||
public bool IsSwipeEnabled { get; set; } = true;
|
||||
|
||||
private DateTime _lastDragTime;
|
||||
/// <summary>
|
||||
/// Gets or sets the indicator visibility.
|
||||
/// </summary>
|
||||
public bool ShowIndicators { get; set; } = true;
|
||||
|
||||
private float _lastDragX;
|
||||
/// <summary>
|
||||
/// Gets or sets the indicator color.
|
||||
/// </summary>
|
||||
public SKColor IndicatorColor { get; set; } = new SKColor(180, 180, 180);
|
||||
|
||||
private bool _isAnimating;
|
||||
/// <summary>
|
||||
/// Gets or sets the selected indicator color.
|
||||
/// </summary>
|
||||
public SKColor SelectedIndicatorColor { get; set; } = new SKColor(33, 150, 243);
|
||||
|
||||
private float _animationStartOffset;
|
||||
/// <summary>
|
||||
/// Event raised when position changes.
|
||||
/// </summary>
|
||||
public event EventHandler<PositionChangedEventArgs>? PositionChanged;
|
||||
|
||||
private float _animationTargetOffset;
|
||||
/// <summary>
|
||||
/// Event raised when scrolling.
|
||||
/// </summary>
|
||||
public event EventHandler? Scrolled;
|
||||
|
||||
private DateTime _animationStartTime;
|
||||
/// <summary>
|
||||
/// Adds an item to the carousel.
|
||||
/// </summary>
|
||||
public void AddItem(SkiaView item)
|
||||
{
|
||||
_items.Add(item);
|
||||
AddChild(item);
|
||||
InvalidateMeasure();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private const float AnimationDurationMs = 300f;
|
||||
/// <summary>
|
||||
/// Removes an item from the carousel.
|
||||
/// </summary>
|
||||
public void RemoveItem(SkiaView item)
|
||||
{
|
||||
if (_items.Remove(item))
|
||||
{
|
||||
RemoveChild(item);
|
||||
if (_currentPosition >= _items.Count)
|
||||
{
|
||||
_currentPosition = Math.Max(0, _items.Count - 1);
|
||||
}
|
||||
InvalidateMeasure();
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public int Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentPosition;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value >= 0 && value < _items.Count && value != _currentPosition)
|
||||
{
|
||||
int currentPosition = _currentPosition;
|
||||
_currentPosition = value;
|
||||
AnimateToPosition(value);
|
||||
this.PositionChanged?.Invoke(this, new PositionChangedEventArgs(currentPosition, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Clears all items.
|
||||
/// </summary>
|
||||
public void ClearItems()
|
||||
{
|
||||
foreach (var item in _items)
|
||||
{
|
||||
RemoveChild(item);
|
||||
}
|
||||
_items.Clear();
|
||||
_currentPosition = 0;
|
||||
_scrollOffset = 0;
|
||||
_targetScrollOffset = 0;
|
||||
InvalidateMeasure();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public int ItemCount => _items.Count;
|
||||
/// <summary>
|
||||
/// Scrolls to the specified position.
|
||||
/// </summary>
|
||||
public void ScrollTo(int position, bool animate = true)
|
||||
{
|
||||
if (position < 0 || position >= _items.Count) return;
|
||||
|
||||
public bool Loop { get; set; }
|
||||
int oldPosition = _currentPosition;
|
||||
_currentPosition = position;
|
||||
|
||||
public float PeekAreaInsets { get; set; }
|
||||
if (animate)
|
||||
{
|
||||
AnimateToPosition(position);
|
||||
}
|
||||
else
|
||||
{
|
||||
_scrollOffset = GetOffsetForPosition(position);
|
||||
_targetScrollOffset = _scrollOffset;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public float ItemSpacing { get; set; }
|
||||
if (oldPosition != position)
|
||||
{
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(oldPosition, position));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSwipeEnabled { get; set; } = true;
|
||||
private void AnimateToPosition(int position)
|
||||
{
|
||||
_animationStartOffset = _scrollOffset;
|
||||
_animationTargetOffset = GetOffsetForPosition(position);
|
||||
_animationStartTime = DateTime.UtcNow;
|
||||
_isAnimating = true;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public bool ShowIndicators { get; set; } = true;
|
||||
private float GetOffsetForPosition(int position)
|
||||
{
|
||||
float itemWidth = Bounds.Width - PeekAreaInsets * 2;
|
||||
return position * (itemWidth + ItemSpacing);
|
||||
}
|
||||
|
||||
public SKColor IndicatorColor { get; set; } = new SKColor((byte)180, (byte)180, (byte)180);
|
||||
private int GetPositionForOffset(float offset)
|
||||
{
|
||||
float itemWidth = Bounds.Width - PeekAreaInsets * 2;
|
||||
if (itemWidth <= 0) return 0;
|
||||
return Math.Clamp((int)Math.Round(offset / (itemWidth + ItemSpacing)), 0, Math.Max(0, _items.Count - 1));
|
||||
}
|
||||
|
||||
public SKColor SelectedIndicatorColor { get; set; } = new SKColor((byte)33, (byte)150, (byte)243);
|
||||
protected override SKSize MeasureOverride(SKSize availableSize)
|
||||
{
|
||||
float itemWidth = availableSize.Width - PeekAreaInsets * 2;
|
||||
float itemHeight = availableSize.Height - (ShowIndicators ? 30 : 0);
|
||||
|
||||
public event EventHandler<PositionChangedEventArgs>? PositionChanged;
|
||||
foreach (var item in _items)
|
||||
{
|
||||
item.Measure(new SKSize(itemWidth, itemHeight));
|
||||
}
|
||||
|
||||
public event EventHandler? Scrolled;
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
public void AddItem(SkiaView item)
|
||||
{
|
||||
_items.Add(item);
|
||||
AddChild(item);
|
||||
InvalidateMeasure();
|
||||
Invalidate();
|
||||
}
|
||||
protected override SKRect ArrangeOverride(SKRect bounds)
|
||||
{
|
||||
float itemWidth = bounds.Width - PeekAreaInsets * 2;
|
||||
float itemHeight = bounds.Height - (ShowIndicators ? 30 : 0);
|
||||
|
||||
public void RemoveItem(SkiaView item)
|
||||
{
|
||||
if (_items.Remove(item))
|
||||
{
|
||||
RemoveChild(item);
|
||||
if (_currentPosition >= _items.Count)
|
||||
{
|
||||
_currentPosition = Math.Max(0, _items.Count - 1);
|
||||
}
|
||||
InvalidateMeasure();
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
float x = bounds.Left + PeekAreaInsets + i * (itemWidth + ItemSpacing) - _scrollOffset;
|
||||
var itemBounds = new SKRect(x, bounds.Top, x + itemWidth, bounds.Top + itemHeight);
|
||||
_items[i].Arrange(itemBounds);
|
||||
}
|
||||
|
||||
public void ClearItems()
|
||||
{
|
||||
foreach (SkiaView item in _items)
|
||||
{
|
||||
RemoveChild(item);
|
||||
}
|
||||
_items.Clear();
|
||||
_currentPosition = 0;
|
||||
_scrollOffset = 0f;
|
||||
_targetScrollOffset = 0f;
|
||||
InvalidateMeasure();
|
||||
Invalidate();
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public void ScrollTo(int position, bool animate = true)
|
||||
{
|
||||
if (position >= 0 && position < _items.Count)
|
||||
{
|
||||
int currentPosition = _currentPosition;
|
||||
_currentPosition = position;
|
||||
if (animate)
|
||||
{
|
||||
AnimateToPosition(position);
|
||||
}
|
||||
else
|
||||
{
|
||||
_scrollOffset = GetOffsetForPosition(position);
|
||||
_targetScrollOffset = _scrollOffset;
|
||||
Invalidate();
|
||||
}
|
||||
if (currentPosition != position)
|
||||
{
|
||||
this.PositionChanged?.Invoke(this, new PositionChangedEventArgs(currentPosition, position));
|
||||
}
|
||||
}
|
||||
}
|
||||
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
||||
{
|
||||
// Update animation
|
||||
if (_isAnimating)
|
||||
{
|
||||
float elapsed = (float)(DateTime.UtcNow - _animationStartTime).TotalMilliseconds;
|
||||
float progress = Math.Clamp(elapsed / AnimationDurationMs, 0f, 1f);
|
||||
|
||||
private void AnimateToPosition(int position)
|
||||
{
|
||||
_animationStartOffset = _scrollOffset;
|
||||
_animationTargetOffset = GetOffsetForPosition(position);
|
||||
_animationStartTime = DateTime.UtcNow;
|
||||
_isAnimating = true;
|
||||
Invalidate();
|
||||
}
|
||||
// Ease out cubic
|
||||
float t = 1f - (1f - progress) * (1f - progress) * (1f - progress);
|
||||
|
||||
private float GetOffsetForPosition(int position)
|
||||
{
|
||||
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
|
||||
SKRect bounds = base.Bounds;
|
||||
float num = ((SKRect)(ref bounds)).Width - PeekAreaInsets * 2f;
|
||||
return (float)position * (num + ItemSpacing);
|
||||
}
|
||||
_scrollOffset = _animationStartOffset + (_animationTargetOffset - _animationStartOffset) * t;
|
||||
|
||||
private int GetPositionForOffset(float offset)
|
||||
{
|
||||
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
|
||||
SKRect bounds = base.Bounds;
|
||||
float num = ((SKRect)(ref bounds)).Width - PeekAreaInsets * 2f;
|
||||
if (num <= 0f)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Math.Clamp((int)Math.Round(offset / (num + ItemSpacing)), 0, Math.Max(0, _items.Count - 1));
|
||||
}
|
||||
if (progress >= 1f)
|
||||
{
|
||||
_isAnimating = false;
|
||||
_scrollOffset = _animationTargetOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
Invalidate(); // Continue animation
|
||||
}
|
||||
}
|
||||
|
||||
protected override SKSize MeasureOverride(SKSize availableSize)
|
||||
{
|
||||
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
|
||||
float num = ((SKSize)(ref availableSize)).Width - PeekAreaInsets * 2f;
|
||||
float num2 = ((SKSize)(ref availableSize)).Height - (float)(ShowIndicators ? 30 : 0);
|
||||
foreach (SkiaView item in _items)
|
||||
{
|
||||
item.Measure(new SKSize(num, num2));
|
||||
}
|
||||
return availableSize;
|
||||
}
|
||||
canvas.Save();
|
||||
canvas.ClipRect(bounds);
|
||||
|
||||
protected override SKRect ArrangeOverride(SKRect bounds)
|
||||
{
|
||||
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
|
||||
float num = ((SKRect)(ref bounds)).Width - PeekAreaInsets * 2f;
|
||||
float num2 = ((SKRect)(ref bounds)).Height - (float)(ShowIndicators ? 30 : 0);
|
||||
SKRect bounds2 = default(SKRect);
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
float num3 = ((SKRect)(ref bounds)).Left + PeekAreaInsets + (float)i * (num + ItemSpacing) - _scrollOffset;
|
||||
((SKRect)(ref bounds2))._002Ector(num3, ((SKRect)(ref bounds)).Top, num3 + num, ((SKRect)(ref bounds)).Top + num2);
|
||||
_items[i].Arrange(bounds2);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
// Draw visible items
|
||||
float itemWidth = bounds.Width - PeekAreaInsets * 2;
|
||||
float contentHeight = bounds.Height - (ShowIndicators ? 30 : 0);
|
||||
|
||||
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
||||
{
|
||||
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_014c: Unknown result type (might be due to invalid IL or missing references)
|
||||
if (_isAnimating)
|
||||
{
|
||||
float num = Math.Clamp((float)(DateTime.UtcNow - _animationStartTime).TotalMilliseconds / 300f, 0f, 1f);
|
||||
float num2 = 1f - (1f - num) * (1f - num) * (1f - num);
|
||||
_scrollOffset = _animationStartOffset + (_animationTargetOffset - _animationStartOffset) * num2;
|
||||
if (num >= 1f)
|
||||
{
|
||||
_isAnimating = false;
|
||||
_scrollOffset = _animationTargetOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
canvas.Save();
|
||||
canvas.ClipRect(bounds, (SKClipOperation)1, false);
|
||||
float num3 = ((SKRect)(ref bounds)).Width - PeekAreaInsets * 2f;
|
||||
_ = ((SKRect)(ref bounds)).Height;
|
||||
_ = ShowIndicators;
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
float num4 = ((SKRect)(ref bounds)).Left + PeekAreaInsets + (float)i * (num3 + ItemSpacing) - _scrollOffset;
|
||||
if (num4 + num3 > ((SKRect)(ref bounds)).Left && num4 < ((SKRect)(ref bounds)).Right)
|
||||
{
|
||||
_items[i].Draw(canvas);
|
||||
}
|
||||
}
|
||||
if (ShowIndicators && _items.Count > 1)
|
||||
{
|
||||
DrawIndicators(canvas, bounds);
|
||||
}
|
||||
canvas.Restore();
|
||||
}
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
float x = bounds.Left + PeekAreaInsets + i * (itemWidth + ItemSpacing) - _scrollOffset;
|
||||
|
||||
private void DrawIndicators(SKCanvas canvas, SKRect bounds)
|
||||
{
|
||||
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_006e: Expected O, but got Unknown
|
||||
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_008f: Expected O, but got Unknown
|
||||
float num = 8f;
|
||||
float num2 = 12f;
|
||||
float num3 = (float)_items.Count * num + (float)(_items.Count - 1) * (num2 - num);
|
||||
float num4 = ((SKRect)(ref bounds)).MidX - num3 / 2f;
|
||||
float num5 = ((SKRect)(ref bounds)).Bottom - 15f;
|
||||
SKPaint val = new SKPaint
|
||||
{
|
||||
Color = IndicatorColor,
|
||||
Style = (SKPaintStyle)0,
|
||||
IsAntialias = true
|
||||
};
|
||||
try
|
||||
{
|
||||
SKPaint val2 = new SKPaint
|
||||
{
|
||||
Color = SelectedIndicatorColor,
|
||||
Style = (SKPaintStyle)0,
|
||||
IsAntialias = true
|
||||
};
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
float num6 = num4 + (float)i * num2;
|
||||
SKPaint val3 = ((i == _currentPosition) ? val2 : val);
|
||||
canvas.DrawCircle(num6, num5, num / 2f, val3);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
((IDisposable)val2)?.Dispose();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
((IDisposable)val)?.Dispose();
|
||||
}
|
||||
}
|
||||
// Only draw visible items
|
||||
if (x + itemWidth > bounds.Left && x < bounds.Right)
|
||||
{
|
||||
_items[i].Draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
public override SkiaView? HitTest(float x, float y)
|
||||
{
|
||||
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
|
||||
if (base.IsVisible)
|
||||
{
|
||||
SKRect bounds = base.Bounds;
|
||||
if (((SKRect)(ref bounds)).Contains(x, y))
|
||||
{
|
||||
{
|
||||
foreach (SkiaView item in _items)
|
||||
{
|
||||
SkiaView skiaView = item.HitTest(x, y);
|
||||
if (skiaView != null)
|
||||
{
|
||||
return skiaView;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Draw indicators
|
||||
if (ShowIndicators && _items.Count > 1)
|
||||
{
|
||||
DrawIndicators(canvas, bounds);
|
||||
}
|
||||
|
||||
public override void OnPointerPressed(PointerEventArgs e)
|
||||
{
|
||||
if (base.IsEnabled && IsSwipeEnabled)
|
||||
{
|
||||
_isDragging = true;
|
||||
_dragStartX = e.X;
|
||||
_dragStartOffset = _scrollOffset;
|
||||
_lastDragX = e.X;
|
||||
_lastDragTime = DateTime.UtcNow;
|
||||
_velocity = 0f;
|
||||
_isAnimating = false;
|
||||
e.Handled = true;
|
||||
base.OnPointerPressed(e);
|
||||
}
|
||||
}
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
public override void OnPointerMoved(PointerEventArgs e)
|
||||
{
|
||||
if (_isDragging)
|
||||
{
|
||||
float num = _dragStartX - e.X;
|
||||
_scrollOffset = _dragStartOffset + num;
|
||||
float offsetForPosition = GetOffsetForPosition(_items.Count - 1);
|
||||
_scrollOffset = Math.Clamp(_scrollOffset, 0f, offsetForPosition);
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
float num2 = (float)(utcNow - _lastDragTime).TotalSeconds;
|
||||
if (num2 > 0f)
|
||||
{
|
||||
_velocity = (_lastDragX - e.X) / num2;
|
||||
}
|
||||
_lastDragX = e.X;
|
||||
_lastDragTime = utcNow;
|
||||
this.Scrolled?.Invoke(this, EventArgs.Empty);
|
||||
Invalidate();
|
||||
e.Handled = true;
|
||||
base.OnPointerMoved(e);
|
||||
}
|
||||
}
|
||||
private void DrawIndicators(SKCanvas canvas, SKRect bounds)
|
||||
{
|
||||
float indicatorSize = 8f;
|
||||
float indicatorSpacing = 12f;
|
||||
float totalWidth = _items.Count * indicatorSize + (_items.Count - 1) * (indicatorSpacing - indicatorSize);
|
||||
float startX = bounds.MidX - totalWidth / 2;
|
||||
float y = bounds.Bottom - 15;
|
||||
|
||||
public override void OnPointerReleased(PointerEventArgs e)
|
||||
{
|
||||
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
|
||||
if (!_isDragging)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isDragging = false;
|
||||
SKRect bounds = base.Bounds;
|
||||
_ = ((SKRect)(ref bounds)).Width;
|
||||
_ = PeekAreaInsets;
|
||||
int num = GetPositionForOffset(_scrollOffset);
|
||||
if (Math.Abs(_velocity) > 500f)
|
||||
{
|
||||
if (_velocity > 0f && num < _items.Count - 1)
|
||||
{
|
||||
num++;
|
||||
}
|
||||
else if (_velocity < 0f && num > 0)
|
||||
{
|
||||
num--;
|
||||
}
|
||||
}
|
||||
ScrollTo(num);
|
||||
e.Handled = true;
|
||||
base.OnPointerReleased(e);
|
||||
}
|
||||
using var normalPaint = new SKPaint
|
||||
{
|
||||
Color = IndicatorColor,
|
||||
Style = SKPaintStyle.Fill,
|
||||
IsAntialias = true
|
||||
};
|
||||
|
||||
using var selectedPaint = new SKPaint
|
||||
{
|
||||
Color = SelectedIndicatorColor,
|
||||
Style = SKPaintStyle.Fill,
|
||||
IsAntialias = true
|
||||
};
|
||||
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
float x = startX + i * indicatorSpacing;
|
||||
var paint = i == _currentPosition ? selectedPaint : normalPaint;
|
||||
canvas.DrawCircle(x, y, indicatorSize / 2, paint);
|
||||
}
|
||||
}
|
||||
|
||||
public override SkiaView? HitTest(float x, float y)
|
||||
{
|
||||
if (!IsVisible || !Bounds.Contains(x, y)) return null;
|
||||
|
||||
// Check items
|
||||
foreach (var item in _items)
|
||||
{
|
||||
var hit = item.HitTest(x, y);
|
||||
if (hit != null) return hit;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public override void OnPointerPressed(PointerEventArgs e)
|
||||
{
|
||||
if (!IsEnabled || !IsSwipeEnabled) return;
|
||||
|
||||
_isDragging = true;
|
||||
_dragStartX = e.X;
|
||||
_dragStartOffset = _scrollOffset;
|
||||
_lastDragX = e.X;
|
||||
_lastDragTime = DateTime.UtcNow;
|
||||
_velocity = 0;
|
||||
_isAnimating = false;
|
||||
|
||||
e.Handled = true;
|
||||
base.OnPointerPressed(e);
|
||||
}
|
||||
|
||||
public override void OnPointerMoved(PointerEventArgs e)
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
float delta = _dragStartX - e.X;
|
||||
_scrollOffset = _dragStartOffset + delta;
|
||||
|
||||
// Clamp scrolling
|
||||
float maxOffset = GetOffsetForPosition(_items.Count - 1);
|
||||
_scrollOffset = Math.Clamp(_scrollOffset, 0, maxOffset);
|
||||
|
||||
// Calculate velocity
|
||||
var now = DateTime.UtcNow;
|
||||
float timeDelta = (float)(now - _lastDragTime).TotalSeconds;
|
||||
if (timeDelta > 0)
|
||||
{
|
||||
_velocity = (_lastDragX - e.X) / timeDelta;
|
||||
}
|
||||
_lastDragX = e.X;
|
||||
_lastDragTime = now;
|
||||
|
||||
Scrolled?.Invoke(this, EventArgs.Empty);
|
||||
Invalidate();
|
||||
e.Handled = true;
|
||||
|
||||
base.OnPointerMoved(e);
|
||||
}
|
||||
|
||||
public override void OnPointerReleased(PointerEventArgs e)
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
_isDragging = false;
|
||||
|
||||
// Determine target position based on velocity and position
|
||||
float itemWidth = Bounds.Width - PeekAreaInsets * 2;
|
||||
int targetPosition = GetPositionForOffset(_scrollOffset);
|
||||
|
||||
// Apply velocity influence
|
||||
if (Math.Abs(_velocity) > 500)
|
||||
{
|
||||
if (_velocity > 0 && targetPosition < _items.Count - 1)
|
||||
{
|
||||
targetPosition++;
|
||||
}
|
||||
else if (_velocity < 0 && targetPosition > 0)
|
||||
{
|
||||
targetPosition--;
|
||||
}
|
||||
}
|
||||
|
||||
ScrollTo(targetPosition, true);
|
||||
e.Handled = true;
|
||||
|
||||
base.OnPointerReleased(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event args for position changed events.
|
||||
/// </summary>
|
||||
public class PositionChangedEventArgs : EventArgs
|
||||
{
|
||||
public int PreviousPosition { get; }
|
||||
public int CurrentPosition { get; }
|
||||
|
||||
public PositionChangedEventArgs(int previousPosition, int currentPosition)
|
||||
{
|
||||
PreviousPosition = previousPosition;
|
||||
CurrentPosition = currentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user