diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index b28d445443..9fb8f4026d 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -420,6 +420,17 @@ namespace Avalonia.Automation.Peers [PrivateApi] public AutomationPeer? GetAutomationRoot() => GetAutomationRootCore(); + /// + /// Converts a rectangle in the peer's coordinate space, as returned by + /// , to screen coordinates; returns null if the peer is + /// not currently hosted in a rendered visual tree. + /// + /// + /// A rectangle in the peer's own coordinate space, as returned by + /// . + /// + public Rect? ToScreen(Rect rect) => ToScreenCore(rect); + /// /// Gets a value that indicates whether the element that is associated with this automation /// peer currently has keyboard focus. @@ -669,6 +680,8 @@ namespace Avalonia.Automation.Peers private protected virtual AutomationPeer? GetVisualRootCore() => GetAutomationRootCore(); + private protected virtual Rect? ToScreenCore(Rect rect) => null; + protected virtual bool IsContentElementOverrideCore() { diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs index c4128465b7..24f143f408 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -155,6 +155,13 @@ namespace Avalonia.Automation.Peers return null; } + private protected override Rect? ToScreenCore(Rect rect) + { + if (Owner?.PresentationSource?.RootVisual is not { } root) + return null; + return new PixelRect(root.PointToScreen(rect.TopLeft), root.PointToScreen(rect.BottomRight)).ToRect(1); + } + /// /// Invalidates the peer's children and causes a re-read from . /// diff --git a/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs index 1c0236e8c0..d5b0a84179 100644 --- a/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs +++ b/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs @@ -11,7 +11,6 @@ using Avalonia.Automation; using Avalonia.Automation.Peers; using Avalonia.Controls.Automation.Peers; using Avalonia.Threading; -using Avalonia.VisualTree; using Avalonia.Win32.Automation.Interop; using AAP = Avalonia.Automation.Provider; using UIA = Avalonia.Win32.Automation.Interop; @@ -76,18 +75,7 @@ namespace Avalonia.Win32.Automation public virtual Rect GetBoundingRectangle() { - return InvokeSync(() => - { - if (Peer.GetVisualRoot() is ControlAutomationPeer root && - root.Owner.GetPresentationSource() is not null) - { - var originalRect = Peer.GetBoundingRectangle(); - return new PixelRect(root.Owner.PointToScreen(originalRect.TopLeft), - root.Owner.PointToScreen(originalRect.BottomRight)).ToRect(1); - } - - return default; - }); + return InvokeSync(() => Peer.ToScreen(Peer.GetBoundingRectangle()) ?? default); } public virtual IRawElementProviderFragmentRoot? GetFragmentRoot() diff --git a/tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs b/tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs index 7ba9ff4fdb..41aa697237 100644 --- a/tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs @@ -232,6 +232,31 @@ namespace Avalonia.Controls.UnitTests.Automation } } + public class ToScreen : ScopedTestBase + { + [Fact] + public void Converts_Rect_When_Attached_To_Rooted_Tree() + { + var border = new Border(); + var root = new TestRoot(border); + var peer = CreatePeer(border); + + Assert.Equal(new Rect(10, 20, 30, 40), peer.ToScreen(new Rect(10, 20, 30, 40))); + } + + [Fact] + public void Returns_Null_When_Detached() + { + var border = new Border(); + var root = new TestRoot(border); + var peer = CreatePeer(border); + + root.Child = null; + + Assert.Null(peer.ToScreen(new Rect(10, 20, 30, 40))); + } + } + private static AutomationPeer CreatePeer(Control control) { return ControlAutomationPeer.CreatePeerForElement(control);