Compare commits

...

75 Commits

Author SHA1 Message Date
Mike Griese
971e7c5c91 update scrollbar as we scroll the buffer 2023-08-28 11:38:49 -05:00
Mike Griese
444398d630 Merge remote-tracking branch 'origin/main' into dev/migrie/search-v2-v3 2023-08-28 10:41:21 -05:00
Mike Griese
198decc6e0 🚧 dead code construction crew part 2 2023-08-23 14:45:05 -05:00
Mike Griese
574f30b424 🚧 dead code construction crew 2023-08-23 14:40:21 -05:00
Mike Griese
d3e69f788c It's so much nicer 2023-08-23 14:38:49 -05:00
Mike Griese
c1d1f6371e Merge branch 'dev/migrie/fhl/search-marks' into dev/migrie/search-v2-v3 2023-08-23 14:22:34 -05:00
Mike Griese
1fb4331cb3 Merge remote-tracking branch 'origin/main' into dev/migrie/search-v2-v3 2023-08-23 13:37:50 -05:00
Leonard Hecker
362693b326 Fix AuditMode failures 2023-08-23 17:12:24 +02:00
Leonard Hecker
7cccf568a7 Optimize GetReadableColumnCount 2023-08-23 17:09:50 +02:00
Leonard Hecker
1fc01d8197 Address Dustin's feedback 2023-08-23 17:09:39 +02:00
Leonard Hecker
1e527c4090 Remove selection coloring from Search 2023-08-23 17:09:24 +02:00
Leonard Hecker
d53d4bd99c Spelling fix 2023-08-22 22:50:40 +02:00
Leonard Hecker
ca4ec38485 Address feedback, Use namespaces 2023-08-22 22:49:56 +02:00
Leonard Hecker
c731eae08c Address feedback 2023-08-22 18:55:44 +02:00
Leonard Hecker
56c79112e5 Address feedback 2023-08-21 19:26:27 +02:00
Leonard Hecker
ecc735d007 Implement backwards text iteration 2023-08-21 19:21:50 +02:00
Leonard Hecker
e0fb764c05 Fix unit tests 2023-08-21 16:21:23 +02:00
Leonard Hecker
5ca26aa3dd More x86 woes, Improve UiaTextRangeBase 2023-08-21 15:32:19 +02:00
Leonard Hecker
8146ecdffe Fix a bug and x86 woes 2023-08-21 15:15:16 +02:00
Leonard Hecker
dba1629d06 Spelling fix 2023-08-21 14:59:18 +02:00
Leonard Hecker
a346a2a60a Improve documentation 2023-08-21 14:56:12 +02:00
Leonard Hecker
462b8d228d Use ICU for text search 2023-08-21 14:21:02 +02:00
Mike Griese
0a1568ddfc Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2023-08-15 13:04:41 -05:00
Mike Griese
b1107de808 this formatter man 2023-08-14 12:41:37 -05:00
Mike Griese
c97721c352 PR notes 2023-08-14 07:15:27 -05:00
Mike Griese
fd77061c42 resave resw in VS 2023-08-14 06:14:35 -05:00
Mike Griese
bc315b16d9 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2023-08-14 05:47:32 -05:00
Mike Griese
d707096ba6 format 2023-08-09 08:23:39 -05:00
Mike Griese
6b650d725d Merge branch 'main' into dev/migrie/fhl/search-marks 2023-08-09 08:07:48 -05:00
Mike Griese
876d4faf53 audit mode more 2023-07-19 16:19:32 -05:00
Mike Griese
aa4730812c I think this is probably unreasonably cheecky 2023-07-19 15:04:10 -05:00
Mike Griese
c2f36cc44e this'll do 2023-07-19 14:17:58 -05:00
Mike Griese
59063725b5 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2023-07-19 10:53:37 -05:00
Mike Griese
af776524ba Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2023-07-19 10:24:55 -05:00
Mike Griese
28bce48b29 the codeformat check should just commit the fix itself >__< 2023-07-18 15:28:16 -05:00
Mike Griese
e4056caaeb PR nits 2023-07-18 15:10:57 -05:00
Mike Griese
5360d68e59 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2023-07-18 14:05:29 -05:00
Mike Griese
09eb0a91a6 some minor review nits 2023-07-13 13:34:08 -05:00
Mike Griese
ebdc887368 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2023-07-13 13:27:10 -05:00
Mike Griese
b288f5dec9 oops missed this merge conflict 2023-01-29 09:54:52 -06:00
Mike Griese
5509a965c8 Merge branch 'main' into dev/migrie/fhl/search-marks 2023-01-29 06:57:17 -06:00
Mike Griese
49c729aae4 Merge branch 'dev/migrie/fhl/search-marks' of https://github.com/microsoft/terminal into dev/migrie/fhl/search-marks 2022-12-01 16:24:41 -06:00
Mike Griese
2dc568391d -Wall 2022-12-01 16:22:22 -06:00
Mike Griese
28a71b10ac this doesn't fail locally. Maybe it got fixed in the merge with main? 2022-11-30 13:36:42 -06:00
Mike Griese
9e6b97c12e Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2022-11-30 13:21:33 -06:00
Mike Griese
953d87b1ad runformat 2022-11-17 14:14:18 -06:00
Dustin L. Howett
03f81b2b7e Migrate spelling-0.0.21 changes from main 2022-11-17 14:14:18 -06:00
Mike Griese
198b3cff4d why _was_ that optional 2022-11-17 14:13:31 -06:00
Mike Griese
da1e89f3ee KISS, you're welcome leonard 2022-11-17 14:03:32 -06:00
Mike Griese
f396d3685c POC: remove yielding for fun and for profit 2022-11-17 13:50:42 -06:00
Mike Griese
195fc67fa3 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2022-11-17 13:07:45 -06:00
Mike Griese
e49e13e3c6 This is a test fix, because now we send notifications even for circling 2022-11-04 07:36:08 -05:00
Mike Griese
bcbce3462f Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/search-marks 2022-11-03 16:04:08 -05:00
Mike Griese
57913e426b update the pips if the first hit is in the viewport 2022-09-20 12:57:00 -05:00
Mike Griese
e4e603d757 rename slightly for convention's sake 2022-09-20 12:11:55 -05:00
Mike Griese
d437420eed making the scrollbar more sensible 2022-09-20 11:54:04 -05:00
Mike Griese
5cf5ccdd51 this seems to work well enough 2022-09-20 11:39:15 -05:00
Mike Griese
f61c080a26 cleanup 2022-09-20 11:28:47 -05:00
Mike Griese
ec0ae7d34e throttledfunc, for good and for profit 2022-09-20 11:15:55 -05:00
Mike Griese
3f39b5b10a KB focus works again 2022-09-20 10:36:28 -05:00
Mike Griese
8ae9e39d67 pips in the scrollbar 2022-09-20 10:17:15 -05:00
Mike Griese
a3c1d8a254 for all your dead code removal needs, call 0118 999 881 999 119 725 3 2022-09-20 07:03:16 -05:00
Mike Griese
bc5bf1e130 That was silly of me 2022-09-20 07:01:14 -05:00
Mike Griese
85f1bb7cde move the code around. It works again, bug... the buttons don't work at all?? 2022-09-20 06:53:51 -05:00
Mike Griese
f87a988d12 Merge branch 'pull/8588' into dev/migrie/fhl/search-results
Co-authored-by: Don-Vito <khvitaly@gmail.com>
2022-09-20 05:53:00 -05:00
khvitaly
160716d47b Reduce the value :) 2021-01-20 23:43:35 +02:00
khvitaly
8e5efb71bb Introduce computation of required statusbox width 2021-01-20 19:01:06 +02:00
khvitaly
1068f857b9 Merge branch 'main' into 6319-search-status 2021-01-20 11:17:25 +02:00
khvitaly
fe59504d20 Run search in the background + itnroduce grace for next search 2020-12-20 04:08:51 +02:00
khvitaly
4a2de19375 Add Searching status to translation 2020-12-20 04:08:09 +02:00
khvitaly
4ab4d416f0 Allow disabling navigation buttons in SearchBox 2020-12-20 04:07:37 +02:00
khvitaly
81a4a3120c Merge remote-tracking branch 'upstream/main' into 6319-search-status 2020-12-18 11:02:54 +02:00
khvitaly
5391f4e64b Remove live-search feature flag 2020-12-16 02:56:08 +02:00
khvitaly
ff1558c272 Teach search box to update status when new output is written 2020-12-16 02:24:52 +02:00
khvitaly
fa06c6fa2f Introduce search status 2020-12-14 23:53:28 +02:00
19 changed files with 367 additions and 19 deletions

View File

@@ -8,6 +8,14 @@
using namespace Microsoft::Console::Types;
bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData)
{
return ResetIfStale(renderData,
_needle,
_step == -1, // this is the opposite of the initializer below
_caseInsensitive);
}
bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool reverse, bool caseInsensitive)
{
const auto& textBuffer = renderData.GetTextBuffer();
@@ -87,6 +95,7 @@ const til::point_span* Search::GetCurrent() const noexcept
// Routine Description:
// - Takes the found word and selects it in the screen buffer
bool Search::SelectCurrent() const
{
if (const auto s = GetCurrent())
@@ -102,3 +111,13 @@ bool Search::SelectCurrent() const
return false;
}
const std::vector<til::point_span>& Search::Results() const noexcept
{
return _results;
}
size_t Search::CurrentMatch() const noexcept
{
return _index;
}

View File

@@ -25,6 +25,7 @@ class Search final
public:
Search() = default;
bool ResetIfStale(Microsoft::Console::Render::IRenderData& renderData);
bool ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool reverse, bool caseInsensitive);
void MovePastCurrentSelection();
@@ -34,6 +35,10 @@ public:
const til::point_span* GetCurrent() const noexcept;
bool SelectCurrent() const;
const std::vector<til::point_span>& Results() const noexcept;
size_t CurrentMatch() const noexcept;
bool CurrentDirection() const noexcept;
private:
// _renderData is a pointer so that Search() is constexpr default constructable.
Microsoft::Console::Render::IRenderData* _renderData = nullptr;

View File

@@ -39,6 +39,9 @@ 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 delay before performing the search after change of search criteria
constexpr const auto SearchAfterChangeDelay = std::chrono::milliseconds(200);
namespace winrt::Microsoft::Terminal::Control::implementation
{
static winrt::Microsoft::Terminal::Core::OptionalColor OptionalFromColor(const til::color& c)
@@ -1346,6 +1349,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
nullptr;
}
til::color ControlCore::ForegroundColor() const
{
return _terminal->GetRenderSettings().GetColorAlias(ColorAlias::DefaultForeground);
}
til::color ControlCore::BackgroundColor() const
{
return _terminal->GetRenderSettings().GetColorAlias(ColorAlias::DefaultBackground);
@@ -1552,6 +1560,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
const auto foundMatch = _searcher.SelectCurrent();
auto foundResults = winrt::make_self<implementation::FoundResultsArgs>(foundMatch);
if (foundMatch)
{
// this is used for search,
@@ -1560,15 +1569,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->SetBlockSelection(false);
_renderer->TriggerSelection();
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
foundResults->TotalMatches(gsl::narrow<int32_t>(_searcher.Results().size()));
foundResults->CurrentMatch(gsl::narrow<int32_t>(_searcher.CurrentMatch()));
_terminal->AlwaysNotifyOnBufferRotation(true);
}
// Raise a FoundMatch event, which the control will use to notify
// narrator if there was any results in the buffer
_FoundMatchHandlers(*this, winrt::make<implementation::FoundResultsArgs>(foundMatch));
_FoundMatchHandlers(*this, *foundResults);
}
Windows::Foundation::Collections::IVector<int32_t> ControlCore::SearchResultRows()
{
auto lock = _terminal->LockForWriting();
_searcher.ResetIfStale(*GetRenderData());
auto results = std::vector<int32_t>();
// use a map to remove duplicates
std::map<int32_t, bool> rows;
for (const auto& match : _searcher.Results())
{
const auto row = match.start.y;
// First check if it's in the map
if (rows.find(row) == rows.end())
{
rows[row] = true;
results.push_back(row);
}
}
return winrt::single_threaded_vector<int32_t>(std::move(results));
}
void ControlCore::ClearSearch()
{
_terminal->AlwaysNotifyOnBufferRotation(false);
_searcher = {};
}
@@ -1866,6 +1903,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->Write(hstr);
// Start the throttled update of where our hyperlinks are.
const auto shared = _shared.lock_shared();
if (shared->updatePatternLocations)
{

View File

@@ -106,6 +106,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::hstring FontFaceName() const noexcept;
uint16_t FontWeight() const noexcept;
til::color ForegroundColor() const;
til::color BackgroundColor() const;
void SendInput(const winrt::hstring& wstr);
@@ -208,6 +209,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive);
void ClearSearch();
Windows::Foundation::Collections::IVector<int32_t> SearchResultRows();
void LeftClickOnTerminal(const til::point terminalPosition,
const int numberOfClicks,
const bool altEnabled,

View File

@@ -127,8 +127,12 @@ namespace Microsoft.Terminal.Control
Microsoft.Terminal.Core.Point CursorPosition { get; };
void ResumeRendering();
void BlinkAttributeTick();
void Search(String text, Boolean goForward, Boolean caseSensitive);
void ClearSearch();
IVector<Int32> SearchResultRows { get; };
Microsoft.Terminal.Core.Color ForegroundColor { get; };
Microsoft.Terminal.Core.Color BackgroundColor { get; };
SelectionData SelectionInfo { get; };

View File

@@ -176,6 +176,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
WINRT_PROPERTY(bool, FoundMatch);
WINRT_PROPERTY(int32_t, TotalMatches);
WINRT_PROPERTY(int32_t, CurrentMatch);
};
struct ShowWindowArgs : public ShowWindowArgsT<ShowWindowArgs>

View File

@@ -82,6 +82,8 @@ namespace Microsoft.Terminal.Control
runtimeclass FoundResultsArgs
{
Boolean FoundMatch { get; };
Int32 TotalMatches { get; };
Int32 CurrentMatch { get; };
}
runtimeclass ShowWindowArgs

View File

@@ -178,6 +178,18 @@
<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="TermControl_NumResults" xml:space="preserve">
<value>{0}/{1}</value>
<comment>Will be displayed to indicate what result the user has selected, of how many total results. {0} will be replaced with the index of the current result. {1} will be replaced with the total number of results.</comment>
</data>
<data name="InvalidUri" xml:space="preserve">
<value>Invalid URI</value>
<comment>Whenever we encounter an invalid URI or URL we show this string as a warning.</comment>
@@ -276,4 +288,4 @@ Please either install the missing font or choose another one.</value>
<value>Select output</value>
<comment>The tooltip for a button for selecting all of a command's output</comment>
</data>
</root>
</root>

View File

@@ -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;
@@ -18,12 +19,26 @@ namespace winrt::Microsoft::Terminal::Control::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:
@@ -62,6 +77,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Enter)
{
// If the buttons are disabled, then don't allow enter to search either.
if (!GoForwardButton().IsEnabled() || !GoBackwardButton().IsEnabled())
{
return;
}
const auto state = CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift);
if (WI_IsFlagSet(state, CoreVirtualKeyStates::Down))
{
@@ -209,4 +230,137 @@ namespace winrt::Microsoft::Terminal::Control::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::wstring currentString;
std::wstring totalString;
if (currentMatch < 0 || currentMatch > (MaximumTotalResultsToShowInStatus - 1))
{
currentString = CurrentIndexTooHighStatus;
}
else
{
currentString = fmt::format(L"{}", currentMatch + 1);
}
if (totalMatches > MaximumTotalResultsToShowInStatus)
{
totalString = TotalResultsTooHighStatus;
}
else
{
totalString = fmt::format(L"{}", totalMatches);
}
return winrt::hstring{ fmt::format(RS_(L"TermControl_NumResults").c_str(), currentString, totalString) };
}
// 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);
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);
}
}

View File

@@ -21,6 +21,11 @@ namespace winrt::Microsoft::Terminal::Control::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(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
@@ -28,17 +33,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SetFocusOnTextbox();
void PopulateTextbox(const winrt::hstring& text);
bool ContainsFocus();
void SetStatus(int32_t totalMatches, int32_t currentMatch);
void SetNavigationEnabled(bool enabled);
void GoBackwardClicked(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::RoutedEventArgs& /*e*/);
void GoForwardClicked(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::RoutedEventArgs& /*e*/);
void CloseClick(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::RoutedEventArgs& 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, Control::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(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);

View File

@@ -11,8 +11,11 @@ namespace Microsoft.Terminal.Control
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;
}
}

View File

@@ -161,7 +161,15 @@
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsSpellCheckEnabled="False"
KeyDown="TextBoxKeyDown" />
KeyDown="TextBoxKeyDown"
TextChanged="TextBoxTextChanged" />
<TextBlock x:Name="StatusBox"
x:Uid="SearchBox_StatusBox"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="15" />
<ToggleButton x:Name="GoBackwardButton"
x:Uid="SearchBox_SearchBackwards"
@@ -195,7 +203,8 @@
Height="32"
Margin="4,0"
Padding="0"
BackgroundSizing="OuterBorderEdge">
BackgroundSizing="OuterBorderEdge"
Click="CaseSensitivityButtonClicked">
<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>

View File

@@ -302,6 +302,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto fullHeight{ ScrollBarCanvas().ActualHeight() };
const auto totalBufferRows{ update.newMaximum + update.newViewportSize };
auto drawPip = [&](const auto row, const auto rightAlign, const auto& brush) {
Windows::UI::Xaml::Shapes::Rectangle r;
r.Fill(brush);
r.Width(16.0f / 3.0f); // pip width - 1/3rd of the scrollbar width.
r.Height(2);
const auto fractionalHeight = row / totalBufferRows;
const auto relativePos = fractionalHeight * fullHeight;
ScrollBarCanvas().Children().Append(r);
Windows::UI::Xaml::Controls::Canvas::SetTop(r, relativePos);
if (rightAlign)
{
Windows::UI::Xaml::Controls::Canvas::SetLeft(r, 16.0f * .66f);
}
};
for (const auto m : marks)
{
Windows::UI::Xaml::Shapes::Rectangle r;
@@ -312,14 +327,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// pre-evaluate that for us, and shove the real value into the
// Color member, regardless if the mark has a literal value set.
brush.Color(static_cast<til::color>(m.Color.Color));
r.Fill(brush);
r.Width(16.0f / 3.0f); // pip width - 1/3rd of the scrollbar width.
r.Height(2);
const auto markRow = m.Start.Y;
const auto fractionalHeight = markRow / totalBufferRows;
const auto relativePos = fractionalHeight * fullHeight;
ScrollBarCanvas().Children().Append(r);
Windows::UI::Xaml::Controls::Canvas::SetTop(r, relativePos);
drawPip(m.Start.Y, false, brush);
}
if (_searchBox)
{
const auto searchMatches{ _core.SearchResultRows() };
if (searchMatches.Size() > 0 && _searchBox->Visibility() == Visibility::Visible)
{
const til::color fgColor{ _core.ForegroundColor() };
Media::SolidColorBrush searchMarkBrush{};
searchMarkBrush.Color(fgColor);
for (const auto m : searchMatches)
{
drawPip(m, true, searchMarkBrush);
}
}
}
}
}
@@ -390,6 +413,7 @@ namespace winrt::Microsoft::Terminal::Control::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
// - goForward: boolean that represents if the current search direction is forward
@@ -403,6 +427,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.Search(text, goForward, caseSensitive);
}
// 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:
// - <none>
void TermControl::_SearchChanged(const winrt::hstring& text,
const bool goForward,
const bool caseSensitive)
{
if (_searchBox && _searchBox->Visibility() == Visibility::Visible)
{
_core.Search(text, goForward, caseSensitive);
}
}
// Method Description:
// - The handler for the close button or pressing "Esc" when focusing on the
// search dialog.
@@ -3420,8 +3462,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - args: contains information about the results that were or were not found.
// Return Value:
// - <none>
void TermControl::_coreFoundMatch(const IInspectable& /*sender*/, const Control::FoundResultsArgs& args)
winrt::fire_and_forget TermControl::_coreFoundMatch(const IInspectable& /*sender*/, Control::FoundResultsArgs args)
{
co_await wil::resume_foreground(Dispatcher());
if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(*this) })
{
automationPeer.RaiseNotificationEvent(
@@ -3430,6 +3473,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
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 */);
}
// Manually send a scrollbar update, now, on the UI thread. We're
// already UI-driven, so that's okay. We're not really changing the
// scrollbar, but we do want to update the position of any marks. The
// Core might send a scrollbar updated event too, but if the first
// search hit is in the visible viewport, then the pips won't display
// until the user first scrolls.
auto scrollBar = ScrollBar();
ScrollBarUpdate update{
.newValue = scrollBar.Value(),
.newMaximum = scrollBar.Maximum(),
.newMinimum = scrollBar.Minimum(),
.newViewportSize = scrollBar.ViewportSize(),
};
_throttledUpdateScrollbar(update);
if (_searchBox)
{
_searchBox->SetStatus(args.TotalMatches(), args.CurrentMatch());
_searchBox->SetNavigationEnabled(true);
}
}
void TermControl::OwningHwnd(uint64_t owner)

View File

@@ -274,6 +274,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _UpdateSettingsFromUIThread();
void _UpdateAppearanceFromUIThread(Control::IControlAppearance newAppearance);
void _ApplyUISettings();
winrt::fire_and_forget UpdateAppearance(Control::IControlAppearance newAppearance);
void _SetBackgroundImage(const IControlAppearance& newAppearance);
@@ -343,6 +344,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const;
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, const Windows::UI::Xaml::RoutedEventArgs& args);
// TSFInputControl Handlers
@@ -357,7 +360,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);
winrt::fire_and_forget _coreFoundMatch(const IInspectable& sender, Control::FoundResultsArgs args);
til::point _toPosInDips(const Core::Point terminalCellPos);
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);

View File

@@ -1302,6 +1302,7 @@
x:Load="False"
Closed="_CloseSearchBoxControl"
Search="_Search"
SearchChanged="_SearchChanged"
Visibility="Collapsed" />
</Grid>

View File

@@ -16,6 +16,7 @@
#include "../../cascadia/terminalcore/ITerminalInput.hpp"
#include <til/ticket_lock.h>
#include <til/winrt.h>
inline constexpr size_t TaskbarMinProgress{ 10 };
@@ -118,6 +119,8 @@ public:
const til::point& end,
const bool fromUi);
til::property<bool> AlwaysNotifyOnBufferRotation;
std::wstring_view CurrentCommand() const;
#pragma region ITerminalApi

View File

@@ -472,7 +472,7 @@ void Terminal::NotifyBufferRotation(const int delta)
const auto oldScrollOffset = _scrollOffset;
_PreserveUserScrollOffset(delta);
if (_scrollOffset != oldScrollOffset || hasScrollMarks)
if (_scrollOffset != oldScrollOffset || hasScrollMarks || AlwaysNotifyOnBufferRotation())
{
_NotifyScrollEvent();
}

View File

@@ -154,6 +154,13 @@ void ScrollTest::TestNotifyScrolling()
// SHRT_MAX
// - Have a selection
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Data:notifyOnCircling", L"{false, true}")
END_TEST_METHOD_PROPERTIES();
INIT_TEST_PROPERTY(bool, notifyOnCircling, L"Controls whether we should always request scroll notifications");
_term->AlwaysNotifyOnBufferRotation(notifyOnCircling);
Log::Comment(L"Watch out - this test takes a while to run, and won't "
L"output anything unless in encounters an error. This is expected.");
@@ -180,10 +187,12 @@ void ScrollTest::TestNotifyScrolling()
// causes the first scroll event
auto scrolled = currentRow >= TerminalViewHeight - 1;
// When we circle the buffer, the scroll bar's position does not
// change.
// When we circle the buffer, the scroll bar's position does not change.
// However, as of GH#14045, we will send a notification IF the control
// requested on (by setting AlwaysNotifyOnBufferRotation)
auto circledBuffer = currentRow >= totalBufferSize - 1;
auto expectScrollBarNotification = scrolled && !circledBuffer;
auto expectScrollBarNotification = (scrolled && !circledBuffer) || // If we scrolled, but didn't circle the buffer OR
(circledBuffer && notifyOnCircling); // we circled AND we asked for notifications.
if (expectScrollBarNotification)
{

View File

@@ -13,7 +13,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
property& operator=(const property& other) = default;
T operator()() const
T operator()() const noexcept
{
return _value;
}
@@ -23,11 +23,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
}
explicit operator bool() const noexcept
{
#ifdef WINRT_Windows_Foundation_H
if constexpr (std::is_same_v<T, winrt::hstring>)
{
return !_value.empty();
}
else
#endif
{
return _value;
}