Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce search status #8588

Closed
wants to merge 10 commits into from
8 changes: 8 additions & 0 deletions src/cascadia/TerminalControl/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@
<data name="HowToOpenRun.Text" xml:space="preserve">
<value>Ctrl+Click to follow link</value>
</data>
<data name="TermControl_NoMatch" xml:space="preserve">
<value>No results</value>
<comment>Will be presented near the search box when Find operation returned no results.</comment>
</data>
<data name="TermControl_Searching" xml:space="preserve">
<value>Searching...</value>
<comment>Will be presented near the search box when Find operation is running.</comment>
</data>
<data name="NoticeFontNotFound" xml:space="preserve">
<value>Unable to find the selected font "{0}".

Expand Down
154 changes: 154 additions & 0 deletions src/cascadia/TerminalControl/SearchBoxControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "pch.h"
#include "SearchBoxControl.h"
#include "SearchBoxControl.g.cpp"
#include <LibraryResources.h>

using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
Expand All @@ -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:
Expand Down Expand Up @@ -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:
// - <none>
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:
// - <none>
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:
// - <none>
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);
}
Comment on lines +347 to +350
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a complicated version of MaxWidth. Any opportunity for simplification?


StatusBox().Text(status);
}

// Method Description:
// - Enables / disables results navigation buttons
// Arguments:
// - enable: if true, the buttons should be enabled
// Return Value:
// - <none>
void SearchBoxControl::SetNavigationEnabled(bool enabled)
{
GoBackwardButton().IsEnabled(enabled);
GoForwardButton().IsEnabled(enabled);
}
}
15 changes: 14 additions & 1 deletion src/cascadia/TerminalControl/SearchBoxControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,44 @@ 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<SearchBoxControl>
{
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);

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<winrt::Windows::Foundation::IInspectable> _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);
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalControl/SearchBoxControl.idl
Original file line number Diff line number Diff line change
Expand Up @@ -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<SearchBoxControl, Windows.UI.Xaml.RoutedEventArgs> Closed;
}
}
15 changes: 12 additions & 3 deletions src/cascadia/TerminalControl/SearchBoxControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#404040"/>
<SolidColorBrush x:Key="TextControlButtonForegroundPressed" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed" Color="#FF4343"/>

<!-- ToggleButton colors !-->
<SolidColorBrush x:Key="ToggleButtonForeground" Color="#B5B5B5"/>

Expand Down Expand Up @@ -161,8 +161,16 @@
KeyDown="TextBoxKeyDown"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Center">
VerticalAlignment="Center"
TextChanged="TextBoxTextChanged">
</TextBox>
<TextBlock x:Name="StatusBox"
x:Uid="SearchBox_StatusBox"
FontSize="15"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Center">
</TextBlock>

<ToggleButton x:Name="GoBackwardButton"
x:Uid="SearchBox_SearchBackwards"
Expand All @@ -181,7 +189,8 @@

<ToggleButton x:Name="CaseSensitivityButton"
x:Uid="SearchBox_CaseSensitivity"
Style="{StaticResource ToggleButtonStyle}">
Style="{StaticResource ToggleButtonStyle}"
Click="CaseSensitivityButtonClicked">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this also trigger for keyboard activation? is there another event on ToggleButton that we can listen to for it being toggled?

<PathIcon Data="M8.87305 10H7.60156L6.5625 7.25195H2.40625L1.42871 10H0.150391L3.91016 0.197266H5.09961L8.87305 10ZM6.18652 6.21973L4.64844 2.04297C4.59831 1.90625 4.54818 1.6875 4.49805 1.38672H4.4707C4.42513 1.66471 4.37272 1.88346 4.31348 2.04297L2.78906 6.21973H6.18652ZM15.1826 10H14.0615V8.90625H14.0342C13.5465 9.74479 12.8288 10.1641 11.8809 10.1641C11.1836 10.1641 10.6367 9.97949 10.2402 9.61035C9.84831 9.24121 9.65234 8.7513 9.65234 8.14062C9.65234 6.83268 10.4225 6.07161 11.9629 5.85742L14.0615 5.56348C14.0615 4.37402 13.5807 3.7793 12.6191 3.7793C11.776 3.7793 11.015 4.06641 10.3359 4.64062V3.49219C11.0241 3.05469 11.8171 2.83594 12.7148 2.83594C14.36 2.83594 15.1826 3.70638 15.1826 5.44727V10ZM14.0615 6.45898L12.373 6.69141C11.8535 6.76432 11.4616 6.89421 11.1973 7.08105C10.9329 7.26335 10.8008 7.58919 10.8008 8.05859C10.8008 8.40039 10.9215 8.68066 11.1631 8.89941C11.4092 9.11361 11.735 9.2207 12.1406 9.2207C12.6966 9.2207 13.1546 9.02702 13.5146 8.63965C13.8792 8.24772 14.0615 7.75326 14.0615 7.15625V6.45898Z"/>
</ToggleButton>

Expand Down
Loading