diff --git a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw
index e94aa14bd92..434edb36e9b 100644
--- a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw
@@ -178,6 +178,14 @@
Ctrl+Click to follow link
+
+ No results
+ Will be presented near the search box when Find operation returned no results.
+
+
+ Searching...
+ Will be presented near the search box when Find operation is running.
+
Unable to find the selected font "{0}".
diff --git a/src/cascadia/TerminalControl/SearchBoxControl.cpp b/src/cascadia/TerminalControl/SearchBoxControl.cpp
index 57026c40321..432da162c5e 100644
--- a/src/cascadia/TerminalControl/SearchBoxControl.cpp
+++ b/src/cascadia/TerminalControl/SearchBoxControl.cpp
@@ -4,6 +4,7 @@
#include "pch.h"
#include "SearchBoxControl.h"
#include "SearchBoxControl.g.cpp"
+#include
using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
@@ -18,12 +19,26 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
this->CharacterReceived({ this, &SearchBoxControl::_CharacterHandler });
this->KeyDown({ this, &SearchBoxControl::_KeyDownHandler });
+ this->RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) {
+ // Once the control is visible again we trigger SearchChanged event.
+ // We do this since we probably have a value from the previous search,
+ // and in such case logically the search changes from "nothing" to this value.
+ // A good example for SearchChanged event consumer is Terminal Control.
+ // Once the Search Box is open we want the Terminal Control
+ // to immediately perform the search with the value appearing in the box.
+ if (Visibility() == Visibility::Visible)
+ {
+ _SearchChangedHandlers(TextBox().Text(), _GoForward(), _CaseSensitive());
+ }
+ });
_focusableElements.insert(TextBox());
_focusableElements.insert(CloseButton());
_focusableElements.insert(CaseSensitivityButton());
_focusableElements.insert(GoForwardButton());
_focusableElements.insert(GoBackwardButton());
+
+ StatusBox().Width(_GetStatusMaxWidth());
}
// Method Description:
@@ -209,4 +224,143 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
e.Handled(true);
}
+
+ // Method Description:
+ // - Handler for changing the text. Triggers SearchChanged event
+ // Arguments:
+ // - sender: not used
+ // - e: event data
+ // Return Value:
+ // -
+ void SearchBoxControl::TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/)
+ {
+ _SearchChangedHandlers(TextBox().Text(), _GoForward(), _CaseSensitive());
+ }
+
+ // Method Description:
+ // - Handler for clicking the case sensitivity toggle. Triggers SearchChanged event
+ // Arguments:
+ // - sender: not used
+ // - e: not used
+ // Return Value:
+ // -
+ void SearchBoxControl::CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/)
+ {
+ _SearchChangedHandlers(TextBox().Text(), _GoForward(), _CaseSensitive());
+ }
+
+ // Method Description:
+ // - Formats a status message representing the search state:
+ // * "Searching" - if totalMatches is negative
+ // * "No results" - if totalMatches is 0
+ // * "?/n" - if totalMatches=n matches and we didn't start the iteration over matches
+ // (usually we will get this after buffer update)
+ // * "m/n" - if we are currently at match m out of n.
+ // * "m/max+" - if n > max results to show
+ // * "?/max+" - if m > max results to show
+ // Arguments:
+ // - totalMatches - total number of matches (search results)
+ // - currentMatch - the index of the current match (0-based)
+ // Return Value:
+ // - status message
+ winrt::hstring SearchBoxControl::_FormatStatus(int32_t totalMatches, int32_t currentMatch)
+ {
+ if (totalMatches < 0)
+ {
+ return RS_(L"TermControl_Searching");
+ }
+
+ if (totalMatches == 0)
+ {
+ return RS_(L"TermControl_NoMatch");
+ }
+ std::wstringstream ss;
+
+ if (currentMatch < 0 || currentMatch > (MaximumTotalResultsToShowInStatus - 1))
+ {
+ ss << CurrentIndexTooHighStatus;
+ }
+ else
+ {
+ ss << currentMatch + 1;
+ }
+
+ ss << StatusDelimiter;
+
+ if (totalMatches > MaximumTotalResultsToShowInStatus)
+ {
+ ss << TotalResultsTooHighStatus;
+ }
+ else
+ {
+ ss << totalMatches;
+ }
+
+ return ss.str().data();
+ }
+
+ // Method Description:
+ // - Helper method to measure the width of the text block given the text and the font size
+ // Arguments:
+ // - text: the text to measure
+ // - fontSize: the size of the font to measure
+ // Return Value:
+ // - the size in pixels
+ double SearchBoxControl::_TextWidth(winrt::hstring text, double fontSize)
+ {
+ winrt::Windows::UI::Xaml::Controls::TextBlock t;
+ t.FontSize(fontSize);
+ t.Text(text);
+ t.Measure({ FLT_MAX, FLT_MAX });
+ return t.ActualWidth();
+ }
+
+ // Method Description:
+ // - This method tries to predict the maximal size of the status box
+ // by measuring different possible statuses
+ // Return Value:
+ // - the size in pixels
+ double SearchBoxControl::_GetStatusMaxWidth()
+ {
+ const auto fontSize = StatusBox().FontSize();
+ const auto maxLength = std::max({ _TextWidth(_FormatStatus(-1, -1), fontSize),
+ _TextWidth(_FormatStatus(0, -1), fontSize),
+ _TextWidth(_FormatStatus(MaximumTotalResultsToShowInStatus, MaximumTotalResultsToShowInStatus - 1), fontSize),
+ _TextWidth(_FormatStatus(MaximumTotalResultsToShowInStatus + 1, MaximumTotalResultsToShowInStatus - 1), fontSize),
+ _TextWidth(_FormatStatus(MaximumTotalResultsToShowInStatus + 1, MaximumTotalResultsToShowInStatus), fontSize) });
+
+ return maxLength;
+ }
+
+ // Method Description:
+ // - Formats and sets the status message in the status box.
+ // Increases the size of the box if required.
+ // Arguments:
+ // - totalMatches - total number of matches (search results)
+ // - currentMatch - the index of the current match (0-based)
+ // Return Value:
+ // -
+ void SearchBoxControl::SetStatus(int32_t totalMatches, int32_t currentMatch)
+ {
+ const auto status = _FormatStatus(totalMatches, currentMatch);
+ const auto requiredWidth = _TextWidth(status, StatusBox().FontSize());
+ if (requiredWidth > StatusBox().Width())
+ {
+ StatusBox().Width(requiredWidth);
+ }
+
+ StatusBox().Text(status);
+ }
+
+ // Method Description:
+ // - Enables / disables results navigation buttons
+ // Arguments:
+ // - enable: if true, the buttons should be enabled
+ // Return Value:
+ // -
+ void SearchBoxControl::SetNavigationEnabled(bool enabled)
+ {
+ GoBackwardButton().IsEnabled(enabled);
+ GoForwardButton().IsEnabled(enabled);
+ }
}
diff --git a/src/cascadia/TerminalControl/SearchBoxControl.h b/src/cascadia/TerminalControl/SearchBoxControl.h
index cdf4ee63830..ab78166d63f 100644
--- a/src/cascadia/TerminalControl/SearchBoxControl.h
+++ b/src/cascadia/TerminalControl/SearchBoxControl.h
@@ -17,13 +17,17 @@ Author(s):
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Controls.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
-
#include "SearchBoxControl.g.h"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
struct SearchBoxControl : SearchBoxControlT
{
+ static constexpr int32_t MaximumTotalResultsToShowInStatus = 999;
+ static constexpr std::wstring_view TotalResultsTooHighStatus = L"999+";
+ static constexpr std::wstring_view CurrentIndexTooHighStatus = L"?";
+ static constexpr std::wstring_view StatusDelimiter = L"/";
+
SearchBoxControl();
void TextBoxKeyDown(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
@@ -31,17 +35,26 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void SetFocusOnTextbox();
void PopulateTextbox(winrt::hstring const& text);
bool ContainsFocus();
+ void SetStatus(int32_t totalMatches, int32_t currentMatch);
+ void SetNavigationEnabled(bool enabled);
void GoBackwardClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/);
void GoForwardClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/);
void CloseClick(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+ void TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+ void CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
WINRT_CALLBACK(Search, SearchHandler);
+ WINRT_CALLBACK(SearchChanged, SearchHandler);
TYPED_EVENT(Closed, TerminalControl::SearchBoxControl, Windows::UI::Xaml::RoutedEventArgs);
private:
std::unordered_set _focusableElements;
+ static winrt::hstring _FormatStatus(int32_t totalMatches, int32_t currentMatch);
+ static double _TextWidth(winrt::hstring text, double fontSize);
+ double _GetStatusMaxWidth();
+
bool _GoForward();
bool _CaseSensitive();
void _KeyDownHandler(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
diff --git a/src/cascadia/TerminalControl/SearchBoxControl.idl b/src/cascadia/TerminalControl/SearchBoxControl.idl
index 78011841c21..0de5f825ee7 100644
--- a/src/cascadia/TerminalControl/SearchBoxControl.idl
+++ b/src/cascadia/TerminalControl/SearchBoxControl.idl
@@ -11,8 +11,11 @@ namespace Microsoft.Terminal.TerminalControl
void SetFocusOnTextbox();
void PopulateTextbox(String text);
Boolean ContainsFocus();
+ void SetStatus(Int32 totalMatches, Int32 currentMatch);
+ void SetNavigationEnabled(Boolean enabled);
event SearchHandler Search;
+ event SearchHandler SearchChanged;
event Windows.Foundation.TypedEventHandler Closed;
}
}
diff --git a/src/cascadia/TerminalControl/SearchBoxControl.xaml b/src/cascadia/TerminalControl/SearchBoxControl.xaml
index f64ae36ff66..bb87ab6efb3 100644
--- a/src/cascadia/TerminalControl/SearchBoxControl.xaml
+++ b/src/cascadia/TerminalControl/SearchBoxControl.xaml
@@ -53,7 +53,7 @@
-
+
@@ -161,8 +161,16 @@
KeyDown="TextBoxKeyDown"
Margin="5"
HorizontalAlignment="Left"
- VerticalAlignment="Center">
+ VerticalAlignment="Center"
+ TextChanged="TextBoxTextChanged">
+
+
+ Style="{StaticResource ToggleButtonStyle}"
+ Click="CaseSensitivityButtonClicked">
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index a1549b125ec..1cd6b99971c 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -38,6 +38,15 @@ constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100);
// The minimum delay between updating the locations of regex patterns
constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(500);
+// The minimum delay between triggering search upon output to terminal
+constexpr const auto SearchUponOutputInterval = std::chrono::milliseconds(500);
+
+// The delay before performing the search after output to terminal
+constexpr const auto SearchAfterOutputDelay = std::chrono::milliseconds(800);
+
+// The delay before performing the search after change of search criteria
+constexpr const auto SearchAfterChangeDelay = std::chrono::milliseconds(200);
+
DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::TerminalControl::CopyFormat);
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
@@ -113,6 +122,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto onReceiveOutputFn = [this](const hstring str) {
_terminal->Write(str);
_updatePatternLocations->Run();
+ _updateSearchStatus->Run();
};
_connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn);
@@ -184,10 +194,26 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
ScrollBarUpdateInterval,
Dispatcher());
+ _updateSearchStatus = std::make_shared>(
+ [weakThis = get_weak()]() {
+ if (auto control{ weakThis.get() })
+ {
+ // If in the middle of the search, recompute the matches.
+ // We avoid navigation to the first result to prevent auto-scrolling.
+ if (control->_searchState.has_value())
+ {
+ const SearchState searchState{ control->_searchState->Text, control->_searchState->Sensitivity };
+ control->_searchState.emplace(searchState);
+ control->_SearchAsync(std::nullopt, SearchAfterOutputDelay);
+ }
+ }
+ },
+ SearchUponOutputInterval,
+ Dispatcher());
+
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast(1.0 / 30.0 * 1000000));
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });
-
_ApplyUISettings();
}
@@ -227,34 +253,177 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Method Description:
// - Search text in text buffer. This is triggered if the user click
// search button or press enter.
+ // In the live search mode it will be also triggered once every time search criteria changes
// Arguments:
- // - text: the text to search
+ // - text: not used
// - goForward: boolean that represents if the current search direction is forward
- // - caseSensitive: boolean that represents if the current search is case sensitive
+ // - caseSensitive: not used
// Return Value:
// -
- void TermControl::_Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
+ void TermControl::_Search(const winrt::hstring& /*text*/, const bool goForward, const bool /*caseSensitive*/)
{
- if (text.size() == 0 || _closing)
+ _SelectSearchResult(goForward);
+ }
+
+ // Method Description:
+ // - Search text in text buffer.
+ // This is triggered when the user starts typing, clicks on navigation or
+ // when the search is active and the terminal text is changing
+ // Arguments:
+ // - goForward: optional boolean that represents if the current search direction is forward
+ // - delay: time in milliseconds to wait before performing the search
+ // (grace time to allow next search to start)
+ // Return Value:
+ // -
+ fire_and_forget TermControl::_SearchAsync(std::optional goForward, Windows::Foundation::TimeSpan const& delay)
+ {
+ // Run only if the search state was initialized
+ if (_closing || !_searchState.has_value())
{
return;
}
- const Search::Direction direction = goForward ?
- Search::Direction::Forward :
- Search::Direction::Backward;
+ const auto originalSearchId = _searchState->SearchId;
+ auto weakThis{ this->get_weak() };
- const Search::Sensitivity sensitivity = caseSensitive ?
- Search::Sensitivity::CaseSensitive :
- Search::Sensitivity::CaseInsensitive;
+ // If no matches were computed it means we need to perform the search
+ if (!_searchState->Matches.has_value())
+ {
+ // Before we search, let's wait a bit:
+ // probably the search criteria or the data are still modified.
+ co_await winrt::resume_after(delay);
- Search search(*GetUiaData(), text.c_str(), direction, sensitivity);
- auto lock = _terminal->LockForWriting();
- if (search.FindNext())
+ // Switch back to Dispatcher so we can set the Searching status
+ co_await winrt::resume_foreground(Dispatcher());
+ if (auto control{ weakThis.get() })
+ {
+ // If search box was collapsed or the new one search was triggered - let's cancel this one
+ if (!_searchState.has_value() || _searchState->SearchId != originalSearchId)
+ {
+ co_return;
+ }
+
+ // Let's mark the start of searching
+ if (_searchBox)
+ {
+ _searchBox->SetStatus(-1, -1);
+ _searchBox->SetNavigationEnabled(false);
+ }
+
+ std::vector> matches;
+ if (!_searchState->Text.empty())
+ {
+ // We perform explicit search forward, so the first result will also be the earliest buffer location
+ // We will use goForward later to decide if we need to select 1 of n or n of n.
+ Search search(*GetUiaData(), _searchState->Text.c_str(), Search::Direction::Forward, _searchState->Sensitivity);
+ while (co_await _SearchOne(search))
+ {
+ // if search box was collapsed or the new one search was triggered - let's cancel this one
+ if (!_searchState.has_value() || _searchState->SearchId != originalSearchId)
+ {
+ co_return;
+ }
+
+ matches.push_back(search.GetFoundLocation());
+ }
+
+ // if search box was collapsed or the new one search was triggered - let's cancel this one
+ if (!_searchState.has_value() || _searchState->SearchId != originalSearchId)
+ {
+ co_return;
+ }
+ }
+ _searchState->Matches.emplace(std::move(matches));
+ }
+ }
+
+ if (auto control{ weakThis.get() })
{
- _terminal->SetBlockSelection(false);
- search.Select();
+ _SelectSearchResult(goForward);
+ }
+ }
+
+ // Method Description:
+ // - Selects one of precomputed search results in the terminal (if exist).
+ // - Updates the search box control accordingly.
+ // - The selection might be preceded by going to next / previous result
+ // - goForward: if true, select next result; if false, select previous result;
+ // if not set, remain at the current result.
+ // Return Value:
+ // -
+ void TermControl::_SelectSearchResult(std::optional goForward)
+ {
+ if (_searchState.has_value() && _searchState->Matches.has_value())
+ {
+ auto& state = _searchState.value();
+ auto& matches = state.Matches.value();
+
+ if (goForward.has_value())
+ {
+ state.UpdateIndex(goForward.value());
+
+ const auto currentMatch = state.GetCurrentMatch();
+ if (currentMatch.has_value())
+ {
+ auto lock = _terminal->LockForWriting();
+ _terminal->SetBlockSelection(false);
+ _terminal->SelectNewRegion(currentMatch->first, currentMatch->second);
+ _renderer->TriggerSelection();
+ }
+ }
+
+ if (_searchBox)
+ {
+ _searchBox->SetStatus(gsl::narrow(matches.size()), state.CurrentMatchIndex);
+ _searchBox->SetNavigationEnabled(!_searchState->Matches->empty());
+ }
+ }
+ }
+
+ // Method Description:
+ // - Search for a single value in a background
+ // Arguments:
+ // - search: search object to use to find the next match
+ // Return Value:
+ // -
+ winrt::Windows::Foundation::IAsyncOperation TermControl::_SearchOne(Search& search)
+ {
+ bool found{ false };
+ auto weakThis{ this->get_weak() };
+
+ co_await winrt::resume_background();
+ if (auto control{ weakThis.get() })
+ {
+ // We don't lock the terminal for the duration of the entire search,
+ // since if the terminal was modified the search ID will be updated.
+ auto lock = _terminal->LockForWriting();
+ found = search.FindNext();
+ }
+
+ co_await winrt::resume_foreground(Dispatcher());
+ co_return found;
+ }
+
+ // Method Description:
+ // - The handler for the "search criteria changed" event. Clears selection and initiates a new search.
+ // Arguments:
+ // - text: the text to search
+ // - goForward: indicates whether the search should be performed forward (if set to true) or backward
+ // - caseSensitive: boolean that represents if the current search is case sensitive
+ // Return Value:
+ // -
+ void TermControl::_SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
+ {
+ if (_searchBox && _searchBox->Visibility() == Visibility::Visible)
+ {
+ // Clear the selection reset the anchor
+ _terminal->ClearSelection();
_renderer->TriggerSelection();
+
+ const auto sensitivity = caseSensitive ? Search::Sensitivity::CaseSensitive : Search::Sensitivity::CaseInsensitive;
+ const SearchState searchState{ text, sensitivity };
+ _searchState.emplace(searchState);
+ _SearchAsync(goForward, SearchAfterChangeDelay);
}
}
@@ -269,6 +438,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, RoutedEventArgs const& /*args*/)
{
_searchBox->Visibility(Visibility::Collapsed);
+ _searchState.reset();
// Set focus back to terminal control
this->Focus(FocusState::Programmatic);
@@ -3267,6 +3437,53 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return _terminal->GetTaskbarProgress();
}
+ std::atomic SearchState::_searchIdGenerator{ 0 };
+
+ // Method Description:
+ // - Updates the index of the current match according to the direction.
+ // The index will remain unchanged (usually -1) if the number of matches is 0
+ // Arguments:
+ // - goForward: if true, move to the next match, else go to previous
+ // Return Value:
+ // -
+ void SearchState::UpdateIndex(bool goForward)
+ {
+ if (Matches.has_value())
+ {
+ const int numMatches = ::base::saturated_cast(Matches->size());
+ if (numMatches > 0)
+ {
+ if (CurrentMatchIndex == -1)
+ {
+ CurrentMatchIndex = goForward ? 0 : numMatches - 1;
+ }
+ else
+ {
+ CurrentMatchIndex = (numMatches + CurrentMatchIndex + (goForward ? 1 : -1)) % numMatches;
+ }
+ }
+ }
+ }
+
+ // Method Description:
+ // - Retrieves the current match
+ // Arguments:
+ // -
+ // Return Value:
+ // - current match, null-opt if current match is invalid
+ // (e.g., when the index is -1 or there are no matches)
+ std::optional> SearchState::GetCurrentMatch()
+ {
+ if (Matches.has_value() && CurrentMatchIndex > -1 && CurrentMatchIndex < Matches->size())
+ {
+ return til::at(Matches.value(), CurrentMatchIndex);
+ }
+ else
+ {
+ return std::nullopt;
+ }
+ }
+
// -------------------------------- WinRT Events ---------------------------------
// Winrt events need a method for adding a callback to the event and removing the callback.
// These macros will define them both for you.
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index 288dea233dc..3f83ddbc2ce 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -98,6 +98,28 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const hstring _message;
};
+ struct SearchState
+ {
+ public:
+ static std::atomic _searchIdGenerator;
+
+ SearchState(const winrt::hstring& text, const Search::Sensitivity sensitivity) :
+ Text(text),
+ Sensitivity(sensitivity),
+ SearchId(_searchIdGenerator.fetch_add(1))
+ {
+ }
+
+ const winrt::hstring Text;
+ const Search::Sensitivity Sensitivity;
+ const size_t SearchId;
+ std::optional>> Matches;
+ int32_t CurrentMatchIndex{ -1 };
+
+ void UpdateIndex(bool goForward);
+ std::optional> GetCurrentMatch();
+ };
+
struct TermControl : TermControlT
{
TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection);
@@ -209,6 +231,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
std::shared_ptr> _updatePatternLocations;
+ std::shared_ptr> _updateSearchStatus;
+
struct ScrollBarUpdate
{
std::optional newValue;
@@ -261,6 +285,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
+ std::optional _searchState;
+
void _ApplyUISettings();
void _UpdateSystemParameterSettings() noexcept;
void _InitializeBackgroundBrush();
@@ -326,8 +352,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const unsigned int _NumberOfClicks(winrt::Windows::Foundation::Point clickPos, Timestamp clickTime);
double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const;
+ winrt::Windows::Foundation::IAsyncOperation _SearchOne(Search& search);
void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive);
+ void _SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive);
void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, Windows::UI::Xaml::RoutedEventArgs const& args);
+ fire_and_forget _SearchAsync(std::optional goForward, Windows::Foundation::TimeSpan const& delay);
+ void _SelectSearchResult(std::optional goForward);
// TSFInputControl Handlers
void _CompositionCompleted(winrt::hstring text);
diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml
index 8ef1d8f835a..51dde3521d6 100644
--- a/src/cascadia/TerminalControl/TermControl.xaml
+++ b/src/cascadia/TerminalControl/TermControl.xaml
@@ -76,6 +76,7 @@
HorizontalAlignment="Right"
VerticalAlignment="Top"
Search="_Search"
+ SearchChanged="_SearchChanged"
Closed="_CloseSearchBoxControl" />