From e7797eea673ecac0da6463314c43ce5645a96064 Mon Sep 17 00:00:00 2001
From: ErrorCraft <51973682+ErrorCraft@users.noreply.github.com>
Date: Wed, 3 Aug 2022 20:51:10 +0200
Subject: [PATCH 01/67] Add Sector shape
---
src/Avalonia.Base/Utilities/MathUtilities.cs | 35 +++++++++++
src/Avalonia.Controls/Shapes/Sector.cs | 65 ++++++++++++++++++++
2 files changed, 100 insertions(+)
create mode 100644 src/Avalonia.Controls/Shapes/Sector.cs
diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs
index d381979c1e..3c48c3469e 100644
--- a/src/Avalonia.Base/Utilities/MathUtilities.cs
+++ b/src/Avalonia.Base/Utilities/MathUtilities.cs
@@ -324,6 +324,41 @@ namespace Avalonia.Utilities
return angle * 2 * Math.PI;
}
+ ///
+ /// Calculates the point of an angle on an ellipse.
+ ///
+ /// The centre point of the ellipse.
+ /// The x radius of the ellipse.
+ /// The y radius of the ellipse.
+ /// The angle in radians.
+ /// A point on the ellipse.
+ public static Point GetEllipsePoint(Point centre, double radiusX, double radiusY, double angle)
+ {
+ return new Point(radiusX * Math.Cos(angle) + centre.X, radiusY * Math.Sin(angle) + centre.Y);
+ }
+
+ ///
+ /// Gets the minimum and maximum from the specified numbers.
+ ///
+ /// The first number.
+ /// The second number.
+ /// A tuple containing the minimum and maximum of the two specified numbers.
+ public static (double min, double max) GetMinMax(double a, double b)
+ {
+ return a < b ? (a, b) : (b, a);
+ }
+
+ ///
+ /// Gets the minimum and maximum from the specified number and the difference with that number.
+ ///
+ /// The initial value to use.
+ /// The difference for .
+ /// A tuple containing the minimum and maximum of the specified number and the difference with that number.
+ public static (double min, double max) GetMinMaxFromDelta(double initialValue, double delta)
+ {
+ return GetMinMax(initialValue, initialValue + delta);
+ }
+
private static void ThrowCannotBeGreaterThanException(T min, T max)
{
throw new ArgumentException($"{min} cannot be greater than {max}.");
diff --git a/src/Avalonia.Controls/Shapes/Sector.cs b/src/Avalonia.Controls/Shapes/Sector.cs
new file mode 100644
index 0000000000..5d2f6701a7
--- /dev/null
+++ b/src/Avalonia.Controls/Shapes/Sector.cs
@@ -0,0 +1,65 @@
+using System;
+using Avalonia.Media;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls.Shapes
+{
+ public class Sector : Shape
+ {
+ public static readonly StyledProperty StartAngleProperty = AvaloniaProperty.Register(nameof(StartAngle), 0.0d);
+ public static readonly StyledProperty AngleProperty = AvaloniaProperty.Register(nameof(Angle), 0.0d);
+
+ public double StartAngle
+ {
+ get => GetValue(StartAngleProperty);
+ set => SetValue(StartAngleProperty, value);
+ }
+
+ public double Angle
+ {
+ get => GetValue(AngleProperty);
+ set => SetValue(AngleProperty, value);
+ }
+
+ static Sector()
+ {
+ StrokeThicknessProperty.OverrideDefaultValue(1.0d);
+ AffectsGeometry(BoundsProperty, StrokeThicknessProperty, StartAngleProperty, AngleProperty);
+ }
+
+ protected override Geometry? CreateDefiningGeometry()
+ {
+ Rect rect = new Rect(Bounds.Size);
+ Rect deflatedRect = rect.Deflate(StrokeThickness * 0.5d);
+
+ if (Angle >= 360.0d || Angle <= -360.0d)
+ {
+ return new EllipseGeometry(deflatedRect);
+ }
+
+ if (Angle == 0.0d)
+ {
+ return new StreamGeometry();
+ }
+
+ (double startAngle, double endAngle) = MathUtilities.GetMinMaxFromDelta(MathUtilities.Deg2Rad(StartAngle), MathUtilities.Deg2Rad(Angle));
+
+ Point centre = new Point(rect.Width * 0.5d, rect.Height * 0.5d);
+ double radiusX = deflatedRect.Width * 0.5d;
+ double radiusY = deflatedRect.Height * 0.5d;
+ Point startCurvePoint = MathUtilities.GetEllipsePoint(centre, radiusX, radiusY, startAngle);
+ Point endCurvePoint = MathUtilities.GetEllipsePoint(centre, radiusX, radiusY, endAngle);
+ Size size = new Size(radiusX, radiusY);
+
+ StreamGeometry streamGeometry = new StreamGeometry();
+ using StreamGeometryContext streamGeometryContext = streamGeometry.Open();
+
+ streamGeometryContext.BeginFigure(startCurvePoint, false);
+ streamGeometryContext.ArcTo(endCurvePoint, size, 0.0d, Math.Abs(Angle) > 180.0d, SweepDirection.Clockwise);
+ streamGeometryContext.LineTo(centre);
+ streamGeometryContext.EndFigure(true);
+
+ return streamGeometry;
+ }
+ }
+}
From ed582a9fde3aef0a2cd9462cbe66c9dc1446076d Mon Sep 17 00:00:00 2001
From: aldelaro5
Date: Wed, 3 Aug 2022 22:39:40 -0400
Subject: [PATCH 02/67] Implement DataContext update notifications on the
DataGrid
This fixes #5661 by allowing the DataGrid to notify its cells that their DataContext is about to be changed.
---
src/Avalonia.Controls.DataGrid/DataGrid.cs | 32 ++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index d42468f47e..f4cd425c53 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -2167,6 +2167,38 @@ namespace Avalonia.Controls
return desiredSize;
}
+
+ ///
+ protected override void OnDataContextBeginUpdate()
+ {
+ base.OnDataContextBeginUpdate();
+ foreach (DataGridRow row in GetAllRows())
+ {
+ foreach (DataGridCell cell in row.Cells)
+ {
+ if (cell.Content is StyledElement)
+ {
+ DataContextProperty.Notifying?.Invoke((IAvaloniaObject)cell.Content, true);
+ }
+ }
+ }
+ }
+
+ ///
+ protected override void OnDataContextEndUpdate()
+ {
+ base.OnDataContextEndUpdate();
+ foreach (DataGridRow row in GetAllRows())
+ {
+ foreach (DataGridCell cell in row.Cells)
+ {
+ if (cell.Content is StyledElement)
+ {
+ DataContextProperty.Notifying?.Invoke((IAvaloniaObject)cell.Content, false);
+ }
+ }
+ }
+ }
///
/// Raises the BeginningEdit event.
From 8e64d69e8316d09fe7e7c9fc88f2255b7e5acbbd Mon Sep 17 00:00:00 2001
From: Max Katz
Date: Mon, 8 Aug 2022 01:58:45 -0400
Subject: [PATCH 03/67] Fix control catalog crash
---
samples/ControlCatalog/Pages/PointersPage.xaml.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/ControlCatalog/Pages/PointersPage.xaml.cs b/samples/ControlCatalog/Pages/PointersPage.xaml.cs
index 6fc468e37f..e82fcb6226 100644
--- a/samples/ControlCatalog/Pages/PointersPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/PointersPage.xaml.cs
@@ -59,7 +59,7 @@ Position: ??? ???";
e.Pointer.Capture(null);
e.Handled = true;
}
- else
+ else if (e.Pointer.Captured is not null)
{
throw new InvalidOperationException("How?");
}
From 37dab7a565b047ff330fb2534bc531d30b43e3ce Mon Sep 17 00:00:00 2001
From: Max Katz
Date: Mon, 8 Aug 2022 02:18:44 -0400
Subject: [PATCH 04/67] Minor scroll gesture recognizer fixes
---
.../Input/GestureRecognizers/ScrollGestureRecognizer.cs | 3 +--
src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs | 4 ++++
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
index 889b7e3b82..0fb991eaac 100644
--- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
+++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
@@ -66,8 +66,7 @@ namespace Avalonia.Input.GestureRecognizers
public void PointerPressed(PointerPressedEventArgs e)
{
- if (e.Pointer.IsPrimary &&
- (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen))
+ if (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)
{
EndGesture();
_tracking = e.Pointer;
diff --git a/src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs
index 0e4e0ed3e2..b8f6f99ae8 100644
--- a/src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs
+++ b/src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs
@@ -137,9 +137,13 @@ namespace Avalonia.Input.Raw
///
public Point Position { get; set; }
+ ///
public float Twist { get; set; }
+ ///
public float Pressure { get; set; }
+ ///
public float XTilt { get; set; }
+ ///
public float YTilt { get; set; }
From 8716a5111acfd67d07585053d4564870d314f1b0 Mon Sep 17 00:00:00 2001
From: Max Katz
Date: Mon, 8 Aug 2022 03:00:51 -0400
Subject: [PATCH 05/67] Revamp android input handling to support mouse, pen and
historical points
---
.../Platform/SkiaPlatform/TopLevelImpl.cs | 17 +-
.../Helpers/AndroidMotionEventsHelper.cs | 251 ++++++++++++++++++
.../Helpers/AndroidTouchEventsHelper.cs | 85 ------
3 files changed, 264 insertions(+), 89 deletions(-)
create mode 100644 src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
delete mode 100644 src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index dc74214170..17a0d6b63a 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+
using Android.Content;
using Android.Graphics;
using Android.Views;
@@ -30,7 +31,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
private readonly IFramebufferPlatformSurface _framebuffer;
private readonly AndroidKeyboardEventsHelper _keyboardHelper;
- private readonly AndroidTouchEventsHelper _touchHelper;
+ private readonly AndroidMotionEventsHelper _pointerHelper;
private readonly ITextInputMethodImpl _textInputMethod;
private ViewImpl _view;
@@ -39,8 +40,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view = new ViewImpl(avaloniaView.Context, this, placeOnTop);
_textInputMethod = new AndroidInputMethod(_view);
_keyboardHelper = new AndroidKeyboardEventsHelper(this);
- _touchHelper = new AndroidTouchEventsHelper(this, () => InputRoot,
- GetAvaloniaPointFromEvent);
+ _pointerHelper = new AndroidMotionEventsHelper(this);
_gl = GlPlatformSurface.TryCreate(this);
_framebuffer = new FramebufferManager(this);
@@ -160,10 +160,19 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_tl.Draw();
}
+ protected override bool DispatchGenericPointerEvent(MotionEvent e)
+ {
+ bool callBase;
+ bool? result = _tl._pointerHelper.DispatchMotionEvent(e, out callBase);
+ bool baseResult = callBase ? base.DispatchGenericPointerEvent(e) : false;
+
+ return result != null ? result.Value : baseResult;
+ }
+
public override bool DispatchTouchEvent(MotionEvent e)
{
bool callBase;
- bool? result = _tl._touchHelper.DispatchTouchEvent(e, out callBase);
+ bool? result = _tl._pointerHelper.DispatchMotionEvent(e, out callBase);
bool baseResult = callBase ? base.DispatchTouchEvent(e) : false;
return result != null ? result.Value : baseResult;
diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
new file mode 100644
index 0000000000..ce385ebe34
--- /dev/null
+++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
@@ -0,0 +1,251 @@
+using System;
+using System.Collections.Generic;
+
+using Android.Views;
+
+using Avalonia.Android.Platform.SkiaPlatform;
+using Avalonia.Collections.Pooled;
+using Avalonia.Input;
+using Avalonia.Input.Raw;
+
+#nullable enable
+
+namespace Avalonia.Android.Platform.Specific.Helpers
+{
+ internal class AndroidMotionEventsHelper : IDisposable
+ {
+ private static readonly PooledList s_intermediatePointsPooledList = new(ClearMode.Never);
+ private static readonly float s_radiansToDegree = (float)(180f * Math.PI);
+ private readonly TouchDevice _touchDevice;
+ private readonly MouseDevice _mouseDevice;
+ private readonly PenDevice _penDevice;
+ private readonly TopLevelImpl _view;
+ private bool _disposed;
+
+ public AndroidMotionEventsHelper(TopLevelImpl view)
+ {
+ _touchDevice = new TouchDevice();
+ _penDevice = new PenDevice();
+ _mouseDevice = new MouseDevice();
+ _view = view;
+ }
+
+ public bool? DispatchMotionEvent(MotionEvent e, out bool callBase)
+ {
+ callBase = true;
+ if (_disposed)
+ {
+ return null;
+ }
+
+ var eventTime = (ulong)DateTime.Now.Millisecond;
+ var inputRoot = _view.InputRoot;
+ var actionMasked = e.ActionMasked;
+ var modifiers = GetModifiers(e.MetaState, e.ButtonState);
+
+ if (actionMasked == MotionEventActions.Move)
+ {
+ for (int index = 0; index < e.PointerCount; index++)
+ {
+ var toolType = e.GetToolType(index);
+ var device = GetDevice(toolType);
+ var eventType = toolType == MotionEventToolType.Finger ? RawPointerEventType.TouchUpdate : RawPointerEventType.Move;
+ var point = CreatePoint(e, index);
+ modifiers |= GetToolModifiers(toolType);
+
+ // ButtonState reports only mouse buttons, but not touch or stylus pointer.
+ if (toolType != MotionEventToolType.Mouse)
+ {
+ modifiers |= RawInputModifiers.LeftMouseButton;
+ }
+
+ var args = new RawTouchEventArgs(device, eventTime, inputRoot, eventType, point, modifiers, e.GetPointerId(index))
+ {
+ IntermediatePoints = new Lazy?>(() =>
+ {
+ var site = e.HistorySize;
+ s_intermediatePointsPooledList.Clear();
+ s_intermediatePointsPooledList.Capacity = site;
+
+ for (int pos = 0; pos < site; pos++)
+ {
+ s_intermediatePointsPooledList.Add(CreateHistoricalPoint(e, index, pos));
+ }
+
+ return s_intermediatePointsPooledList;
+ })
+ };
+ _view.Input(args);
+ }
+ }
+ else
+ {
+ var index = e.ActionIndex;
+ var toolType = e.GetToolType(index);
+ var device = GetDevice(toolType);
+ modifiers |= GetToolModifiers(toolType);
+ var point = CreatePoint(e, index);
+
+ if (actionMasked == MotionEventActions.Scroll && toolType == MotionEventToolType.Mouse)
+ {
+ var delta = new Vector(e.GetAxisValue(Axis.Hscroll), e.GetAxisValue(Axis.Vscroll));
+ var args = new RawMouseWheelEventArgs(device, eventTime, inputRoot, point.Position, delta, RawInputModifiers.None);
+ _view.Input(args);
+ }
+ else
+ {
+ var eventType = GetActionType(e, actionMasked, toolType);
+ if (eventType >= 0)
+ {
+ var args = new RawTouchEventArgs(device, eventTime, inputRoot, eventType, point, modifiers, e.GetPointerId(index));
+ _view.Input(args);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static RawInputModifiers GetModifiers(MetaKeyStates metaState, MotionEventButtonState buttonState)
+ {
+ var modifiers = RawInputModifiers.None;
+ if (metaState.HasAnyFlag(MetaKeyStates.ShiftOn))
+ {
+ modifiers |= RawInputModifiers.Shift;
+ }
+ if (metaState.HasAnyFlag(MetaKeyStates.CtrlOn))
+ {
+ modifiers |= RawInputModifiers.Control;
+ }
+ if (metaState.HasAnyFlag(MetaKeyStates.AltOn))
+ {
+ modifiers |= RawInputModifiers.Alt;
+ }
+ if (metaState.HasAnyFlag(MetaKeyStates.MetaOn))
+ {
+ modifiers |= RawInputModifiers.Meta;
+ }
+ if (buttonState.HasAnyFlag(MotionEventButtonState.Primary))
+ {
+ modifiers |= RawInputModifiers.LeftMouseButton;
+ }
+ if (buttonState.HasAnyFlag(MotionEventButtonState.Secondary))
+ {
+ modifiers |= RawInputModifiers.RightMouseButton;
+ }
+ if (buttonState.HasAnyFlag(MotionEventButtonState.Tertiary))
+ {
+ modifiers |= RawInputModifiers.MiddleMouseButton;
+ }
+ if (buttonState.HasAnyFlag(MotionEventButtonState.Back))
+ {
+ modifiers |= RawInputModifiers.XButton1MouseButton;
+ }
+ if (buttonState.HasAnyFlag(MotionEventButtonState.Forward))
+ {
+ modifiers |= RawInputModifiers.XButton2MouseButton;
+ }
+ if (buttonState.HasAnyFlag(MotionEventButtonState.StylusPrimary))
+ {
+ modifiers |= RawInputModifiers.PenBarrelButton;
+ }
+ return modifiers;
+ }
+
+#pragma warning disable CA1416 // Validate platform compatibility
+ private static RawPointerEventType GetActionType(MotionEvent e, MotionEventActions actionMasked, MotionEventToolType toolType)
+ {
+ var isTouch = toolType == MotionEventToolType.Finger;
+ var isMouse = toolType == MotionEventToolType.Mouse;
+ switch (actionMasked)
+ {
+ // DOWN
+ case MotionEventActions.Down when !isMouse:
+ case MotionEventActions.PointerDown when !isMouse:
+ return isTouch ? RawPointerEventType.TouchBegin : RawPointerEventType.LeftButtonDown;
+ case MotionEventActions.ButtonPress:
+ return e.ActionButton switch
+ {
+ MotionEventButtonState.Back => RawPointerEventType.XButton1Down,
+ MotionEventButtonState.Forward => RawPointerEventType.XButton2Down,
+ MotionEventButtonState.Primary => RawPointerEventType.LeftButtonDown,
+ MotionEventButtonState.Secondary => RawPointerEventType.RightButtonDown,
+ MotionEventButtonState.StylusPrimary => RawPointerEventType.LeftButtonDown,
+ MotionEventButtonState.StylusSecondary => RawPointerEventType.RightButtonDown,
+ MotionEventButtonState.Tertiary => RawPointerEventType.MiddleButtonDown,
+ _ => RawPointerEventType.LeftButtonDown
+ };
+ // UP
+ case MotionEventActions.Up when !isMouse:
+ case MotionEventActions.PointerUp when !isMouse:
+ return isTouch ? RawPointerEventType.TouchEnd : RawPointerEventType.LeftButtonUp;
+ case MotionEventActions.ButtonRelease:
+ return e.ActionButton switch
+ {
+ MotionEventButtonState.Back => RawPointerEventType.XButton1Up,
+ MotionEventButtonState.Forward => RawPointerEventType.XButton2Up,
+ MotionEventButtonState.Primary => RawPointerEventType.LeftButtonUp,
+ MotionEventButtonState.Secondary => RawPointerEventType.RightButtonUp,
+ MotionEventButtonState.StylusPrimary => RawPointerEventType.LeftButtonUp,
+ MotionEventButtonState.StylusSecondary => RawPointerEventType.RightButtonUp,
+ MotionEventButtonState.Tertiary => RawPointerEventType.MiddleButtonUp,
+ _ => RawPointerEventType.LeftButtonUp
+ };
+ // MOVE
+ case MotionEventActions.Outside:
+ case MotionEventActions.HoverMove:
+ case MotionEventActions.Move:
+ return isTouch ? RawPointerEventType.TouchUpdate : RawPointerEventType.Move;
+ // CANCEL
+ case MotionEventActions.Cancel:
+ return isTouch ? RawPointerEventType.TouchCancel : RawPointerEventType.LeaveWindow;
+ default:
+ return (RawPointerEventType)(-1);
+ }
+ }
+#pragma warning restore CA1416 // Validate platform compatibility
+
+ private IPointerDevice GetDevice(MotionEventToolType type)
+ {
+ return type switch
+ {
+ MotionEventToolType.Mouse => _mouseDevice,
+ MotionEventToolType.Stylus => _penDevice,
+ MotionEventToolType.Eraser => _penDevice,
+ MotionEventToolType.Finger => _touchDevice,
+ _ => _touchDevice
+ };
+ }
+
+ private RawPointerPoint CreatePoint(MotionEvent e, int index)
+ {
+ return new RawPointerPoint
+ {
+ Position = new Point(e.GetX(index), e.GetY(index)) / _view.RenderScaling,
+ Pressure = Math.Min(e.GetPressure(index), 1), // android pressure can depend on the device, can be mixed up with "GetSize", may be larger than 1.0f on some devices
+ Twist = e.GetOrientation(index) * s_radiansToDegree
+ };
+ }
+
+ private RawPointerPoint CreateHistoricalPoint(MotionEvent e, int index, int pos)
+ {
+ return new RawPointerPoint
+ {
+ Position = new Point(e.GetHistoricalX(index, pos), e.GetHistoricalY(index, pos)) / _view.RenderScaling,
+ Pressure = Math.Min(e.GetHistoricalPressure(index, pos), 1),
+ Twist = e.GetHistoricalOrientation(index, pos) * s_radiansToDegree
+ };
+ }
+
+ private static RawInputModifiers GetToolModifiers(MotionEventToolType toolType)
+ {
+ // Android "Eraser" indicates Inverted pen OR actual Eraser. So we have to go both here.
+ return toolType == MotionEventToolType.Eraser ? RawInputModifiers.PenInverted | RawInputModifiers.PenEraser : RawInputModifiers.None;
+ }
+
+ public void Dispose()
+ {
+ _disposed = true;
+ }
+ }
+}
diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs
deleted file mode 100644
index 6142598514..0000000000
--- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System;
-using Android.Views;
-using Avalonia.Input;
-using Avalonia.Input.Raw;
-using Avalonia.Platform;
-
-namespace Avalonia.Android.Platform.Specific.Helpers
-{
- public class AndroidTouchEventsHelper : IDisposable where TView : ITopLevelImpl, IAndroidView
- {
- private TView _view;
- public bool HandleEvents { get; set; }
-
- public AndroidTouchEventsHelper(TView view, Func getInputRoot, Func getPointfunc)
- {
- this._view = view;
- HandleEvents = true;
- _getPointFunc = getPointfunc;
- _getInputRoot = getInputRoot;
- }
-
- private TouchDevice _touchDevice = new TouchDevice();
- private Func _getPointFunc;
- private Func _getInputRoot;
-
- public bool? DispatchTouchEvent(MotionEvent e, out bool callBase)
- {
- if (!HandleEvents)
- {
- callBase = true;
- return null;
- }
-
- var eventTime = DateTime.Now;
-
- //Basic touch support
- var pointerEventType = e.Action switch
- {
- MotionEventActions.Down => RawPointerEventType.TouchBegin,
- MotionEventActions.Up => RawPointerEventType.TouchEnd,
- MotionEventActions.Cancel => RawPointerEventType.TouchCancel,
- _ => RawPointerEventType.TouchUpdate
- };
-
- if (e.Action.HasFlag(MotionEventActions.PointerDown))
- {
- pointerEventType = RawPointerEventType.TouchBegin;
- }
-
- if (e.Action.HasFlag(MotionEventActions.PointerUp))
- {
- pointerEventType = RawPointerEventType.TouchEnd;
- }
-
- for (int i = 0; i < e.PointerCount; i++)
- {
- //if point is in view otherwise it's possible avalonia not to find the proper window to dispatch the event
- var point = _getPointFunc(e, i);
-
- double x = _view.View.GetX();
- double y = _view.View.GetY();
- double r = x + _view.View.Width;
- double b = y + _view.View.Height;
-
- if (x <= point.X && r >= point.X && y <= point.Y && b >= point.Y)
- {
- var inputRoot = _getInputRoot();
-
- var mouseEvent = new RawTouchEventArgs(_touchDevice, (uint)eventTime.Ticks, inputRoot,
- i == e.ActionIndex ? pointerEventType : RawPointerEventType.TouchUpdate, point, RawInputModifiers.None, e.GetPointerId(i));
- _view.Input(mouseEvent);
- }
- }
-
- callBase = true;
- //if return false events for move and up are not received!!!
- return e.Action != MotionEventActions.Up;
- }
-
- public void Dispose()
- {
- HandleEvents = false;
- }
- }
-}
From ee662cecdd0c9b57f986d44e93afa26b5d524b3e Mon Sep 17 00:00:00 2001
From: ili
Date: Mon, 8 Aug 2022 20:13:47 +0500
Subject: [PATCH 06/67] Avoid lock in timers
---
.../AndroidThreadingInterface.cs | 45 +++++++----------
.../InternalPlatformThreadingInterface.cs | 4 +-
.../HeadlessPlatformThreadingInterface.cs | 48 +++++++++----------
3 files changed, 42 insertions(+), 55 deletions(-)
diff --git a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs
index 42f75a27e1..de9149e9a1 100644
--- a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs
+++ b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs
@@ -27,46 +27,33 @@ namespace Avalonia.Android
{
if (interval.TotalMilliseconds < 10)
interval = TimeSpan.FromMilliseconds(10);
- object l = new object();
+
var stopped = false;
Timer timer = null;
- var scheduled = false;
timer = new Timer(_ =>
{
- lock (l)
+ if (stopped)
+ return;
+
+ EnsureInvokeOnMainThread(() =>
{
- if (stopped)
+ try
{
- timer.Dispose();
- return;
+ tick();
}
- if (scheduled)
- return;
- scheduled = true;
- EnsureInvokeOnMainThread(() =>
+ finally
{
- try
- {
- tick();
- }
- finally
- {
- lock (l)
- {
- scheduled = false;
- }
- }
- });
- }
- }, null, TimeSpan.Zero, interval);
+ if (!stopped)
+ timer.Change(interval, Timeout.InfiniteTimeSpan);
+ }
+ });
+ },
+ null, interval, Timeout.InfiniteTimeSpan);
return Disposable.Create(() =>
{
- lock (l)
- {
- stopped = true;
- timer.Dispose();
- }
+ stopped = true;
+ timer.Dispose();
});
}
diff --git a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
index 630d2d8efb..e1f6db9c60 100644
--- a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
+++ b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
@@ -43,7 +43,7 @@ namespace Avalonia.Controls.Platform
_priority = priority;
_interval = interval;
_tick = tick;
- _timer = new Timer(OnTimer, null, interval, TimeSpan.FromMilliseconds(-1));
+ _timer = new Timer(OnTimer, null, interval, Timeout.InfiniteTimeSpan);
_handle = GCHandle.Alloc(_timer);
}
@@ -57,7 +57,7 @@ namespace Avalonia.Controls.Platform
if (_timer == null)
return;
_tick();
- _timer?.Change(_interval, TimeSpan.FromMilliseconds(-1));
+ _timer?.Change(_interval, Timeout.InfiniteTimeSpan);
});
}
diff --git a/src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs b/src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs
index e42a7b1a71..b233b46dd0 100644
--- a/src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs
+++ b/src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs
@@ -36,35 +36,35 @@ namespace Avalonia.Headless
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{
- var cancelled = false;
- var enqueued = false;
- var l = new object();
- var timer = new Timer(_ =>
+ if (interval.TotalMilliseconds < 10)
+ interval = TimeSpan.FromMilliseconds(10);
+
+ var stopped = false;
+ Timer timer = null;
+ timer = new Timer(_ =>
{
- lock (l)
+ if (stopped)
+ return;
+
+ Dispatcher.UIThread.Post(() =>
{
- if (cancelled || enqueued)
- return;
- enqueued = true;
- Dispatcher.UIThread.Post(() =>
+ try
{
- lock (l)
- {
- enqueued = false;
- if (cancelled)
- return;
- tick();
- }
- }, priority);
- }
- }, null, interval, interval);
+ tick();
+ }
+ finally
+ {
+ if (!stopped)
+ timer.Change(interval, Timeout.InfiniteTimeSpan);
+ }
+ });
+ },
+ null, interval, Timeout.InfiniteTimeSpan);
+
return Disposable.Create(() =>
{
- lock (l)
- {
- timer.Dispose();
- cancelled = true;
- }
+ stopped = true;
+ timer.Dispose();
});
}
From 26e401a36cf3c14897c182a849b3cc79854041ea Mon Sep 17 00:00:00 2001
From: Max Katz
Date: Tue, 9 Aug 2022 02:41:50 -0400
Subject: [PATCH 07/67] Revert ScrollGestureRecognizer changes for now
---
.../Input/GestureRecognizers/ScrollGestureRecognizer.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
index 0fb991eaac..889b7e3b82 100644
--- a/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
+++ b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
@@ -66,7 +66,8 @@ namespace Avalonia.Input.GestureRecognizers
public void PointerPressed(PointerPressedEventArgs e)
{
- if (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)
+ if (e.Pointer.IsPrimary &&
+ (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen))
{
EndGesture();
_tracking = e.Pointer;
From 2c39f80f9bab22fbdb66d41db33543a84f556ba4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Pedro?=
Date: Tue, 9 Aug 2022 17:43:12 +0100
Subject: [PATCH 08/67] Changed Skia OpenGL bitmap origin to bottom left.
---
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs | 2 +-
src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
index f37e0f8701..063ad50b0b 100644
--- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
@@ -345,7 +345,7 @@ namespace ControlCatalog.Pages
0.01f, 1000);
- var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0));
+ var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, 1, 0));
var model = Matrix4x4.CreateFromYawPitchRoll(_yaw, _pitch, _roll);
var modelLoc = GL.GetUniformLocationString(_shaderProgram, "uModel");
var viewLoc = GL.GetUniformLocationString(_shaderProgram, "uView");
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
index a4617bb4d5..fb69a1ad3d 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
@@ -41,7 +41,7 @@ namespace Avalonia.Skia
new GRGlTextureInfo(
GlConsts.GL_TEXTURE_2D, (uint)_surface.GetTextureId(),
(uint)_surface.InternalFormat)))
- using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.TopLeft,
+ using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.BottomLeft,
SKColorType.Rgba8888))
{
// Again, silently ignore, if something went wrong it's not our fault
From f63ed9cf6bf19b8490c55d2f9bf6cc6b9ae05fb3 Mon Sep 17 00:00:00 2001
From: Giuseppe Lippolis
Date: Wed, 10 Aug 2022 10:33:00 +0200
Subject: [PATCH 09/67] fix: Null Annotation
---
samples/ControlCatalog/MainView.xaml.cs | 6 +-
samples/ControlCatalog/Models/Person.cs | 8 +--
.../Pages/AutoCompleteBoxPage.xaml.cs | 24 +++----
.../Pages/ButtonSpinnerPage.xaml.cs | 25 ++++---
.../ControlCatalog/Pages/ButtonsPage.xaml.cs | 2 +-
.../ControlCatalog/Pages/CarouselPage.xaml.cs | 2 +-
.../Pages/ClipboardPage.xaml.cs | 70 +++++++++++++------
.../ControlCatalog/Pages/ComboBoxPage.xaml.cs | 2 +-
.../Pages/CompositionPage.axaml.cs | 20 +++---
.../Pages/ContextFlyoutPage.xaml.cs | 22 +++---
.../Pages/ContextMenuPage.xaml.cs | 23 +++---
.../ControlCatalog/Pages/DataGridPage.xaml.cs | 4 +-
.../ControlCatalog/Pages/DialogsPage.xaml.cs | 13 +++-
.../Pages/DragAndDropPage.xaml.cs | 20 +++---
.../ControlCatalog/Pages/FlyoutsPage.axaml.cs | 2 +-
.../Pages/ItemsRepeaterPage.xaml.cs | 10 +--
.../Pages/NumericUpDownPage.xaml.cs | 6 +-
.../ControlCatalog/Pages/OpenGlPage.xaml.cs | 4 +-
.../ControlCatalog/Pages/PointersPage.xaml.cs | 31 ++++----
...ransitioningContentControlPageViewModel.cs | 6 +-
20 files changed, 165 insertions(+), 135 deletions(-)
diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs
index 58433f13ce..7133ddaa6a 100644
--- a/samples/ControlCatalog/MainView.xaml.cs
+++ b/samples/ControlCatalog/MainView.xaml.cs
@@ -22,8 +22,8 @@ namespace ControlCatalog
if (AvaloniaLocator.Current?.GetService()?.GetRuntimeInfo().IsDesktop == true)
{
- IList tabItems = ((IList)sideBar.Items);
- tabItems.Add(new TabItem()
+ var tabItems = (sideBar.Items as IList);
+ tabItems?.Add(new TabItem()
{
Header = "Screens",
Content = new ScreenPage()
@@ -36,7 +36,7 @@ namespace ControlCatalog
{
if (themes.SelectedItem is CatalogTheme theme)
{
- var themeStyle = Application.Current.Styles[0];
+ var themeStyle = Application.Current!.Styles[0];
if (theme == CatalogTheme.FluentLight)
{
if (App.Fluent.Mode != FluentThemeMode.Light)
diff --git a/samples/ControlCatalog/Models/Person.cs b/samples/ControlCatalog/Models/Person.cs
index 2dfa02c7ed..99bc50250b 100644
--- a/samples/ControlCatalog/Models/Person.cs
+++ b/samples/ControlCatalog/Models/Person.cs
@@ -85,7 +85,7 @@ namespace ControlCatalog.Models
}
else
{
- if (_errorLookup.TryGetValue(propertyName, out List errorList))
+ if (_errorLookup.TryGetValue(propertyName, out var errorList))
{
errorList.Clear();
errorList.Add(error!);
@@ -114,12 +114,12 @@ namespace ControlCatalog.Models
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
- public IEnumerable? GetErrors(string propertyName)
+ public IEnumerable GetErrors(string? propertyName)
{
- if (_errorLookup.TryGetValue(propertyName, out List errorList))
+ if (propertyName is { } && _errorLookup.TryGetValue(propertyName, out var errorList))
return errorList;
else
- return null;
+ return Array.Empty