diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index d38c63a0a7d..d41553a9d33 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1285,12 +1285,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation ::Search search(*GetUiaData(), text.c_str(), direction, sensitivity); auto lock = _terminal->LockForWriting(); - if (search.FindNext()) + const bool foundMatch{ search.FindNext() }; + if (foundMatch) { _terminal->SetBlockSelection(false); search.Select(); _renderer->TriggerSelection(); } + + // Raise a FoundMatch event, which the control will use to notify + // narrator if there was any results in the buffer + auto foundResults = winrt::make_self(foundMatch); + _FoundMatchHandlers(*this, *foundResults); } void ControlCore::SetBackgroundOpacity(const double opacity) diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 5beebf9e91b..e15a6d448b9 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -171,6 +171,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs); TYPED_EVENT(TransparencyChanged, IInspectable, Control::TransparencyChangedEventArgs); TYPED_EVENT(ReceivedOutput, IInspectable, IInspectable); + TYPED_EVENT(FoundMatch, IInspectable, Control::FoundResultsArgs); // clang-format on private: diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 84cb83e80f2..4c2bb44a6ea 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -110,6 +110,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler RaiseNotice; event Windows.Foundation.TypedEventHandler TransparencyChanged; event Windows.Foundation.TypedEventHandler ReceivedOutput; + event Windows.Foundation.TypedEventHandler FoundMatch; }; } diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index f4b4d9bc786..28417c4eb33 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -11,3 +11,4 @@ #include "ScrollPositionChangedArgs.g.cpp" #include "RendererWarningArgs.g.cpp" #include "TransparencyChangedEventArgs.g.cpp" +#include "FoundResultsArgs.g.cpp" diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 92b60f393d1..de0a842eeb4 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -11,6 +11,7 @@ #include "ScrollPositionChangedArgs.g.h" #include "RendererWarningArgs.g.h" #include "TransparencyChangedEventArgs.g.h" +#include "FoundResultsArgs.g.h" #include "cppwinrt_utils.h" namespace winrt::Microsoft::Terminal::Control::implementation @@ -131,4 +132,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(double, Opacity); }; + + struct FoundResultsArgs : public FoundResultsArgsT + { + public: + FoundResultsArgs(const bool foundMatch) : + _FoundMatch(foundMatch) + { + } + + WINRT_PROPERTY(bool, FoundMatch); + }; } diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index dae093f51f8..77d794d1355 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -67,4 +67,10 @@ namespace Microsoft.Terminal.Control { Double Opacity { get; }; } + + + runtimeclass FoundResultsArgs + { + Boolean FoundMatch { get; }; + } } diff --git a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw index f2fde4b9dc5..4c17742c1ce 100644 --- a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw @@ -200,4 +200,12 @@ Please either install the missing font or choose another one. Read-only mode is enabled. + + Results found + Announced to a screen reader when the user searches for some text and there are matches for that text in the terminal. + + + No results found + Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal. + diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 993ddf0fce5..b5c8a8ee05a 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -83,6 +83,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged }); _core.RaiseNotice({ this, &TermControl::_coreRaisedNotice }); _core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged }); + _core.FoundMatch({ this, &TermControl::_coreFoundMatch }); _interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); _interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); @@ -2601,4 +2602,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return _core.ReadEntireBuffer(); } + + // Method Description: + // - Called when the core raises a FoundMatch event. That's done in response + // to us starting a search query with ControlCore::Search. + // - The args will tell us if there were or were not any results for that + // particular search. We'll use that to control what to announce to + // Narrator. When we have more elaborate search information to report, we + // may want to report that here. (see GH #3920) + // Arguments: + // - args: contains information about the results that were or were not found. + // Return Value: + // - + void TermControl::_coreFoundMatch(const IInspectable& /*sender*/, const Control::FoundResultsArgs& args) + { + if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(*this) }) + { + automationPeer.RaiseNotificationEvent( + Automation::Peers::AutomationNotificationKind::ActionCompleted, + Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent, + args.FoundMatch() ? RS_(L"SearchBox_MatchesAvailable") : RS_(L"SearchBox_NoMatches"), // what to announce if results were found + L"SearchBoxResultAnnouncement" /* unique name for this group of notifications */); + } + } + } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 6be047aae17..8082f5d5055 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -274,6 +274,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args); void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args); void _coreWarningBell(const IInspectable& sender, const IInspectable& args); + void _coreFoundMatch(const IInspectable& sender, const Control::FoundResultsArgs& args); }; }