Skip to content

Commit

Permalink
Allow an AutomationPeer to override its visual root.
Browse files Browse the repository at this point in the history
This is needed for example when a UI framework hosts a peer in the automation tree of a main window whose control is actually hosted in a popup. It allows the bounding rectangle to be calculated correctly in that case.

s
  • Loading branch information
grokys committed Jul 27, 2023
1 parent eb1f784 commit c1645ca
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 14 deletions.
23 changes: 22 additions & 1 deletion src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,14 @@ public abstract class AutomationPeer
/// <summary>
/// Gets the <see cref="AutomationPeer"/> that is the parent of this <see cref="AutomationPeer"/>.
/// </summary>
/// <returns></returns>
public AutomationPeer? GetParent() => GetParentCore();

/// <summary>
/// Gets the <see cref="AutomationPeer"/> that is the root of this <see cref="AutomationPeer"/>'s
/// visual tree.
/// </summary>
public AutomationPeer? GetVisualRoot() => GetVisualRootCore();

/// <summary>
/// Gets a value that indicates whether the element that is associated with this automation
/// peer currently has keyboard focus.
Expand Down Expand Up @@ -247,6 +252,22 @@ protected virtual AutomationControlType GetControlTypeOverrideCore()
return GetAutomationControlTypeCore();
}

protected virtual AutomationPeer? GetVisualRootCore()
{
var parent = GetParent();

while (parent != null)
{
var nextParent = parent.GetParent();
if (nextParent == null)
return parent;
parent = nextParent;
}

return null;
}


protected virtual bool IsContentElementOverrideCore()
{
return IsControlElement() && IsContentElementCore();
Expand Down
16 changes: 3 additions & 13 deletions src/Windows/Avalonia.Win32/Automation/AutomationNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public Rect BoundingRectangle

public virtual IRawElementProviderFragmentRoot? FragmentRoot
{
get => InvokeSync(() => GetRoot()) as IRawElementProviderFragmentRoot;
get => InvokeSync(() => GetRoot());
}

public virtual IRawElementProviderSimple? HostRawElementProvider => null;
Expand Down Expand Up @@ -243,20 +243,10 @@ protected void RaiseFocusChanged(AutomationNode? focused)
(int)UiaEventId.AutomationFocusChanged);
}

private AutomationNode? GetRoot()
private RootAutomationNode? GetRoot()
{
Dispatcher.UIThread.VerifyAccess();

var peer = Peer;
var parent = peer.GetParent();

while (peer.GetProvider<AAP.IRootProvider>() is null && parent is object)
{
peer = parent;
parent = peer.GetParent();
}

return peer is object ? GetOrCreate(peer) : null;
return GetOrCreate(Peer.GetVisualRoot()) as RootAutomationNode;
}

private void OnPeerChildrenChanged(object? sender, EventArgs e)
Expand Down

0 comments on commit c1645ca

Please sign in to comment.