mirror of
https://github.com/microsoft/terminal.git
synced 2026-02-04 01:04:33 +00:00
Improve screen reader announcements for search box (#19726)
## Summary of the Pull Request
Improves the notification read out by a screen reader when a search is
done in the terminal. This is done across several scenarios:
- previously, the results were only read occasionally. Moving the block
out of `if (results.SearchInvalidated)` fixes that.
- previously, we read "Results found" or "No results found". Now, we
read an accessible version of the written status message.
In order to maintain consistency with the written status message,
`_FormatText()` now takes an `isAccessible` parameter to output a more
accessible version of the text. Specifically...
- `CurrentIndexTooHighStatus` (aka `?`) would not be read, so we replace
it with `TermControl_UnknownSearchResultIndex` (aka `unknown`)
- `TermControl_TooManySearchResults` (aka `999+`) would drop the `+`
when read, so we replace it with `TermControl_TooManySearchResults` (aka
`over 999`)
- `TermControl_NumResults` (aka `{0}/{1}``) would be read as a fraction,
so we replace it with `TermControl_NumResultsAccessible` (aka `{0} of
{1}`).
## Validation Steps Performed
✅ Announcements are read out when interacting with search box (i.e.
next, prev, regex toggle, etc.)
✅ "4 of 5" read out by screen reader (or something similar) when search
is performed
Closes #19691
This commit is contained in:
@@ -217,14 +217,6 @@
|
||||
<data name="TermControlReadOnly" xml:space="preserve">
|
||||
<value>Read-only mode is enabled.</value>
|
||||
</data>
|
||||
<data name="SearchBox_MatchesAvailable" xml:space="preserve">
|
||||
<value>Results found</value>
|
||||
<comment>Announced to a screen reader when the user searches for some text and there are matches for that text in the terminal.</comment>
|
||||
</data>
|
||||
<data name="SearchBox_NoMatches" xml:space="preserve">
|
||||
<value>No results found</value>
|
||||
<comment>Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal.</comment>
|
||||
</data>
|
||||
<data name="PasteCommandButton.Label" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
<comment>The label of a button for pasting the contents of the clipboard.</comment>
|
||||
@@ -330,4 +322,16 @@
|
||||
<value>Suggested input: {0}</value>
|
||||
<comment>{Locked="{0}"} {0} will be replaced with a string of input that is suggested for the user to input</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="TermControl_NumResultsAccessible" xml:space="preserve">
|
||||
<value>{0} of {1}</value>
|
||||
<comment>{Locked="{0}"}{Locked="{1}"} Read out by the screen reader to announce number of results from a search query. "{0}" is replaced with index of search result. "{1}" is replaced by total number of results.</comment>
|
||||
</data>
|
||||
<data name="TermControl_UnknownSearchResultIndex" xml:space="preserve">
|
||||
<value>unknown</value>
|
||||
<comment>Will be read out by a screen reader when a value for the index of a search result is mismatched and unclear. Displayed as a part of TermControl_NumResultsAccessible.</comment>
|
||||
</data>
|
||||
<data name="TermControl_TooManySearchResults" xml:space="preserve">
|
||||
<value>over 999</value>
|
||||
<comment>Will be read out by a screen reader when a search returns over 999 search results.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -463,9 +463,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Arguments:
|
||||
// - totalMatches - total number of matches (search results)
|
||||
// - currentMatch - the index of the current match (0-based)
|
||||
// - isAccessible - if true, format the string for screen readers. Defaults to false.
|
||||
// Return Value:
|
||||
// - status message
|
||||
winrt::hstring SearchBoxControl::_FormatStatus(int32_t totalMatches, int32_t currentMatch)
|
||||
winrt::hstring SearchBoxControl::_FormatStatus(int32_t totalMatches, int32_t currentMatch, bool isAccessible)
|
||||
{
|
||||
if (totalMatches < 0)
|
||||
{
|
||||
@@ -482,7 +483,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
if (currentMatch < 0 || currentMatch > (MaximumTotalResultsToShowInStatus - 1))
|
||||
{
|
||||
currentString = CurrentIndexTooHighStatus;
|
||||
currentString = isAccessible ? RS_(L"TermControl_UnknownSearchResultIndex") : CurrentIndexTooHighStatus;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -491,13 +492,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
if (totalMatches > MaximumTotalResultsToShowInStatus)
|
||||
{
|
||||
totalString = TotalResultsTooHighStatus;
|
||||
totalString = isAccessible ? RS_(L"TermControl_TooManySearchResults") : TotalResultsTooHighStatus;
|
||||
}
|
||||
else
|
||||
{
|
||||
totalString = fmt::to_wstring(totalMatches);
|
||||
}
|
||||
|
||||
if (isAccessible)
|
||||
{
|
||||
return winrt::hstring{ RS_fmt(L"TermControl_NumResultsAccessible", currentString, totalString) };
|
||||
}
|
||||
return winrt::hstring{ RS_fmt(L"TermControl_NumResults", currentString, totalString) };
|
||||
}
|
||||
|
||||
@@ -557,10 +562,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
StatusBox().Text(status);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Formats and returns an accessible status message representing the search state.
|
||||
// - Similar to SetStatus but returns a more descriptive string for screen readers.
|
||||
hstring SearchBoxControl::GetAccessibleStatus(int32_t totalMatches, int32_t currentMatch, bool searchRegexInvalid)
|
||||
{
|
||||
if (searchRegexInvalid)
|
||||
{
|
||||
return RS_(L"SearchRegexInvalid");
|
||||
}
|
||||
return _FormatStatus(totalMatches, currentMatch, true);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes the status message in the status box.
|
||||
void SearchBoxControl::ClearStatus()
|
||||
{
|
||||
StatusBox().Text(L"");
|
||||
StatusBox().Text({});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void PopulateTextbox(const winrt::hstring& text);
|
||||
bool ContainsFocus();
|
||||
void SetStatus(int32_t totalMatches, int32_t currentMatch, bool searchRegexInvalid);
|
||||
winrt::hstring GetAccessibleStatus(int32_t totalMatches, int32_t currentMatch, bool searchRegexInvalid);
|
||||
void ClearStatus();
|
||||
|
||||
void GoBackwardClicked(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::RoutedEventArgs& /*e*/);
|
||||
@@ -77,7 +78,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _PlayCloseAnimation();
|
||||
bool _AnimationEnabled();
|
||||
|
||||
static winrt::hstring _FormatStatus(int32_t totalMatches, int32_t currentMatch);
|
||||
static winrt::hstring _FormatStatus(int32_t totalMatches, int32_t currentMatch, bool isAccessible = false);
|
||||
static double _TextWidth(winrt::hstring text, double fontSize);
|
||||
double _GetStatusMaxWidth();
|
||||
|
||||
|
||||
@@ -3754,13 +3754,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
};
|
||||
_updateScrollBar->Run(update);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto automationPeer{ FrameworkElementAutomationPeer::FromElement(*this) })
|
||||
if (auto automationPeer{ FrameworkElementAutomationPeer::FromElement(*this) })
|
||||
{
|
||||
const auto status = _searchBox->GetAccessibleStatus(results.TotalMatches, results.CurrentMatch, results.SearchRegexInvalid);
|
||||
if (!status.empty())
|
||||
{
|
||||
automationPeer.RaiseNotificationEvent(
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent,
|
||||
results.TotalMatches > 0 ? RS_(L"SearchBox_MatchesAvailable") : RS_(L"SearchBox_NoMatches"), // what to announce if results were found
|
||||
status,
|
||||
L"SearchBoxResultAnnouncement" /* unique name for this group of notifications */);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user