Browse Source
Fix empty automation bounds for custom root providers (#21730 )
* Add AutomationPeer.ToScreen so each peer converts its own bounding rectangle to screen coordinates, fixing empty UIA bounds for root providers that are not ControlAutomationPeers.
* Update AutomationPeer.cs
* Update AutomationPeer.cs
pull/21736/head
Jumar Macato
3 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with
46 additions and
13 deletions
src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
src/Windows/Avalonia.Win32.Automation/AutomationNode.cs
tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs
@ -420,6 +420,17 @@ namespace Avalonia.Automation.Peers
[PrivateApi]
public AutomationPeer ? GetAutomationRoot ( ) = > GetAutomationRootCore ( ) ;
/// <summary>
/// Converts a rectangle in the peer's coordinate space, as returned by
/// <see cref="GetBoundingRectangle"/>, to screen coordinates; returns null if the peer is
/// not currently hosted in a rendered visual tree.
/// </summary>
/// <param name="rect">
/// A rectangle in the peer's own coordinate space, as returned by
/// <see cref="GetBoundingRectangle"/>.
/// </param>
public Rect ? ToScreen ( Rect rect ) = > ToScreenCore ( rect ) ;
/// <summary>
/// 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 ( )
{
@ -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 ) ;
}
/// <summary>
/// Invalidates the peer's children and causes a re-read from <see cref="GetChildrenCore"/>.
/// </summary>
@ -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 ( )
@ -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 ( 1 0 , 2 0 , 3 0 , 4 0 ) , peer . ToScreen ( new Rect ( 1 0 , 2 0 , 3 0 , 4 0 ) ) ) ;
}
[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 ( 1 0 , 2 0 , 3 0 , 4 0 ) ) ) ;
}
}
private static AutomationPeer CreatePeer ( Control control )
{
return ControlAutomationPeer . CreatePeerForElement ( control ) ;