Browse Source

Merge branch 'master' into dynamic-aabb-tree

pull/21310/head
Steve 5 months ago
committed by GitHub
parent
commit
3a52b69ded
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 24
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  2. 40
      src/Avalonia.Base/Input/PointerEventArgs.cs
  3. 4
      src/Avalonia.Base/Rendering/IPresentationSource.cs
  4. 10
      src/Avalonia.Base/VisualExtensions.cs
  5. 4
      src/Avalonia.Controls/PresentationSource/PresentationSource.RenderRoot.cs
  6. 72
      tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs
  7. 4
      tests/Avalonia.RenderTests/TestRenderRoot.cs
  8. 4
      tests/Avalonia.UnitTests/TestRoot.cs

24
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs

@ -546,8 +546,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
{
var projection = new[]
{
MediaStore.IMediaColumns.Size, MediaStore.IMediaColumns.DateAdded,
MediaStore.IMediaColumns.DateModified
Document.ColumnSize, Document.ColumnLastModified
};
using var cursor = Activity.ContentResolver!.Query(Uri, projection, null, null, null);
@ -555,7 +554,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
{
try
{
var columnIndex = cursor.GetColumnIndex(MediaStore.IMediaColumns.Size);
var columnIndex = cursor.GetColumnIndex(Document.ColumnSize);
if (columnIndex != -1)
{
size = (ulong)cursor.GetLong(columnIndex);
@ -569,22 +568,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
try
{
var columnIndex = cursor.GetColumnIndex(MediaStore.IMediaColumns.DateAdded);
if (columnIndex != -1)
{
var longValue = cursor.GetLong(columnIndex);
itemDate = longValue > 0 ? DateTimeOffset.FromUnixTimeMilliseconds(longValue) : null;
}
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)?
.Log(this, "File DateAdded metadata reader failed: '{Exception}'", ex);
}
try
{
var columnIndex = cursor.GetColumnIndex(MediaStore.IMediaColumns.DateModified);
var columnIndex = cursor.GetColumnIndex(Document.ColumnLastModified);
if (columnIndex != -1)
{
var longValue = cursor.GetLong(columnIndex);
@ -594,7 +578,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)?
.Log(this, "File DateAdded metadata reader failed: '{Exception}'", ex);
.Log(this, "File LastModified metadata reader failed: '{Exception}'", ex);
}
}
}

40
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -3,14 +3,14 @@ using System.Collections.Generic;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.Metadata;
using Avalonia.VisualTree;
using Avalonia.Rendering;
namespace Avalonia.Input
{
public class PointerEventArgs : RoutedEventArgs, IKeyModifiersEventArgs
{
private readonly Visual? _rootVisual;
private readonly Point _rootVisualPosition;
private readonly IPresentationSource? _eventPresentationSource; // the original observer of the event
private readonly Point _presentationSourcePosition;
private readonly PointerPointProperties _properties;
private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
@ -24,8 +24,8 @@ namespace Avalonia.Input
: base(routedEvent)
{
Source = source;
_rootVisual = rootVisual;
_rootVisualPosition = rootVisualPosition;
_eventPresentationSource = rootVisual?.PresentationSource;
_presentationSourcePosition = rootVisualPosition;
_properties = properties;
Pointer = pointer;
Timestamp = timestamp;
@ -76,23 +76,25 @@ namespace Avalonia.Input
private Point GetPosition(Point pt, Visual? relativeTo)
{
if (_rootVisual == null)
return default;
if (relativeTo == null)
return pt;
if (_eventPresentationSource == null || relativeTo?.PresentationSource?.RootVisual == null)
return default;
// If the visual the user passed in, is not connected to the same visual root
// (i.e. they called it for a control inside a popup.
if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot) && relativeTo.VisualRoot is { })
if (relativeTo.PresentationSource != _eventPresentationSource)
{
// Convert to absolute screen coordinates.
var screenPt = _rootVisual.PointToScreen(pt);
// Convert to client co-ordinates of the visual inside the other visual root.
return relativeTo.PointToClient(screenPt);
if (_eventPresentationSource.PointToScreen(pt) is { } screenPt &&
relativeTo.PresentationSource.PointToClient(screenPt) is { } targetClientPt)
{
pt = targetClientPt;
}
else
{
return default;
}
}
return pt * _rootVisual.TransformToVisual(relativeTo) ?? default;
return relativeTo.PresentationSource.RootVisual.TranslatePoint(pt, relativeTo) ?? default;
}
/// <summary>
@ -100,7 +102,7 @@ namespace Avalonia.Input
/// </summary>
/// <param name="relativeTo">The visual whose coordinate system to use. Pass null for toplevel coordinate system</param>
/// <returns>The pointer position in the control's coordinates.</returns>
public Point GetPosition(Visual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo);
public Point GetPosition(Visual? relativeTo) => GetPosition(_presentationSourcePosition, relativeTo);
/// <summary>
/// Returns the PointerPoint associated with the current event
@ -117,7 +119,7 @@ namespace Avalonia.Input
/// <returns></returns>
public IReadOnlyList<PointerPoint> GetIntermediatePoints(Visual? relativeTo)
{
var previousPoints = _previousPoints?.Value;
var previousPoints = _previousPoints?.Value;
if (previousPoints == null || previousPoints.Count == 0)
return new[] { GetCurrentPoint(relativeTo) };
var points = new PointerPoint[previousPoints.Count + 1];
@ -145,7 +147,7 @@ namespace Avalonia.Input
/// </summary>
public PointerPointProperties Properties => _properties;
}
public enum MouseButton
{
None,

4
src/Avalonia.Base/Rendering/IPresentationSource.cs

@ -41,6 +41,6 @@ public interface IPresentationSource
/// </summary>
internal Size ClientSize { get; }
internal PixelPoint PointToScreen(Point point);
internal Point PointToClient(PixelPoint point);
internal PixelPoint? PointToScreen(Point point);
internal Point? PointToClient(PixelPoint point);
}

10
src/Avalonia.Base/VisualExtensions.cs

@ -14,12 +14,13 @@ namespace Avalonia
/// <param name="visual">The visual.</param>
/// <param name="point">The point in screen coordinates.</param>
/// <returns>The point in client coordinates.</returns>
/// <exception cref="ArgumentException">Thown when <paramref name="visual"/> does not belong to a visual tree.</exception>
public static Point PointToClient(this Visual visual, PixelPoint point)
{
var source = visual.PresentationSource;
var root = source?.RootVisual ??
throw new ArgumentException("Control does not belong to a visual tree.", nameof(visual));
var rootPoint = source.PointToClient(point);
throw new ArgumentException("Visual does not belong to a visual tree.", nameof(visual));
var rootPoint = source.PointToClient(point) ?? default;
return root.TranslatePoint(rootPoint, visual)!.Value;
}
@ -29,13 +30,14 @@ namespace Avalonia
/// <param name="visual">The visual.</param>
/// <param name="point">The point in client coordinates.</param>
/// <returns>The point in screen coordinates.</returns>
/// <exception cref="ArgumentException">Thown when <paramref name="visual"/> does not belong to a visual tree.</exception>
public static PixelPoint PointToScreen(this Visual visual, Point point)
{
var source = visual.PresentationSource;
var root = source?.RootVisual ??
throw new ArgumentException("Control does not belong to a visual tree.", nameof(visual));
throw new ArgumentException("Visual does not belong to a visual tree.", nameof(visual));
var p = visual.TranslatePoint(point, root);
return source.PointToScreen(p!.Value);
return source.PointToScreen(p!.Value) ?? default;
}
/// <summary>

4
src/Avalonia.Controls/PresentationSource/PresentationSource.RenderRoot.cs

@ -23,9 +23,9 @@ internal partial class PresentationSource
_pointerOverPreProcessor?.SceneInvalidated(sceneInvalidatedEventArgs.DirtyRect);
}
public PixelPoint PointToScreen(Point point) => PlatformImpl?.PointToScreen(point) ?? default;
public PixelPoint? PointToScreen(Point point) => PlatformImpl?.PointToScreen(point);
public Point PointToClient(PixelPoint point) => PlatformImpl?.PointToClient(point) ?? default;
public Point? PointToClient(PixelPoint point) => PlatformImpl?.PointToClient(point);
private void HandleScalingChanged(double scaling)
=> RenderScaling = LayoutHelper.ValidateScaling(scaling);

72
tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs

@ -1,4 +1,6 @@
using Avalonia.Controls;
using System;
using Avalonia.Controls;
using Avalonia.Headless;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Media;
@ -128,6 +130,74 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal(new Point(1, 11), result);
}
private IDisposable SetupCrossTreePositionRequest(PixelPoint topLevelPosition, out PointerEventArgs pointerEvent, out Control elementA, out Control elementB)
{
var app = UnitTestApplication.Start(new TestServices(
inputManager: new InputManager(),
renderInterface: new HeadlessPlatformRenderInterface()));
var renderer = new Mock<IHitTester>();
var deviceMock = CreatePointerDeviceMock();
var impl1 = CreateTopLevelImplMock();
// Mocked position: topLevelPosition
impl1.Setup(w => w.PointToScreen(default)).Returns<Point>(p => (PixelPoint.FromPoint(p, 1) + topLevelPosition));
elementA = new Border();
PointerEventArgs? moveEventArgs = null;
elementA.PointerMoved += (s, e) => moveEventArgs = e;
var root1 = CreateInputRoot(impl1.Object, elementA, renderer.Object);
SetMove(deviceMock, root1.InputRoot, elementA);
impl1.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root1));
Assert.NotNull(moveEventArgs);
pointerEvent = moveEventArgs;
var impl2 = CreateTopLevelImplMock();
// Mocked position: topLevelPosition * 2
impl2.Setup(w => w.PointToClient(default)).Returns<PixelPoint>(p => (p - topLevelPosition - topLevelPosition).ToPoint(1));
elementB = new Border();
var root2 = CreateInputRoot(impl2.Object, elementB, renderer.Object);
return app;
}
[Fact]
public void GetPosition_Should_Support_Cross_Tree_Requests()
{
var topLevelOffset = new PixelPoint(5, 0);
using (SetupCrossTreePositionRequest(topLevelOffset, out var pointerEvent, out _, out var elementB))
{
Assert.Equal(topLevelOffset.ToPoint(1), pointerEvent.GetPosition(elementB));
}
}
[Fact]
public void GetPosition_Should_Return_Default_When_Cross_Tree_Source_Closed()
{
var topLevelOffset = new PixelPoint(5, 0);
using (SetupCrossTreePositionRequest(topLevelOffset, out var pointerEvent, out var elementA, out var elementB))
{
((PresentationSource)elementA.PresentationSource!).Dispose();
Assert.Equal(default, pointerEvent.GetPosition(elementB));
}
}
[Fact]
public void GetPosition_Should_Return_Default_When_Cross_Tree_Target_Closed()
{
var topLevelOffset = new PixelPoint(5, 0);
using (SetupCrossTreePositionRequest(topLevelOffset, out var pointerEvent, out _, out var elementB))
{
((PresentationSource)elementB.PresentationSource!).Dispose();
Assert.Equal(default, pointerEvent.GetPosition(elementB));
}
}
[Fact]
public void Mouse_Pointer_Should_Set_Focus_On_Pointer_Pressed()
{

4
tests/Avalonia.RenderTests/TestRenderRoot.cs

@ -67,9 +67,9 @@ namespace Avalonia.Skia.RenderTests
{
}
public Point PointToClient(PixelPoint point) => point.ToPoint(RenderScaling);
public Point? PointToClient(PixelPoint point) => point.ToPoint(RenderScaling);
public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
public PixelPoint? PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
public IFocusManager? FocusManager { get; }
public IPlatformSettings? PlatformSettings { get; }

4
tests/Avalonia.UnitTests/TestRoot.cs

@ -113,9 +113,9 @@ namespace Avalonia.UnitTests
{
}
public Point PointToClient(PixelPoint p) => p.ToPoint(1);
public Point? PointToClient(PixelPoint p) => p.ToPoint(1);
public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
public PixelPoint? PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
public void RegisterChildrenNames()

Loading…
Cancel
Save