diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm
index 1847a83160..93a67a64e0 100644
--- a/native/Avalonia.Native/src/OSX/automation.mm
+++ b/native/Avalonia.Native/src/OSX/automation.mm
@@ -122,7 +122,7 @@
case AutomationSplitButton: return NSAccessibilityPopUpButtonRole;
case AutomationWindow: return NSAccessibilityWindowRole;
case AutomationPane: return NSAccessibilityGroupRole;
- case AutomationHeader: return NSAccessibilityGroupRole;
+ case AutomationHeader: return @"AXHeading";
case AutomationHeaderItem: return NSAccessibilityButtonRole;
case AutomationTable: return NSAccessibilityTableRole;
case AutomationTitleBar: return NSAccessibilityGroupRole;
@@ -176,6 +176,10 @@
{
return GetNSStringAndRelease(_peer->GetName());
}
+ else if (_peer->GetAutomationControlType() == AutomationHeader)
+ {
+ return [NSNumber numberWithInt:_peer->GetHeadingLevel()];
+ }
return [super accessibilityValue];
}
diff --git a/samples/IntegrationTestApp/Pages/AutomationPage.axaml b/samples/IntegrationTestApp/Pages/AutomationPage.axaml
index 9d6ef43b2a..dcc1ee479c 100644
--- a/samples/IntegrationTestApp/Pages/AutomationPage.axaml
+++ b/samples/IntegrationTestApp/Pages/AutomationPage.axaml
@@ -13,5 +13,14 @@
Foo
+
+ Header 1
+
+
+ Header 2
+
+
+ Header None
+
diff --git a/src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs b/src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs
index 574979c77b..27d7bedcb9 100644
--- a/src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs
+++ b/src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs
@@ -30,5 +30,11 @@ namespace Avalonia.Automation
/// by the method.
///
public static AutomationProperty HelpTextProperty { get; } = new AutomationProperty();
+
+ ///
+ /// Identifiers the heading level automation property. The class name property value is returned
+ /// by the method.
+ ///
+ public static AutomationProperty HeadingLevelProperty { get; } = new AutomationProperty();
}
}
diff --git a/src/Avalonia.Controls/Automation/AutomationProperties.cs b/src/Avalonia.Controls/Automation/AutomationProperties.cs
index 2762beab6b..989aa28935 100644
--- a/src/Avalonia.Controls/Automation/AutomationProperties.cs
+++ b/src/Avalonia.Controls/Automation/AutomationProperties.cs
@@ -104,6 +104,17 @@ namespace Avalonia.Automation
"HelpText",
typeof(AutomationProperties));
+ ///
+ /// Defines the AutomationProperties.HeadingLevel attached property.
+ ///
+ ///
+ /// This property affects the default value for .
+ ///
+ public static readonly AttachedProperty HeadingLevelProperty =
+ AvaloniaProperty.RegisterAttached(
+ "HeadingLevel",
+ typeof(AutomationProperties));
+
///
/// Defines the AutomationProperties.IsColumnHeader attached property.
///
@@ -348,6 +359,25 @@ namespace Avalonia.Automation
return element.GetValue(HelpTextProperty);
}
+ ///
+ /// Helper for setting the value of the on a StyledElement.
+ ///
+ public static void SetHeadingLevel(StyledElement element, int value)
+ {
+ _ = element ?? throw new ArgumentNullException(nameof(element));
+ element.SetValue(HeadingLevelProperty, value);
+ }
+
+ ///
+ /// Helper for reading the value of the on a StyledElement.
+ ///
+ ///
+ public static int GetHeadingLevel(StyledElement element)
+ {
+ _ = element ?? throw new ArgumentNullException(nameof(element));
+ return element.GetValue(HeadingLevelProperty);
+ }
+
///
/// Helper for setting the value of the on a StyledElement.
///
diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
index 0b4653cddb..d46a24cd59 100644
--- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
@@ -270,6 +270,23 @@ namespace Avalonia.Automation.Peers
///
public string GetHelpText() => GetHelpTextCore() ?? string.Empty;
+ ///
+ /// Gets the heading level that is associated with this automation peer.
+ ///
+ ///
+ ///
+ /// -
+ /// Windows
+ /// UIA_HeadingLevelPropertyId
+ ///
+ /// -
+ /// macOS
+ /// NSAccessibilityProtocol.accessibilityValue
+ ///
+ ///
+ ///
+ public int GetHeadingLevel() => GetHeadingLevelCore();
+
///
/// Gets the that is the parent of this .
///
@@ -503,6 +520,7 @@ namespace Avalonia.Automation.Peers
protected abstract AutomationPeer? GetLabeledByCore();
protected abstract string? GetNameCore();
protected virtual string? GetHelpTextCore() => null;
+ protected virtual int GetHeadingLevelCore() => 0;
protected abstract AutomationPeer? GetParentCore();
protected abstract bool HasKeyboardFocusCore();
protected abstract bool IsContentElementCore();
diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
index 0dff2db3c2..8bf578803e 100644
--- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
@@ -127,6 +127,7 @@ namespace Avalonia.Automation.Peers
return result;
}
+ protected override int GetHeadingLevelCore() => AutomationProperties.GetHeadingLevel(Owner);
protected override AutomationPeer? GetParentCore()
{
EnsureConnected();
diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs
index 16ff99f7dc..df268100e9 100644
--- a/src/Avalonia.Native/AvnAutomationPeer.cs
+++ b/src/Avalonia.Native/AvnAutomationPeer.cs
@@ -40,6 +40,7 @@ namespace Avalonia.Native
public IAvnAutomationPeer? LabeledBy => Wrap(_inner.GetLabeledBy());
public IAvnString Name => _inner.GetName().ToAvnString();
public IAvnString HelpText => _inner.GetHelpText().ToAvnString();
+ public int HeadingLevel => _inner.GetHeadingLevel();
public IAvnAutomationPeer? Parent => Wrap(_inner.GetParent());
public IAvnAutomationPeer? VisualRoot => Wrap(_inner.GetVisualRoot());
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index 97afb8667e..ee9538ec0c 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/src/Avalonia.Native/avn.idl
@@ -1238,6 +1238,7 @@ interface IAvnAutomationPeer : IUnknown
void ValueProvider_SetValue(char* value);
IAvnString* GetHelpText();
+ int GetHeadingLevel();
}
[uuid(b00af5da-78af-4b33-bfff-4ce13a6239a9)]
diff --git a/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs
index 39698e6ecb..611507cba5 100644
--- a/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs
+++ b/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs
@@ -37,6 +37,7 @@ namespace Avalonia.Win32.Automation
{ AutomationElementIdentifiers.ClassNameProperty, UiaPropertyId.ClassName },
{ AutomationElementIdentifiers.NameProperty, UiaPropertyId.Name },
{ AutomationElementIdentifiers.HelpTextProperty, UiaPropertyId.HelpText },
+ { AutomationElementIdentifiers.HeadingLevelProperty, UiaPropertyId.HeadingLevel },
{ ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, UiaPropertyId.ExpandCollapseExpandCollapseState },
{ RangeValuePatternIdentifiers.IsReadOnlyProperty, UiaPropertyId.RangeValueIsReadOnly},
{ RangeValuePatternIdentifiers.MaximumProperty, UiaPropertyId.RangeValueMaximum },
@@ -138,6 +139,7 @@ namespace Avalonia.Win32.Automation
UiaPropertyId.LocalizedControlType => InvokeSync(() => Peer.GetLocalizedControlType()),
UiaPropertyId.Name => InvokeSync(() => Peer.GetName()),
UiaPropertyId.HelpText => InvokeSync(() => Peer.GetHelpText()),
+ UiaPropertyId.HeadingLevel => InvokeSync(() => ToUiaHeadingLevel(Peer.GetHeadingLevel())),
UiaPropertyId.ProcessId => s_pid,
UiaPropertyId.RuntimeId => _runtimeId,
_ => null,
@@ -358,6 +360,23 @@ namespace Avalonia.Win32.Automation
};
}
+ private static UiaHeadingLevel ToUiaHeadingLevel(int level)
+ {
+ return level switch
+ {
+ 1 => UiaHeadingLevel.Level1,
+ 2 => UiaHeadingLevel.Level2,
+ 3 => UiaHeadingLevel.Level3,
+ 4 => UiaHeadingLevel.Level4,
+ 5 => UiaHeadingLevel.Level5,
+ 6 => UiaHeadingLevel.Level6,
+ 7 => UiaHeadingLevel.Level7,
+ 8 => UiaHeadingLevel.Level8,
+ 9 => UiaHeadingLevel.Level9,
+ _ => UiaHeadingLevel.None,
+ };
+ }
+
private static int GetProcessId()
{
#if NET6_0_OR_GREATER
diff --git a/src/Windows/Avalonia.Win32.Automation/Interop/IRawElementProviderSimple.cs b/src/Windows/Avalonia.Win32.Automation/Interop/IRawElementProviderSimple.cs
index c8c5d1ceee..71a5fc37a6 100644
--- a/src/Windows/Avalonia.Win32.Automation/Interop/IRawElementProviderSimple.cs
+++ b/src/Windows/Avalonia.Win32.Automation/Interop/IRawElementProviderSimple.cs
@@ -184,7 +184,14 @@ internal enum UiaPropertyId
OutlineThickness,
CenterPoint,
Rotatation,
- Size
+ Size,
+ IsSelectionPattern2Available,
+ Selection2FirstSelectedItem,
+ Selection2LastSelectedItem,
+ Selection2CurrentSelectedItem,
+ Selection2ItemCount,
+ HeadingLevel,
+ IsDialog
}
internal enum UiaPatternId
@@ -270,6 +277,20 @@ internal enum UiaControlTypeId
AppBar
};
+internal enum UiaHeadingLevel
+{
+ None = 80050,
+ Level1,
+ Level2,
+ Level3,
+ Level4,
+ Level5,
+ Level6,
+ Level7,
+ Level8,
+ Level9
+};
+
#if NET8_0_OR_GREATER
[GeneratedComInterface]
#else