From b8f77fea5753456e0aa652b40e24247bf3fb11b1 Mon Sep 17 00:00:00 2001 From: ahopper Date: Mon, 16 Sep 2024 12:03:48 +0100 Subject: [PATCH 1/6] fix Automation.HelpText on windows --- .../Automation/AutomationElementIdentifiers.cs | 6 ++++++ .../Automation/Peers/AutomationPeer.cs | 6 ++++++ .../Automation/Peers/ControlAutomationPeer.cs | 10 ++++++++++ .../Automation/Peers/InteropAutomationPeer.cs | 1 + .../Peers/UnrealizedElementAutomationPeer.cs | 2 ++ .../Avalonia.Win32/Automation/AutomationNode.cs | 2 ++ 6 files changed, 27 insertions(+) diff --git a/src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs b/src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs index 4566cd9db5a..574979c77b5 100644 --- a/src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs +++ b/src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs @@ -24,5 +24,11 @@ public static class AutomationElementIdentifiers /// by the method. /// public static AutomationProperty NameProperty { get; } = new AutomationProperty(); + + /// + /// Identifies the helpText automation property. The class name property value is returned + /// by the method. + /// + public static AutomationProperty HelpTextProperty { get; } = new AutomationProperty(); } } diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index bda9a911837..3f36d9981b9 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -112,6 +112,11 @@ public abstract class AutomationPeer /// Gets text that describes the element that is associated with this automation peer. /// public string GetName() => GetNameCore() ?? string.Empty; + + /// + /// Gets text that provides help for the element that is associated with this automation peer. + /// + public string GetHelpText() => GetHelpTextCore() ?? string.Empty; /// /// Gets the that is the parent of this . @@ -250,6 +255,7 @@ protected virtual string GetLocalizedControlTypeCore() protected abstract string GetClassNameCore(); protected abstract AutomationPeer? GetLabeledByCore(); protected abstract string? GetNameCore(); + protected abstract string? GetHelpTextCore(); 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 0c043e7577a..34de31bc7f8 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -116,7 +116,17 @@ protected override IReadOnlyList GetOrCreateChildrenCore() return result; } + protected override string? GetHelpTextCore() + { + var result = AutomationProperties.GetHelpText(Owner); + if (string.IsNullOrWhiteSpace(result)) + { + result = ToolTip.GetTip(Owner)?.ToString(); + } + + return result; + } protected override AutomationPeer? GetParentCore() { EnsureConnected(); diff --git a/src/Avalonia.Controls/Automation/Peers/InteropAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/InteropAutomationPeer.cs index 367c7804c3c..5dfd507e1b8 100644 --- a/src/Avalonia.Controls/Automation/Peers/InteropAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/InteropAutomationPeer.cs @@ -28,6 +28,7 @@ internal class InteropAutomationPeer : AutomationPeer protected override string GetClassNameCore() => throw new NotImplementedException(); protected override AutomationPeer? GetLabeledByCore() => throw new NotImplementedException(); protected override string? GetNameCore() => throw new NotImplementedException(); + protected override string? GetHelpTextCore() => throw new NotImplementedException(); protected override IReadOnlyList GetOrCreateChildrenCore() => throw new NotImplementedException(); protected override AutomationPeer? GetParentCore() => _parent; protected override bool HasKeyboardFocusCore() => throw new NotImplementedException(); diff --git a/src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs index 56d5aa79aee..16a579f659f 100644 --- a/src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs @@ -20,5 +20,7 @@ public abstract class UnrealizedElementAutomationPeer : AutomationPeer protected override void SetFocusCore() { } protected override bool ShowContextMenuCore() => false; protected internal override bool TrySetParent(AutomationPeer? parent) => false; + protected override string? GetHelpTextCore() => null; + } } diff --git a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs index 0a6f4d8d8e8..00b4f179c7a 100644 --- a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs @@ -28,6 +28,7 @@ internal partial class AutomationNode : MarshalByRefObject, { AutomationElementIdentifiers.BoundingRectangleProperty, UiaPropertyId.BoundingRectangle }, { AutomationElementIdentifiers.ClassNameProperty, UiaPropertyId.ClassName }, { AutomationElementIdentifiers.NameProperty, UiaPropertyId.Name }, + { AutomationElementIdentifiers.HelpTextProperty, UiaPropertyId.HelpText }, { ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, UiaPropertyId.ExpandCollapseExpandCollapseState }, { RangeValuePatternIdentifiers.IsReadOnlyProperty, UiaPropertyId.RangeValueIsReadOnly}, { RangeValuePatternIdentifiers.MaximumProperty, UiaPropertyId.RangeValueMaximum }, @@ -122,6 +123,7 @@ public virtual IRawElementProviderFragmentRoot? FragmentRoot UiaPropertyId.IsOffscreen => InvokeSync(() => Peer.IsOffscreen()), UiaPropertyId.LocalizedControlType => InvokeSync(() => Peer.GetLocalizedControlType()), UiaPropertyId.Name => InvokeSync(() => Peer.GetName()), + UiaPropertyId.HelpText => InvokeSync(() => Peer.GetHelpText()), UiaPropertyId.ProcessId => Process.GetCurrentProcess().Id, UiaPropertyId.RuntimeId => _runtimeId, _ => null, From c7e042cc9cc89a9f3351182425842f075dfaaee9 Mon Sep 17 00:00:00 2001 From: ahopper Date: Tue, 17 Sep 2024 16:11:54 +0100 Subject: [PATCH 2/6] fix AutomationProperties.HelpText on macos --- native/Avalonia.Native/src/OSX/automation.mm | 5 +++++ src/Avalonia.Native/AvnAutomationPeer.cs | 1 + src/Avalonia.Native/avn.idl | 2 ++ 3 files changed, 8 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 7171de38f7d..1847a831603 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -149,6 +149,11 @@ - (NSString *)accessibilityTitle return [super accessibilityTitle]; } +- (NSString *)accessibilityHelp +{ + return GetNSStringAndRelease(_peer->GetHelpText()); +} + - (id)accessibilityValue { if (_peer->IsRangeValueProvider()) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index d62cb041308..16ff99f7dce 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -39,6 +39,7 @@ private AvnAutomationPeer(AutomationPeer inner) public IAvnString ClassName => _inner.GetClassName().ToAvnString(); public IAvnAutomationPeer? LabeledBy => Wrap(_inner.GetLabeledBy()); public IAvnString Name => _inner.GetName().ToAvnString(); + public IAvnString HelpText => _inner.GetHelpText().ToAvnString(); 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 add248be29d..dfbd8c2516a 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -1219,6 +1219,8 @@ interface IAvnAutomationPeer : IUnknown bool IsValueProvider(); IAvnString* ValueProvider_GetValue(); void ValueProvider_SetValue(char* value); + + IAvnString* GetHelpText(); } [uuid(b00af5da-78af-4b33-bfff-4ce13a6239a9)] From e50a4de5c0f841efc1b9bee2a2a40d36e08be906 Mon Sep 17 00:00:00 2001 From: ahopper Date: Tue, 17 Sep 2024 17:54:02 +0100 Subject: [PATCH 3/6] change breaking abstract to virtual --- src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index 3f36d9981b9..7a53ee001da 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -255,7 +255,7 @@ protected virtual string GetLocalizedControlTypeCore() protected abstract string GetClassNameCore(); protected abstract AutomationPeer? GetLabeledByCore(); protected abstract string? GetNameCore(); - protected abstract string? GetHelpTextCore(); + protected virtual string? GetHelpTextCore() => null; protected abstract AutomationPeer? GetParentCore(); protected abstract bool HasKeyboardFocusCore(); protected abstract bool IsContentElementCore(); From e475a2ba86b422ccb97cea66ec916843c39d7e98 Mon Sep 17 00:00:00 2001 From: ahopper Date: Tue, 17 Sep 2024 18:04:09 +0100 Subject: [PATCH 4/6] only fall back to tooltip if string --- src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs index 34de31bc7f8..0dff2db3c2f 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -122,7 +122,7 @@ protected override IReadOnlyList GetOrCreateChildrenCore() if (string.IsNullOrWhiteSpace(result)) { - result = ToolTip.GetTip(Owner)?.ToString(); + result = ToolTip.GetTip(Owner) as string; } return result; From 6ab2f6767c642de79eadf84370562b442ce5bad8 Mon Sep 17 00:00:00 2001 From: ahopper Date: Tue, 17 Sep 2024 18:53:14 +0100 Subject: [PATCH 5/6] remove duplicate override --- .../Automation/Peers/UnrealizedElementAutomationPeer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs index 16a579f659f..0dabb1b069c 100644 --- a/src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs @@ -20,7 +20,6 @@ public abstract class UnrealizedElementAutomationPeer : AutomationPeer protected override void SetFocusCore() { } protected override bool ShowContextMenuCore() => false; protected internal override bool TrySetParent(AutomationPeer? parent) => false; - protected override string? GetHelpTextCore() => null; - + } } From 5de7bd07479b05917d869901855ae74b87add301 Mon Sep 17 00:00:00 2001 From: ahopper Date: Tue, 17 Sep 2024 16:11:54 +0100 Subject: [PATCH 6/6] fix AutomationProperties.HelpText on macos --- native/Avalonia.Native/src/OSX/automation.mm | 5 +++++ src/Avalonia.Native/AvnAutomationPeer.cs | 1 + src/Avalonia.Native/avn.idl | 2 ++ 3 files changed, 8 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 7171de38f7d..1847a831603 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -149,6 +149,11 @@ - (NSString *)accessibilityTitle return [super accessibilityTitle]; } +- (NSString *)accessibilityHelp +{ + return GetNSStringAndRelease(_peer->GetHelpText()); +} + - (id)accessibilityValue { if (_peer->IsRangeValueProvider()) diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index d62cb041308..16ff99f7dce 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -39,6 +39,7 @@ private AvnAutomationPeer(AutomationPeer inner) public IAvnString ClassName => _inner.GetClassName().ToAvnString(); public IAvnAutomationPeer? LabeledBy => Wrap(_inner.GetLabeledBy()); public IAvnString Name => _inner.GetName().ToAvnString(); + public IAvnString HelpText => _inner.GetHelpText().ToAvnString(); 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 add248be29d..dfbd8c2516a 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -1219,6 +1219,8 @@ interface IAvnAutomationPeer : IUnknown bool IsValueProvider(); IAvnString* ValueProvider_GetValue(); void ValueProvider_SetValue(char* value); + + IAvnString* GetHelpText(); } [uuid(b00af5da-78af-4b33-bfff-4ce13a6239a9)]