diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 90c06e1ca4..6fd2cb6218 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -86,7 +86,7 @@ - (NSAccessibilityRole)accessibilityRole { auto controlType = _peer->GetAutomationControlType(); - + switch (controlType) { case AutomationButton: return NSAccessibilityButtonRole; case AutomationCalendar: return NSAccessibilityGridRole; @@ -123,6 +123,7 @@ case AutomationSplitButton: return NSAccessibilityPopUpButtonRole; case AutomationWindow: return NSAccessibilityWindowRole; case AutomationPane: return NSAccessibilityGroupRole; + case AutomationScrollViewer: return NSAccessibilityScrollAreaRole; case AutomationHeader: return @"AXHeading"; case AutomationHeaderItem: return NSAccessibilityButtonRole; case AutomationTable: return NSAccessibilityTableRole; @@ -418,6 +419,25 @@ return YES; } +- (NSArray *)accessibilityActionNames +{ + NSAccessibilityActionName scrollToVisible = @"AXScrollToVisible"; + NSArray *base = [super accessibilityActionNames]; + if (base == nil) + base = @[]; + if ([base containsObject:scrollToVisible]) + return base; + return [base arrayByAddingObject:scrollToVisible]; +} + +- (void)accessibilityPerformAction:(NSAccessibilityActionName)action +{ + if ([action isEqualToString:@"AXScrollToVisible"]) + _peer->BringIntoView(); + else + [super accessibilityPerformAction:action]; +} + - (BOOL)isAccessibilitySelected { if (_peer->IsSelectionItemProvider()) diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index b6b056658b..b28d445443 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -49,6 +49,7 @@ namespace Avalonia.Automation.Peers TitleBar, Separator, Expander, + ScrollViewer, } public enum AutomationLandmarkType @@ -609,6 +610,7 @@ namespace Avalonia.Automation.Peers AutomationControlType.HeaderItem => "header item", AutomationControlType.TitleBar => "title bar", AutomationControlType.Expander => "group", + AutomationControlType.ScrollViewer => "scroll viewer", AutomationControlType.None => (GetLandmarkType()?.ToString() ?? controlType.ToString()).ToLowerInvariant(), _ => controlType.ToString().ToLowerInvariant(), }; diff --git a/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs index 674eb9a241..38fe21bf1f 100644 --- a/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs @@ -66,7 +66,7 @@ namespace Avalonia.Automation.Peers protected override AutomationControlType GetAutomationControlTypeCore() { - return AutomationControlType.Pane; + return AutomationControlType.ScrollViewer; } protected override bool IsContentElementCore() => false; diff --git a/src/Avalonia.FreeDesktop.AtSpi/AtSpiNode.RoleMapping.cs b/src/Avalonia.FreeDesktop.AtSpi/AtSpiNode.RoleMapping.cs index db1746acfe..c8db8ca028 100644 --- a/src/Avalonia.FreeDesktop.AtSpi/AtSpiNode.RoleMapping.cs +++ b/src/Avalonia.FreeDesktop.AtSpi/AtSpiNode.RoleMapping.cs @@ -53,6 +53,7 @@ namespace Avalonia.FreeDesktop.AtSpi AutomationControlType.TitleBar => AtSpiRole.TitleBar, AutomationControlType.Separator => AtSpiRole.Separator, AutomationControlType.Expander => AtSpiRole.Panel, + AutomationControlType.ScrollViewer => AtSpiRole.ScrollPane, _ => AtSpiRole.Unknown, }; } @@ -78,6 +79,7 @@ namespace Avalonia.FreeDesktop.AtSpi AtSpiRole.ProgressBar => "progress bar", AtSpiRole.RadioButton => "radio button", AtSpiRole.ScrollBar => "scroll bar", + AtSpiRole.ScrollPane => "scroll pane", AtSpiRole.Slider => "slider", AtSpiRole.SpinButton => "spin button", AtSpiRole.StatusBar => "status bar", diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index f78f65ed07..b5e9641050 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -66,6 +66,7 @@ namespace Avalonia.Native public int IsKeyboardFocusable() => _inner.IsKeyboardFocusable().AsComBool(); public void SetFocus() => _inner.SetFocus(); public int ShowContextMenu() => _inner.ShowContextMenu().AsComBool(); + public void BringIntoView() => _inner.BringIntoView(); public void SetNode(IAvnAutomationNode node) { diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 414b829a80..14d77d17d1 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -650,6 +650,7 @@ enum AvnAutomationControlType AutomationTitleBar, AutomationSeparator, AutomationExpander, + AutomationScrollViewer, } enum AvnLandmarkType @@ -1281,6 +1282,7 @@ interface IAvnAutomationPeer : IUnknown bool IsKeyboardFocusable(); void SetFocus(); bool ShowContextMenu(); + void BringIntoView(); IAvnAutomationPeer* GetRootPeer(); diff --git a/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs index dad9dca348..e2ffca2ea7 100644 --- a/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs +++ b/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs @@ -373,6 +373,7 @@ namespace Avalonia.Win32.Automation AutomationControlType.TitleBar => UiaControlTypeId.TitleBar, AutomationControlType.Separator => UiaControlTypeId.Separator, AutomationControlType.Expander => UiaControlTypeId.Group, + AutomationControlType.ScrollViewer => UiaControlTypeId.Pane, _ => UiaControlTypeId.Custom, }; }