diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index b7bd8906c5d9ecb7b74597a5103dca8c330e1d7d..70df6d05fe564344c375ee898fb55ded4f01bd5d 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -108,6 +108,7 @@ + diff --git a/src/EditorFeatures/Core/EditorFeaturesResources.Designer.cs b/src/EditorFeatures/Core/EditorFeaturesResources.Designer.cs index b271086368f9ce88a724ced79fc915a26010177e..7f05a43d1c3c0a84cfd05a3ed6b96f988ccb0238 100644 --- a/src/EditorFeatures/Core/EditorFeaturesResources.Designer.cs +++ b/src/EditorFeatures/Core/EditorFeaturesResources.Designer.cs @@ -188,6 +188,15 @@ internal class EditorFeaturesResources { } } + /// + /// Looks up a localized string similar to An inline rename session is active. + /// + internal static string An_inline_rename_session_is_active { + get { + return ResourceManager.GetString("An_inline_rename_session_is_active", resourceCulture); + } + } + /// /// Looks up a localized string similar to _Apply. /// diff --git a/src/EditorFeatures/Core/EditorFeaturesResources.resx b/src/EditorFeatures/Core/EditorFeaturesResources.resx index ca8aacef83d57454fc37027d3da1355ac5657ad9..219f96e03041d2432b249ac5ffbd9b0b33cc96d7 100644 --- a/src/EditorFeatures/Core/EditorFeaturesResources.resx +++ b/src/EditorFeatures/Core/EditorFeaturesResources.resx @@ -754,4 +754,8 @@ Do you want to proceed? '{0}' declarations + + An inline rename session is active + For screenreaders + \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/InlineRename/Dashboard/Dashboard.xaml.cs b/src/EditorFeatures/Core/Implementation/InlineRename/Dashboard/Dashboard.xaml.cs index 63e51412f1a44267400841f2d0c758b8f5a3303b..191e413cd03d70abb4a6fb2b344151538f59d54c 100644 --- a/src/EditorFeatures/Core/Implementation/InlineRename/Dashboard/Dashboard.xaml.cs +++ b/src/EditorFeatures/Core/Implementation/InlineRename/Dashboard/Dashboard.xaml.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Windows; +using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Input; using Microsoft.VisualStudio.Text.Editor; @@ -62,11 +63,27 @@ internal partial class Dashboard : UserControl, IDisposable // Find UI doesn't exist in ETA. } + // Once the Dashboard is loaded, the visual tree is completely created and the + // UIAutomation system has discovered and connected the AutomationPeer to the tree, + // allowing us to raise the AutomationFocusChanged event and have it process correctly. + // for us to set up the AutomationPeer + this.Loaded += Dashboard_Loaded; + this.Focus(); textView.Caret.IsHidden = false; ShouldReceiveKeyboardNavigation = false; } + private void Dashboard_Loaded(object sender, RoutedEventArgs e) + { + // Move automation focus to the Dashboard so that screenreaders will announce that the + // session has begun. + if (AutomationPeer.ListenerExists(AutomationEvents.AutomationFocusChanged)) + { + UIElementAutomationPeer.CreatePeerForElement(this)?.RaiseAutomationEvent(AutomationEvents.AutomationFocusChanged); + } + } + private void ShowCaret() { // We actually want the caret visible even though the view isn't explicitly focused. @@ -178,6 +195,11 @@ protected override void OnAccessKey(AccessKeyEventArgs e) } } + protected override AutomationPeer OnCreateAutomationPeer() + { + return new DashboardAutomationPeer(this); + } + private void DisconnectFromPresentationSource() { if (_rootInputElement != null) @@ -272,6 +294,8 @@ public void Dispose() ((UIElement)_findAdornmentLayer).LayoutUpdated -= FindAdornmentCanvas_LayoutUpdated; } + this.Loaded -= Dashboard_Loaded; + _model.Dispose(); PresentationSource.RemoveSourceChangedHandler(this, OnPresentationSourceChanged); } diff --git a/src/EditorFeatures/Core/Implementation/InlineRename/Dashboard/DashboardAutomationPeer.cs b/src/EditorFeatures/Core/Implementation/InlineRename/Dashboard/DashboardAutomationPeer.cs new file mode 100644 index 0000000000000000000000000000000000000000..b53ba29dc904db68f1e80d32332f81e136c87e3e --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/InlineRename/Dashboard/DashboardAutomationPeer.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename +{ + /// + /// Custom AutomationPeer to announce that an Inline Rename session has begun. + /// + internal class DashboardAutomationPeer : UserControlAutomationPeer + { + public DashboardAutomationPeer(UserControl owner) : base(owner) + { + } + + protected override bool HasKeyboardFocusCore() + { + return true; + } + + protected override bool IsKeyboardFocusableCore() + { + return true; + } + + protected override string GetNameCore() + { + return EditorFeaturesResources.An_inline_rename_session_is_active; + } + + protected override AutomationControlType GetAutomationControlTypeCore() + { + return AutomationControlType.Edit; + } + } +}