From 49ef4b3f280e4fdab2396f13da7d0a3ff4693e42 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 4 Oct 2021 22:51:39 +0200 Subject: [PATCH] Added AccessibilityView and ControlType. - `AutomationProperties.AccessibilityView`: from UWP - control's element's visibility in automation tree - `ControlTypeOverride`: from UWP proposal - overrides the automation control type specified in peer --- .../Automation/AutomationProperties.cs | 91 +++++++++++++++++++ .../Automation/Peers/AutomationPeer.cs | 9 +- .../Automation/Peers/ControlAutomationPeer.cs | 11 ++- 3 files changed, 106 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Automation/AutomationProperties.cs b/src/Avalonia.Controls/Automation/AutomationProperties.cs index 8ff1210a76..c20af148b8 100644 --- a/src/Avalonia.Controls/Automation/AutomationProperties.cs +++ b/src/Avalonia.Controls/Automation/AutomationProperties.cs @@ -1,8 +1,30 @@ using System; +using Avalonia.Automation.Peers; using Avalonia.Controls; namespace Avalonia.Automation { + /// + /// Declares how a control should included in different views of the automation tree. + /// + public enum AccessibilityView + { + /// + /// The control is included in the Raw view of the automation tree. + /// + Raw, + + /// + /// The control is included in the Control view of the automation tree. + /// + Control, + + /// + /// The control is included in the Content view of the automation tree. + /// + Content, + } + public static class AutomationProperties { internal const int AutomationPositionInSetDefault = -1; @@ -16,6 +38,15 @@ namespace Avalonia.Automation "AcceleratorKey", typeof(AutomationProperties)); + /// + /// Defines the AutomationProperties.AccessibilityView attached property. + /// + public static readonly AttachedProperty AccessibilityViewProperty = + AvaloniaProperty.RegisterAttached( + "AccessibilityView", + typeof(AutomationProperties), + defaultValue: AccessibilityView.Content); + /// /// Defines the AutomationProperties.AccessKey attached property /// @@ -32,6 +63,14 @@ namespace Avalonia.Automation "AutomationId", typeof(AutomationProperties)); + /// + /// Defines the AutomationProperties.ControlTypeOverride attached property. + /// + public static readonly AttachedProperty ControlTypeOverrideProperty = + AvaloniaProperty.RegisterAttached( + "ControlTypeOverride", + typeof(AutomationProperties)); + /// /// Defines the AutomationProperties.HelpText attached property. /// @@ -171,6 +210,32 @@ namespace Avalonia.Automation return ((string)element.GetValue(AcceleratorKeyProperty)); } + /// + /// Helper for setting AccessibilityView property on a StyledElement. + /// + public static void SetAccessibilityView(StyledElement element, AccessibilityView value) + { + if (element == null) + { + throw new ArgumentNullException(nameof(element)); + } + + element.SetValue(AccessibilityViewProperty, value); + } + + /// + /// Helper for reading AccessibilityView property from a StyledElement. + /// + public static AccessibilityView GetAccessibilityView(StyledElement element) + { + if (element == null) + { + throw new ArgumentNullException(nameof(element)); + } + + return element.GetValue(AccessibilityViewProperty); + } + /// /// Helper for setting AccessKey property on a StyledElement. /// @@ -223,6 +288,32 @@ namespace Avalonia.Automation return element.GetValue(AutomationIdProperty); } + /// + /// Helper for setting ControlTypeOverride property on a StyledElement. + /// + public static void SetControlTypeOverride(StyledElement element, AutomationControlType? value) + { + if (element == null) + { + throw new ArgumentNullException(nameof(element)); + } + + element.SetValue(ControlTypeOverrideProperty, value); + } + + /// + /// Helper for reading ControlTypeOverride property from a StyledElement. + /// + public static AutomationControlType? GetControlTypeOverride(StyledElement element) + { + if (element == null) + { + throw new ArgumentNullException(nameof(element)); + } + + return element.GetValue(ControlTypeOverrideProperty); + } + /// /// Helper for setting HelpText property on a StyledElement. /// diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index c3276db058..fbca22031a 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -74,7 +74,7 @@ namespace Avalonia.Automation.Peers /// /// Gets the control type for the element that is associated with the UI Automation peer. /// - public AutomationControlType GetAutomationControlType() => GetAutomationControlTypeCore(); + public AutomationControlType GetAutomationControlType() => GetControlTypeOverrideCore(); /// /// Gets the automation ID of the element that is associated with the UI Automation peer. @@ -230,7 +230,12 @@ namespace Avalonia.Automation.Peers protected abstract bool IsKeyboardFocusableCore(); protected abstract void SetFocusCore(); protected abstract bool ShowContextMenuCore(); - + + protected virtual AutomationControlType GetControlTypeOverrideCore() + { + return GetAutomationControlTypeCore(); + } + protected virtual object? GetProviderCore(Type providerType) { return providerType.IsAssignableFrom(this.GetType()) ? this : null; diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs index 947c88a190..3cbcdcf348 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -148,17 +148,22 @@ namespace Avalonia.Automation.Peers protected override string? GetAcceleratorKeyCore() => AutomationProperties.GetAcceleratorKey(Owner); protected override string? GetAccessKeyCore() => AutomationProperties.GetAccessKey(Owner); + protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Custom; protected override string? GetAutomationIdCore() => AutomationProperties.GetAutomationId(Owner) ?? Owner.Name; protected override Rect GetBoundingRectangleCore() => GetBounds(Owner.TransformedBounds); - protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Custom; protected override string GetClassNameCore() => Owner.GetType().Name; protected override bool HasKeyboardFocusCore() => Owner.IsFocused; - protected override bool IsContentElementCore() => true; - protected override bool IsControlElementCore() => true; + protected override bool IsContentElementCore() => AutomationProperties.GetAccessibilityView(Owner) >= AccessibilityView.Content; + protected override bool IsControlElementCore() => AutomationProperties.GetAccessibilityView(Owner) >= AccessibilityView.Control; protected override bool IsEnabledCore() => Owner.IsEnabled; protected override bool IsKeyboardFocusableCore() => Owner.Focusable; protected override void SetFocusCore() => Owner.Focus(); + protected override AutomationControlType GetControlTypeOverrideCore() + { + return AutomationProperties.GetControlTypeOverride(Owner) ?? GetAutomationControlTypeCore(); + } + private static Rect GetBounds(TransformedBounds? bounds) { return bounds?.Bounds.TransformToAABB(bounds!.Value.Transform) ?? default;