Browse Source

Merge branch 'master' into feat/CalendarWeekNumbers

Tim 5 months ago
committed by GitHub
parent
commit
4b512512e1
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[] var projection = new[]
{ {
MediaStore.IMediaColumns.Size, MediaStore.IMediaColumns.DateAdded, Document.ColumnSize, Document.ColumnLastModified
MediaStore.IMediaColumns.DateModified
}; };
using var cursor = Activity.ContentResolver!.Query(Uri, projection, null, null, null); using var cursor = Activity.ContentResolver!.Query(Uri, projection, null, null, null);
@ -555,7 +554,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
{ {
try try
{ {
var columnIndex = cursor.GetColumnIndex(MediaStore.IMediaColumns.Size); var columnIndex = cursor.GetColumnIndex(Document.ColumnSize);
if (columnIndex != -1) if (columnIndex != -1)
{ {
size = (ulong)cursor.GetLong(columnIndex); size = (ulong)cursor.GetLong(columnIndex);
@ -569,22 +568,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
try try
{ {
var columnIndex = cursor.GetColumnIndex(MediaStore.IMediaColumns.DateAdded); var columnIndex = cursor.GetColumnIndex(Document.ColumnLastModified);
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);
if (columnIndex != -1) if (columnIndex != -1)
{ {
var longValue = cursor.GetLong(columnIndex); var longValue = cursor.GetLong(columnIndex);
@ -594,7 +578,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
catch (Exception ex) catch (Exception ex)
{ {
Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)? 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.Input.Raw;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.VisualTree; using Avalonia.Rendering;
namespace Avalonia.Input namespace Avalonia.Input
{ {
public class PointerEventArgs : RoutedEventArgs, IKeyModifiersEventArgs public class PointerEventArgs : RoutedEventArgs, IKeyModifiersEventArgs
{ {
private readonly Visual? _rootVisual; private readonly IPresentationSource? _eventPresentationSource; // the original observer of the event
private readonly Point _rootVisualPosition; private readonly Point _presentationSourcePosition;
private readonly PointerPointProperties _properties; private readonly PointerPointProperties _properties;
private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints; private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
@ -24,8 +24,8 @@ namespace Avalonia.Input
: base(routedEvent) : base(routedEvent)
{ {
Source = source; Source = source;
_rootVisual = rootVisual; _eventPresentationSource = rootVisual?.PresentationSource;
_rootVisualPosition = rootVisualPosition; _presentationSourcePosition = rootVisualPosition;
_properties = properties; _properties = properties;
Pointer = pointer; Pointer = pointer;
Timestamp = timestamp; Timestamp = timestamp;
@ -76,23 +76,25 @@ namespace Avalonia.Input
private Point GetPosition(Point pt, Visual? relativeTo) private Point GetPosition(Point pt, Visual? relativeTo)
{ {
if (_rootVisual == null)
return default;
if (relativeTo == null) if (relativeTo == null)
return pt; 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 if (relativeTo.PresentationSource != _eventPresentationSource)
// (i.e. they called it for a control inside a popup.
if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot) && relativeTo.VisualRoot is { })
{ {
// Convert to absolute screen coordinates. if (_eventPresentationSource.PointToScreen(pt) is { } screenPt &&
var screenPt = _rootVisual.PointToScreen(pt); relativeTo.PresentationSource.PointToClient(screenPt) is { } targetClientPt)
{
// Convert to client co-ordinates of the visual inside the other visual root. pt = targetClientPt;
return relativeTo.PointToClient(screenPt); }
else
{
return default;
}
} }
return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; return relativeTo.PresentationSource.RootVisual.TranslatePoint(pt, relativeTo) ?? default;
} }
/// <summary> /// <summary>
@ -100,7 +102,7 @@ namespace Avalonia.Input
/// </summary> /// </summary>
/// <param name="relativeTo">The visual whose coordinate system to use. Pass null for toplevel coordinate system</param> /// <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> /// <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> /// <summary>
/// Returns the PointerPoint associated with the current event /// Returns the PointerPoint associated with the current event
@ -117,7 +119,7 @@ namespace Avalonia.Input
/// <returns></returns> /// <returns></returns>
public IReadOnlyList<PointerPoint> GetIntermediatePoints(Visual? relativeTo) public IReadOnlyList<PointerPoint> GetIntermediatePoints(Visual? relativeTo)
{ {
var previousPoints = _previousPoints?.Value; var previousPoints = _previousPoints?.Value;
if (previousPoints == null || previousPoints.Count == 0) if (previousPoints == null || previousPoints.Count == 0)
return new[] { GetCurrentPoint(relativeTo) }; return new[] { GetCurrentPoint(relativeTo) };
var points = new PointerPoint[previousPoints.Count + 1]; var points = new PointerPoint[previousPoints.Count + 1];
@ -145,7 +147,7 @@ namespace Avalonia.Input
/// </summary> /// </summary>
public PointerPointProperties Properties => _properties; public PointerPointProperties Properties => _properties;
} }
public enum MouseButton public enum MouseButton
{ {
None, None,

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

@ -41,6 +41,6 @@ public interface IPresentationSource
/// </summary> /// </summary>
internal Size ClientSize { get; } internal Size ClientSize { get; }
internal PixelPoint PointToScreen(Point point); internal PixelPoint? PointToScreen(Point point);
internal Point PointToClient(PixelPoint 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="visual">The visual.</param>
/// <param name="point">The point in screen coordinates.</param> /// <param name="point">The point in screen coordinates.</param>
/// <returns>The point in client coordinates.</returns> /// <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) public static Point PointToClient(this Visual visual, PixelPoint point)
{ {
var source = visual.PresentationSource; var source = visual.PresentationSource;
var root = source?.RootVisual ?? 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 rootPoint = source.PointToClient(point); var rootPoint = source.PointToClient(point) ?? default;
return root.TranslatePoint(rootPoint, visual)!.Value; return root.TranslatePoint(rootPoint, visual)!.Value;
} }
@ -29,13 +30,14 @@ namespace Avalonia
/// <param name="visual">The visual.</param> /// <param name="visual">The visual.</param>
/// <param name="point">The point in client coordinates.</param> /// <param name="point">The point in client coordinates.</param>
/// <returns>The point in screen coordinates.</returns> /// <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) public static PixelPoint PointToScreen(this Visual visual, Point point)
{ {
var source = visual.PresentationSource; var source = visual.PresentationSource;
var root = source?.RootVisual ?? 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); var p = visual.TranslatePoint(point, root);
return source.PointToScreen(p!.Value); return source.PointToScreen(p!.Value) ?? default;
} }
/// <summary> /// <summary>

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

@ -23,9 +23,9 @@ internal partial class PresentationSource
_pointerOverPreProcessor?.SceneInvalidated(sceneInvalidatedEventArgs.DirtyRect); _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) private void HandleScalingChanged(double scaling)
=> RenderScaling = LayoutHelper.ValidateScaling(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;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Media; using Avalonia.Media;
@ -128,6 +130,74 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal(new Point(1, 11), result); 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] [Fact]
public void Mouse_Pointer_Should_Set_Focus_On_Pointer_Pressed() 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 IFocusManager? FocusManager { get; }
public IPlatformSettings? PlatformSettings { 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() public void RegisterChildrenNames()

Loading…
Cancel
Save