Update with recovered code from VM binaries (Jan 1)

Recovered from decompiled OpenMaui.Controls.Linux.dll:
- SkiaShell.cs: FlyoutHeader, FlyoutFooter, scroll support (918 -> 1325 lines)
- X11Window.cs: Cursor support (XCreateFontCursor, XDefineCursor)
- All handlers with dark mode support
- All services with latest implementations
- LinuxApplication with theme change handling
This commit is contained in:
2026-01-01 06:22:48 -05:00
parent 1e84c6168a
commit 1f096c38dc
254 changed files with 49359 additions and 38457 deletions

View File

@@ -1,469 +1,375 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using SkiaSharp;
namespace Microsoft.Maui.Platform;
/// <summary>
/// A view that supports swipe gestures to reveal actions.
/// </summary>
public class SkiaSwipeView : SkiaLayoutView
{
private SkiaView? _content;
private readonly List<SwipeItem> _leftItems = new();
private readonly List<SwipeItem> _rightItems = new();
private readonly List<SwipeItem> _topItems = new();
private readonly List<SwipeItem> _bottomItems = new();
private SkiaView? _content;
private float _swipeOffset = 0f;
private SwipeDirection _activeDirection = SwipeDirection.None;
private bool _isSwiping = false;
private float _swipeStartX;
private float _swipeStartY;
private float _swipeStartOffset;
private bool _isOpen = false;
private readonly List<SwipeItem> _leftItems = new List<SwipeItem>();
private const float SwipeThreshold = 60f;
private const float VelocityThreshold = 500f;
private float _velocity;
private DateTime _lastMoveTime;
private float _lastMovePosition;
private readonly List<SwipeItem> _rightItems = new List<SwipeItem>();
/// <summary>
/// Gets or sets the content view.
/// </summary>
public SkiaView? Content
{
get => _content;
set
{
if (_content != value)
{
if (_content != null)
{
RemoveChild(_content);
}
private readonly List<SwipeItem> _topItems = new List<SwipeItem>();
_content = value;
private readonly List<SwipeItem> _bottomItems = new List<SwipeItem>();
if (_content != null)
{
AddChild(_content);
}
private float _swipeOffset;
InvalidateMeasure();
Invalidate();
}
}
}
private SwipeDirection _activeDirection;
/// <summary>
/// Gets the left swipe items.
/// </summary>
public IList<SwipeItem> LeftItems => _leftItems;
private bool _isSwiping;
/// <summary>
/// Gets the right swipe items.
/// </summary>
public IList<SwipeItem> RightItems => _rightItems;
private float _swipeStartX;
/// <summary>
/// Gets the top swipe items.
/// </summary>
public IList<SwipeItem> TopItems => _topItems;
private float _swipeStartY;
/// <summary>
/// Gets the bottom swipe items.
/// </summary>
public IList<SwipeItem> BottomItems => _bottomItems;
private float _swipeStartOffset;
/// <summary>
/// Gets or sets the swipe mode.
/// </summary>
public SwipeMode Mode { get; set; } = SwipeMode.Reveal;
private bool _isOpen;
/// <summary>
/// Gets or sets the left swipe threshold.
/// </summary>
public float LeftSwipeThreshold { get; set; } = 100f;
private const float SwipeThreshold = 60f;
/// <summary>
/// Gets or sets the right swipe threshold.
/// </summary>
public float RightSwipeThreshold { get; set; } = 100f;
private const float VelocityThreshold = 500f;
/// <summary>
/// Event raised when swipe is started.
/// </summary>
public event EventHandler<SwipeStartedEventArgs>? SwipeStarted;
private float _velocity;
/// <summary>
/// Event raised when swipe ends.
/// </summary>
public event EventHandler<SwipeEndedEventArgs>? SwipeEnded;
private DateTime _lastMoveTime;
/// <summary>
/// Opens the swipe view in the specified direction.
/// </summary>
public void Open(SwipeDirection direction)
{
_activeDirection = direction;
_isOpen = true;
private float _lastMovePosition;
float targetOffset = direction switch
{
SwipeDirection.Left => -RightSwipeThreshold,
SwipeDirection.Right => LeftSwipeThreshold,
_ => 0
};
public SkiaView? Content
{
get
{
return _content;
}
set
{
if (_content != value)
{
if (_content != null)
{
RemoveChild(_content);
}
_content = value;
if (_content != null)
{
AddChild(_content);
}
InvalidateMeasure();
Invalidate();
}
}
}
AnimateTo(targetOffset);
}
public IList<SwipeItem> LeftItems => _leftItems;
/// <summary>
/// Closes the swipe view.
/// </summary>
public void Close()
{
_isOpen = false;
AnimateTo(0);
}
public IList<SwipeItem> RightItems => _rightItems;
private void AnimateTo(float target)
{
// Simple animation - in production would use proper animation
_swipeOffset = target;
Invalidate();
}
public IList<SwipeItem> TopItems => _topItems;
protected override SKSize MeasureOverride(SKSize availableSize)
{
if (_content != null)
{
_content.Measure(availableSize);
}
return availableSize;
}
public IList<SwipeItem> BottomItems => _bottomItems;
protected override SKRect ArrangeOverride(SKRect bounds)
{
if (_content != null)
{
var contentBounds = new SKRect(
bounds.Left + _swipeOffset,
bounds.Top,
bounds.Right + _swipeOffset,
bounds.Bottom);
_content.Arrange(contentBounds);
}
return bounds;
}
public SwipeMode Mode { get; set; }
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{
canvas.Save();
canvas.ClipRect(bounds);
public float LeftSwipeThreshold { get; set; } = 100f;
// Draw swipe items behind content
if (_swipeOffset > 0)
{
DrawSwipeItems(canvas, bounds, _leftItems, true);
}
else if (_swipeOffset < 0)
{
DrawSwipeItems(canvas, bounds, _rightItems, false);
}
public float RightSwipeThreshold { get; set; } = 100f;
// Draw content
_content?.Draw(canvas);
public event EventHandler<SwipeStartedEventArgs>? SwipeStarted;
canvas.Restore();
}
public event EventHandler<SwipeEndedEventArgs>? SwipeEnded;
private void DrawSwipeItems(SKCanvas canvas, SKRect bounds, List<SwipeItem> items, bool isLeft)
{
if (items.Count == 0) return;
public void Open(SwipeDirection direction)
{
_activeDirection = direction;
_isOpen = true;
AnimateTo(direction switch
{
SwipeDirection.Left => 0f - RightSwipeThreshold,
SwipeDirection.Right => LeftSwipeThreshold,
_ => 0f,
});
}
float revealWidth = Math.Abs(_swipeOffset);
float itemWidth = revealWidth / items.Count;
public void Close()
{
_isOpen = false;
AnimateTo(0f);
}
for (int i = 0; i < items.Count; i++)
{
var item = items[i];
float x = isLeft ? bounds.Left + i * itemWidth : bounds.Right - (items.Count - i) * itemWidth;
private void AnimateTo(float target)
{
_swipeOffset = target;
Invalidate();
}
var itemBounds = new SKRect(
x,
bounds.Top,
x + itemWidth,
bounds.Bottom);
protected override SKSize MeasureOverride(SKSize availableSize)
{
//IL_0015: 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)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
if (_content != null)
{
_content.Measure(availableSize);
}
return availableSize;
}
// Draw background
using var bgPaint = new SKPaint
{
Color = item.BackgroundColor,
Style = SKPaintStyle.Fill
};
canvas.DrawRect(itemBounds, bgPaint);
protected override SKRect ArrangeOverride(SKRect bounds)
{
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
if (_content != null)
{
SKRect bounds2 = default(SKRect);
((SKRect)(ref bounds2))._002Ector(((SKRect)(ref bounds)).Left + _swipeOffset, ((SKRect)(ref bounds)).Top, ((SKRect)(ref bounds)).Right + _swipeOffset, ((SKRect)(ref bounds)).Bottom);
_content.Arrange(bounds2);
}
return bounds;
}
// Draw icon or text
if (!string.IsNullOrEmpty(item.Text))
{
using var textPaint = new SKPaint
{
Color = item.TextColor,
TextSize = 14f,
IsAntialias = true,
TextAlign = SKTextAlign.Center
};
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
canvas.Save();
canvas.ClipRect(bounds, (SKClipOperation)1, false);
if (_swipeOffset > 0f)
{
DrawSwipeItems(canvas, bounds, _leftItems, isLeft: true);
}
else if (_swipeOffset < 0f)
{
DrawSwipeItems(canvas, bounds, _rightItems, isLeft: false);
}
_content?.Draw(canvas);
canvas.Restore();
}
float textY = itemBounds.MidY + 5;
canvas.DrawText(item.Text, itemBounds.MidX, textY, textPaint);
}
}
}
private void DrawSwipeItems(SKCanvas canvas, SKRect bounds, List<SwipeItem> items, bool isLeft)
{
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Expected O, but got Unknown
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Expected O, but got Unknown
if (items.Count == 0)
{
return;
}
float num = Math.Abs(_swipeOffset) / (float)items.Count;
SKRect val = default(SKRect);
for (int i = 0; i < items.Count; i++)
{
SwipeItem swipeItem = items[i];
float num2 = (isLeft ? (((SKRect)(ref bounds)).Left + (float)i * num) : (((SKRect)(ref bounds)).Right - (float)(items.Count - i) * num));
((SKRect)(ref val))._002Ector(num2, ((SKRect)(ref bounds)).Top, num2 + num, ((SKRect)(ref bounds)).Bottom);
SKPaint val2 = new SKPaint
{
Color = swipeItem.BackgroundColor,
Style = (SKPaintStyle)0
};
try
{
canvas.DrawRect(val, val2);
if (!string.IsNullOrEmpty(swipeItem.Text))
{
SKPaint val3 = new SKPaint
{
Color = swipeItem.TextColor,
TextSize = 14f,
IsAntialias = true,
TextAlign = (SKTextAlign)1
};
try
{
float num3 = ((SKRect)(ref val)).MidY + 5f;
canvas.DrawText(swipeItem.Text, ((SKRect)(ref val)).MidX, num3, val3);
}
finally
{
((IDisposable)val3)?.Dispose();
}
}
}
finally
{
((IDisposable)val2)?.Dispose();
}
}
}
public override SkiaView? HitTest(float x, float y)
{
if (!IsVisible || !Bounds.Contains(x, y)) return null;
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)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: 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))
{
if (_isOpen)
{
if (_swipeOffset > 0f)
{
bounds = base.Bounds;
if (x < ((SKRect)(ref bounds)).Left + _swipeOffset)
{
return this;
}
}
if (_swipeOffset < 0f)
{
bounds = base.Bounds;
if (x > ((SKRect)(ref bounds)).Right + _swipeOffset)
{
return this;
}
}
}
if (_content != null)
{
SkiaView skiaView = _content.HitTest(x, y);
if (skiaView != null)
{
return skiaView;
}
}
return this;
}
}
return null;
}
// Check if hit is on swipe items
if (_isOpen)
{
if (_swipeOffset > 0 && x < Bounds.Left + _swipeOffset)
{
return this; // Hit on left items
}
else if (_swipeOffset < 0 && x > Bounds.Right + _swipeOffset)
{
return this; // Hit on right items
}
}
public override void OnPointerPressed(PointerEventArgs e)
{
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
if (!base.IsEnabled)
{
return;
}
if (_isOpen)
{
SwipeItem swipeItem = null;
SKRect bounds;
if (_swipeOffset > 0f)
{
float x = e.X;
bounds = base.Bounds;
int num = (int)((x - ((SKRect)(ref bounds)).Left) / (_swipeOffset / (float)_leftItems.Count));
if (num >= 0 && num < _leftItems.Count)
{
swipeItem = _leftItems[num];
}
}
else if (_swipeOffset < 0f)
{
float num2 = Math.Abs(_swipeOffset) / (float)_rightItems.Count;
float x2 = e.X;
bounds = base.Bounds;
int num3 = (int)((x2 - (((SKRect)(ref bounds)).Right + _swipeOffset)) / num2);
if (num3 >= 0 && num3 < _rightItems.Count)
{
swipeItem = _rightItems[num3];
}
}
if (swipeItem != null)
{
swipeItem.OnInvoked();
Close();
e.Handled = true;
return;
}
}
_isSwiping = true;
_swipeStartX = e.X;
_swipeStartY = e.Y;
_swipeStartOffset = _swipeOffset;
_lastMovePosition = e.X;
_lastMoveTime = DateTime.UtcNow;
_velocity = 0f;
base.OnPointerPressed(e);
}
if (_content != null)
{
var hit = _content.HitTest(x, y);
if (hit != null) return hit;
}
public override void OnPointerMoved(PointerEventArgs e)
{
if (!_isSwiping)
{
return;
}
float num = e.X - _swipeStartX;
_ = e.Y;
_ = _swipeStartY;
if (_activeDirection == SwipeDirection.None && Math.Abs(num) > 10f)
{
_activeDirection = ((!(num > 0f)) ? SwipeDirection.Left : SwipeDirection.Right);
this.SwipeStarted?.Invoke(this, new SwipeStartedEventArgs(_activeDirection));
}
if (_activeDirection == SwipeDirection.Right || _activeDirection == SwipeDirection.Left)
{
_swipeOffset = _swipeStartOffset + num;
float max = ((_leftItems.Count > 0) ? LeftSwipeThreshold : 0f);
float min = ((_rightItems.Count > 0) ? (0f - RightSwipeThreshold) : 0f);
_swipeOffset = Math.Clamp(_swipeOffset, min, max);
DateTime utcNow = DateTime.UtcNow;
float num2 = (float)(utcNow - _lastMoveTime).TotalSeconds;
if (num2 > 0f)
{
_velocity = (e.X - _lastMovePosition) / num2;
}
_lastMovePosition = e.X;
_lastMoveTime = utcNow;
Invalidate();
e.Handled = true;
}
base.OnPointerMoved(e);
}
return this;
}
public override void OnPointerPressed(PointerEventArgs e)
{
if (!IsEnabled) return;
// Check for swipe item tap when open
if (_isOpen)
{
SwipeItem? tappedItem = null;
if (_swipeOffset > 0)
{
int index = (int)((e.X - Bounds.Left) / (_swipeOffset / _leftItems.Count));
if (index >= 0 && index < _leftItems.Count)
{
tappedItem = _leftItems[index];
}
}
else if (_swipeOffset < 0)
{
float itemWidth = Math.Abs(_swipeOffset) / _rightItems.Count;
int index = (int)((e.X - (Bounds.Right + _swipeOffset)) / itemWidth);
if (index >= 0 && index < _rightItems.Count)
{
tappedItem = _rightItems[index];
}
}
if (tappedItem != null)
{
tappedItem.OnInvoked();
Close();
e.Handled = true;
return;
}
}
_isSwiping = true;
_swipeStartX = e.X;
_swipeStartY = e.Y;
_swipeStartOffset = _swipeOffset;
_lastMovePosition = e.X;
_lastMoveTime = DateTime.UtcNow;
_velocity = 0;
base.OnPointerPressed(e);
}
public override void OnPointerMoved(PointerEventArgs e)
{
if (!_isSwiping) return;
float deltaX = e.X - _swipeStartX;
float deltaY = e.Y - _swipeStartY;
// Determine swipe direction
if (_activeDirection == SwipeDirection.None)
{
if (Math.Abs(deltaX) > 10)
{
_activeDirection = deltaX > 0 ? SwipeDirection.Right : SwipeDirection.Left;
SwipeStarted?.Invoke(this, new SwipeStartedEventArgs(_activeDirection));
}
}
if (_activeDirection == SwipeDirection.Right || _activeDirection == SwipeDirection.Left)
{
_swipeOffset = _swipeStartOffset + deltaX;
// Clamp offset based on available items
float maxRight = _leftItems.Count > 0 ? LeftSwipeThreshold : 0;
float maxLeft = _rightItems.Count > 0 ? -RightSwipeThreshold : 0;
_swipeOffset = Math.Clamp(_swipeOffset, maxLeft, maxRight);
// Calculate velocity
var now = DateTime.UtcNow;
float timeDelta = (float)(now - _lastMoveTime).TotalSeconds;
if (timeDelta > 0)
{
_velocity = (e.X - _lastMovePosition) / timeDelta;
}
_lastMovePosition = e.X;
_lastMoveTime = now;
Invalidate();
e.Handled = true;
}
base.OnPointerMoved(e);
}
public override void OnPointerReleased(PointerEventArgs e)
{
if (!_isSwiping) return;
_isSwiping = false;
// Determine final state
bool shouldOpen = false;
if (Math.Abs(_velocity) > VelocityThreshold)
{
// Use velocity
shouldOpen = (_velocity > 0 && _leftItems.Count > 0) || (_velocity < 0 && _rightItems.Count > 0);
}
else
{
// Use threshold
shouldOpen = Math.Abs(_swipeOffset) > SwipeThreshold;
}
if (shouldOpen)
{
if (_swipeOffset > 0)
{
Open(SwipeDirection.Right);
}
else
{
Open(SwipeDirection.Left);
}
}
else
{
Close();
}
SwipeEnded?.Invoke(this, new SwipeEndedEventArgs(_activeDirection, _isOpen));
_activeDirection = SwipeDirection.None;
base.OnPointerReleased(e);
}
}
/// <summary>
/// Represents a swipe action item.
/// </summary>
public class SwipeItem
{
/// <summary>
/// Gets or sets the text.
/// </summary>
public string Text { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the icon source.
/// </summary>
public string? IconSource { get; set; }
/// <summary>
/// Gets or sets the background color.
/// </summary>
public SKColor BackgroundColor { get; set; } = new SKColor(33, 150, 243);
/// <summary>
/// Gets or sets the text color.
/// </summary>
public SKColor TextColor { get; set; } = SKColors.White;
/// <summary>
/// Event raised when the item is invoked.
/// </summary>
public event EventHandler? Invoked;
internal void OnInvoked()
{
Invoked?.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
/// Swipe direction.
/// </summary>
public enum SwipeDirection
{
None,
Left,
Right,
Up,
Down
}
/// <summary>
/// Swipe mode.
/// </summary>
public enum SwipeMode
{
Reveal,
Execute
}
/// <summary>
/// Event args for swipe started.
/// </summary>
public class SwipeStartedEventArgs : EventArgs
{
public SwipeDirection Direction { get; }
public SwipeStartedEventArgs(SwipeDirection direction)
{
Direction = direction;
}
}
/// <summary>
/// Event args for swipe ended.
/// </summary>
public class SwipeEndedEventArgs : EventArgs
{
public SwipeDirection Direction { get; }
public bool IsOpen { get; }
public SwipeEndedEventArgs(SwipeDirection direction, bool isOpen)
{
Direction = direction;
IsOpen = isOpen;
}
public override void OnPointerReleased(PointerEventArgs e)
{
if (!_isSwiping)
{
return;
}
_isSwiping = false;
bool flag = false;
if ((!(Math.Abs(_velocity) > 500f)) ? (Math.Abs(_swipeOffset) > 60f) : ((_velocity > 0f && _leftItems.Count > 0) || (_velocity < 0f && _rightItems.Count > 0)))
{
if (_swipeOffset > 0f)
{
Open(SwipeDirection.Right);
}
else
{
Open(SwipeDirection.Left);
}
}
else
{
Close();
}
this.SwipeEnded?.Invoke(this, new SwipeEndedEventArgs(_activeDirection, _isOpen));
_activeDirection = SwipeDirection.None;
base.OnPointerReleased(e);
}
}