mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Fix search highlights during reflow (#17092)
This PR extends `til::throttled_func` to also support debouncing: * throttling: "At most 1 call every N seconds" * debouncing: "Exactly 1 call after N seconds of inactivity" Based on the latter the following series of changes were made: * An `OutputIdle` event was added to `ControlCore` which is raised once there hasn't been any incoming data in 100ms. This also triggers an update of our regex patterns (URL detection). * The event is then caught by `TermControl` which calls `Search()`. * `Search()` in turn was modified to return its results by-value as a struct, which avoids the need for a search-update event and simplifies how we update the UI. This architectural change, most importantly the removal of the `TextLayoutUpdated` event, fixes a DoS bug in Windows Terminal: As the event leads to UI thread activity, printing lots of text continuously results in the UI thread becoming unresponsive. On top of these, a number of improvements were made: * `IRenderEngine::InvalidateHighlight` was changed to take the `TextBuffer` by-reference which avoids the need to accumulate the line renditions in a `std::vector` first. This improves Debug build performance during reflow by what I guess must be roughly a magnitude faster. This difference is very noticeable. * When closing the search box, `ClearSearch()` is called to remove the highlights. The search text is restored when it's reopened, however the current search position isn't. Closes #17073 Closes #17089 ## Validation Steps Performed * UIA announcements: * Pressing Ctrl+Shift+F the first time does not lead to one ✅ * Typing the first letter does ✅ * Closing doesn't ✅ * Reopening does (as it restores the letter) ✅ * Closing the search box dismisses the highlights ✅ * Resizing the window recalculates the highlights ✅ * Changing the terminal output while the box is open recalculates the highlights ✅
This commit is contained in:
parent
87a9f72b9a
commit
360e86b536
@ -13,7 +13,8 @@ bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, c
|
||||
const auto& textBuffer = renderData.GetTextBuffer();
|
||||
const auto lastMutationId = textBuffer.GetLastMutationId();
|
||||
|
||||
if (_needle == needle &&
|
||||
if (_renderData == &renderData &&
|
||||
_needle == needle &&
|
||||
_caseInsensitive == caseInsensitive &&
|
||||
_lastMutationId == lastMutationId)
|
||||
{
|
||||
@ -21,15 +22,16 @@ bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, c
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prevResults)
|
||||
{
|
||||
*prevResults = std::move(_results);
|
||||
}
|
||||
|
||||
_renderData = &renderData;
|
||||
_needle = needle;
|
||||
_caseInsensitive = caseInsensitive;
|
||||
_lastMutationId = lastMutationId;
|
||||
|
||||
if (prevResults)
|
||||
{
|
||||
*prevResults = std::move(_results);
|
||||
}
|
||||
_results = textBuffer.SearchText(needle, caseInsensitive);
|
||||
_index = reverse ? gsl::narrow_cast<ptrdiff_t>(_results.size()) - 1 : 0;
|
||||
_step = reverse ? -1 : 1;
|
||||
@ -142,4 +144,4 @@ const std::vector<til::point_span>& Search::Results() const noexcept
|
||||
ptrdiff_t Search::CurrentMatch() const noexcept
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,19 +29,6 @@ using namespace winrt::Windows::Graphics::Display;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
|
||||
// The minimum delay between updates to the scroll bar's values.
|
||||
// The updates are throttled to limit power usage.
|
||||
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
|
||||
|
||||
// The minimum delay between updating the TSF input control.
|
||||
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) noexcept
|
||||
@ -117,9 +104,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
auto pfnShowWindowChanged = std::bind(&ControlCore::_terminalShowWindowChanged, this, std::placeholders::_1);
|
||||
_terminal->SetShowWindowCallback(pfnShowWindowChanged);
|
||||
|
||||
auto pfnTextLayoutUpdated = std::bind(&ControlCore::_terminalTextLayoutUpdated, this);
|
||||
_terminal->SetTextLayoutUpdatedCallback(pfnTextLayoutUpdated);
|
||||
|
||||
auto pfnPlayMidiNote = std::bind(&ControlCore::_terminalPlayMidiNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
_terminal->SetPlayMidiNoteCallback(pfnPlayMidiNote);
|
||||
|
||||
@ -167,21 +151,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_dispatcher = controller.DispatcherQueue();
|
||||
}
|
||||
|
||||
// A few different events should be throttled, so they don't fire absolutely all the time:
|
||||
// * _updatePatternLocations: When there's new output, or we scroll the
|
||||
// viewport, we should re-check if there are any visible hyperlinks.
|
||||
// But we don't really need to do this every single time text is
|
||||
// output, we can limit this update to once every 500ms.
|
||||
// * _updateScrollBar: Same idea as the TSF update - we don't _really_
|
||||
// need to hop across the process boundary every time text is output.
|
||||
// We can throttle this to once every 8ms, which will get us out of
|
||||
// the way of the main output & rendering threads.
|
||||
const auto shared = _shared.lock();
|
||||
// Raises an OutputIdle event once there hasn't been any output for at least 100ms.
|
||||
// It also updates all regex patterns in the viewport.
|
||||
//
|
||||
// NOTE: Calling UpdatePatternLocations from a background
|
||||
// thread is a workaround for us to hit GH#12607 less often.
|
||||
shared->updatePatternLocations = std::make_unique<til::throttled_func_trailing<>>(
|
||||
UpdatePatternLocationsInterval,
|
||||
[weakTerminal = std::weak_ptr{ _terminal }]() {
|
||||
shared->outputIdle = std::make_unique<til::debounced_func_trailing<>>(
|
||||
std::chrono::milliseconds{ 100 },
|
||||
[weakTerminal = std::weak_ptr{ _terminal }, weakThis = get_weak(), dispatcher = _dispatcher]() {
|
||||
dispatcher.TryEnqueue(DispatcherQueuePriority::Normal, [weakThis]() {
|
||||
if (const auto self = weakThis.get(); !self->_IsClosing())
|
||||
{
|
||||
self->OutputIdle.raise(*self, nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
if (const auto t = weakTerminal.lock())
|
||||
{
|
||||
const auto lock = t->LockForWriting();
|
||||
@ -189,9 +174,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
});
|
||||
|
||||
// Scrollbar updates are also expensive (XAML), so we'll throttle them as well.
|
||||
shared->updateScrollBar = std::make_shared<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>>(
|
||||
_dispatcher,
|
||||
ScrollBarUpdateInterval,
|
||||
std::chrono::milliseconds{ 8 },
|
||||
[weakThis = get_weak()](const auto& update) {
|
||||
if (auto core{ weakThis.get() }; !core->_IsClosing())
|
||||
{
|
||||
@ -218,7 +204,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// thread. These will be recreated in _setupDispatcherAndCallbacks, when
|
||||
// we're re-attached to a new control (on a possibly new UI thread).
|
||||
const auto shared = _shared.lock();
|
||||
shared->updatePatternLocations.reset();
|
||||
shared->outputIdle.reset();
|
||||
shared->updateScrollBar.reset();
|
||||
}
|
||||
|
||||
@ -671,9 +657,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
const auto shared = _shared.lock_shared();
|
||||
if (shared->updatePatternLocations)
|
||||
if (shared->outputIdle)
|
||||
{
|
||||
(*shared->updatePatternLocations)();
|
||||
(*shared->outputIdle)();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1100,12 +1086,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// If this function succeeds with S_FALSE, then the terminal didn't
|
||||
// actually change size. No need to notify the connection of this no-op.
|
||||
const auto hr = _terminal->UserResize({ vp.Width(), vp.Height() });
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE)
|
||||
if (FAILED(hr) || hr == S_FALSE)
|
||||
{
|
||||
_connection.Resize(vp.Height(), vp.Width());
|
||||
return;
|
||||
}
|
||||
|
||||
// let the UI know that the text layout has been updated
|
||||
_terminal->NotifyTextLayoutUpdated();
|
||||
_connection.Resize(vp.Height(), vp.Width());
|
||||
|
||||
// TermControl will call Search() once the OutputIdle even fires after 100ms.
|
||||
// Until then we need to hide the now-stale search results from the renderer.
|
||||
ClearSearch();
|
||||
const auto shared = _shared.lock_shared();
|
||||
if (shared->outputIdle)
|
||||
{
|
||||
(*shared->outputIdle)();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1603,16 +1597,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
ShowWindowChanged.raise(*this, *showWindow);
|
||||
}
|
||||
|
||||
void ControlCore::_terminalTextLayoutUpdated()
|
||||
{
|
||||
ClearSearch();
|
||||
|
||||
// send an UpdateSearchResults event to the UI to put the Search UI into inactive state.
|
||||
auto evArgs = winrt::make_self<implementation::UpdateSearchResultsEventArgs>();
|
||||
evArgs->State(SearchState::Inactive);
|
||||
UpdateSearchResults.raise(*this, *evArgs);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Plays a single MIDI note, blocking for the duration.
|
||||
// Arguments:
|
||||
@ -1672,13 +1656,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - caseSensitive: boolean that represents if the current search is case sensitive
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ControlCore::Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
|
||||
SearchResults ControlCore::Search(const std::wstring_view& text, const bool goForward, const bool caseSensitive, const bool reset)
|
||||
{
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
|
||||
bool searchInvalidated = false;
|
||||
std::vector<til::point_span> oldResults;
|
||||
if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive, &oldResults))
|
||||
{
|
||||
searchInvalidated = true;
|
||||
|
||||
_cachedSearchResultRows = {};
|
||||
if (SnapSearchResultToSelection())
|
||||
{
|
||||
@ -1687,30 +1674,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
_terminal->SetSearchHighlights(_searcher.Results());
|
||||
_terminal->SetSearchHighlightFocused(_searcher.CurrentMatch());
|
||||
}
|
||||
else
|
||||
else if (!reset)
|
||||
{
|
||||
_searcher.FindNext();
|
||||
_terminal->SetSearchHighlightFocused(_searcher.CurrentMatch());
|
||||
}
|
||||
|
||||
int32_t totalMatches = 0;
|
||||
int32_t currentMatch = 0;
|
||||
if (const auto idx = _searcher.CurrentMatch(); idx >= 0)
|
||||
{
|
||||
totalMatches = gsl::narrow<int32_t>(_searcher.Results().size());
|
||||
currentMatch = gsl::narrow<int32_t>(idx);
|
||||
_terminal->SetSearchHighlightFocused(gsl::narrow<size_t>(idx));
|
||||
}
|
||||
|
||||
_renderer->TriggerSearchHighlight(oldResults);
|
||||
|
||||
auto evArgs = winrt::make_self<implementation::UpdateSearchResultsEventArgs>();
|
||||
if (!text.empty())
|
||||
{
|
||||
evArgs->State(SearchState::Active);
|
||||
if (_searcher.GetCurrent())
|
||||
{
|
||||
evArgs->FoundMatch(true);
|
||||
evArgs->TotalMatches(gsl::narrow<int32_t>(_searcher.Results().size()));
|
||||
evArgs->CurrentMatch(gsl::narrow<int32_t>(_searcher.CurrentMatch()));
|
||||
}
|
||||
}
|
||||
|
||||
// Raise an UpdateSearchResults event, which the control will use to update the
|
||||
// UI and notify the narrator about the updated search results in the buffer
|
||||
UpdateSearchResults.raise(*this, *evArgs);
|
||||
return {
|
||||
.TotalMatches = totalMatches,
|
||||
.CurrentMatch = currentMatch,
|
||||
.SearchInvalidated = searchInvalidated,
|
||||
};
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IVector<int32_t> ControlCore::SearchResultRows()
|
||||
@ -1738,16 +1723,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlCore::ClearSearch()
|
||||
{
|
||||
// nothing to clear if there's no results
|
||||
if (_searcher.GetCurrent())
|
||||
{
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_terminal->SetSearchHighlights({});
|
||||
_terminal->SetSearchHighlightFocused({});
|
||||
_renderer->TriggerSearchHighlight(_searcher.Results());
|
||||
_searcher = {};
|
||||
_cachedSearchResultRows = {};
|
||||
}
|
||||
const auto lock = _terminal->LockForWriting();
|
||||
_terminal->SetSearchHighlights({});
|
||||
_terminal->SetSearchHighlightFocused({});
|
||||
_renderer->TriggerSearchHighlight(_searcher.Results());
|
||||
_searcher = {};
|
||||
_cachedSearchResultRows = {};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@ -2130,9 +2111,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
// Start the throttled update of where our hyperlinks are.
|
||||
const auto shared = _shared.lock_shared();
|
||||
if (shared->updatePatternLocations)
|
||||
if (shared->outputIdle)
|
||||
{
|
||||
(*shared->updatePatternLocations)();
|
||||
(*shared->outputIdle)();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
|
||||
@ -219,7 +219,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void SetSelectionAnchor(const til::point position);
|
||||
void SetEndSelectionPoint(const til::point position);
|
||||
|
||||
void Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive);
|
||||
SearchResults Search(const std::wstring_view& text, bool goForward, bool caseSensitive, bool reset);
|
||||
void ClearSearch();
|
||||
void SnapSearchResultToSelection(bool snap) noexcept;
|
||||
bool SnapSearchResultToSelection() const noexcept;
|
||||
@ -279,8 +279,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
til::typed_event<IInspectable, Control::RendererWarningArgs> RendererWarning;
|
||||
til::typed_event<IInspectable, Control::NoticeEventArgs> RaiseNotice;
|
||||
til::typed_event<IInspectable, Control::TransparencyChangedEventArgs> TransparencyChanged;
|
||||
til::typed_event<> ReceivedOutput;
|
||||
til::typed_event<IInspectable, Control::UpdateSearchResultsEventArgs> UpdateSearchResults;
|
||||
til::typed_event<> OutputIdle;
|
||||
til::typed_event<IInspectable, Control::ShowWindowArgs> ShowWindowChanged;
|
||||
til::typed_event<IInspectable, Control::UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
|
||||
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
|
||||
@ -295,7 +294,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
private:
|
||||
struct SharedState
|
||||
{
|
||||
std::unique_ptr<til::throttled_func_trailing<>> updatePatternLocations;
|
||||
std::unique_ptr<til::debounced_func_trailing<>> outputIdle;
|
||||
std::shared_ptr<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>> updateScrollBar;
|
||||
};
|
||||
|
||||
@ -376,7 +375,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const int bufferSize);
|
||||
void _terminalTaskbarProgressChanged();
|
||||
void _terminalShowWindowChanged(bool showOrHide);
|
||||
void _terminalTextLayoutUpdated();
|
||||
void _terminalPlayMidiNote(const int noteNumber,
|
||||
const int velocity,
|
||||
const std::chrono::microseconds duration);
|
||||
|
||||
@ -49,6 +49,13 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean EndAtRightBoundary;
|
||||
};
|
||||
|
||||
struct SearchResults
|
||||
{
|
||||
Int32 TotalMatches;
|
||||
Int32 CurrentMatch;
|
||||
Boolean SearchInvalidated;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SelectionColor
|
||||
{
|
||||
SelectionColor();
|
||||
@ -127,7 +134,7 @@ namespace Microsoft.Terminal.Control
|
||||
void ResumeRendering();
|
||||
void BlinkAttributeTick();
|
||||
|
||||
void Search(String text, Boolean goForward, Boolean caseSensitive);
|
||||
SearchResults Search(String text, Boolean goForward, Boolean caseSensitive, Boolean reset);
|
||||
void ClearSearch();
|
||||
IVector<Int32> SearchResultRows { get; };
|
||||
Boolean SnapSearchResultToSelection;
|
||||
@ -177,8 +184,7 @@ namespace Microsoft.Terminal.Control
|
||||
event Windows.Foundation.TypedEventHandler<Object, RendererWarningArgs> RendererWarning;
|
||||
event Windows.Foundation.TypedEventHandler<Object, NoticeEventArgs> RaiseNotice;
|
||||
event Windows.Foundation.TypedEventHandler<Object, TransparencyChangedEventArgs> TransparencyChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ReceivedOutput;
|
||||
event Windows.Foundation.TypedEventHandler<Object, UpdateSearchResultsEventArgs> UpdateSearchResults;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> OutputIdle;
|
||||
event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
|
||||
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> CloseTerminalRequested;
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
#include "ScrollPositionChangedArgs.g.cpp"
|
||||
#include "RendererWarningArgs.g.cpp"
|
||||
#include "TransparencyChangedEventArgs.g.cpp"
|
||||
#include "UpdateSearchResultsEventArgs.g.cpp"
|
||||
#include "ShowWindowArgs.g.cpp"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.cpp"
|
||||
#include "CompletionsChangedEventArgs.g.cpp"
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
#include "ScrollPositionChangedArgs.g.h"
|
||||
#include "RendererWarningArgs.g.h"
|
||||
#include "TransparencyChangedEventArgs.g.h"
|
||||
#include "UpdateSearchResultsEventArgs.g.h"
|
||||
#include "ShowWindowArgs.g.h"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.h"
|
||||
#include "CompletionsChangedEventArgs.g.h"
|
||||
@ -141,17 +140,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
WINRT_PROPERTY(float, Opacity);
|
||||
};
|
||||
|
||||
struct UpdateSearchResultsEventArgs : public UpdateSearchResultsEventArgsT<UpdateSearchResultsEventArgs>
|
||||
{
|
||||
public:
|
||||
UpdateSearchResultsEventArgs() = default;
|
||||
|
||||
WINRT_PROPERTY(SearchState, State, SearchState::Inactive);
|
||||
WINRT_PROPERTY(bool, FoundMatch);
|
||||
WINRT_PROPERTY(int32_t, TotalMatches);
|
||||
WINRT_PROPERTY(int32_t, CurrentMatch);
|
||||
};
|
||||
|
||||
struct ShowWindowArgs : public ShowWindowArgsT<ShowWindowArgs>
|
||||
{
|
||||
public:
|
||||
|
||||
@ -84,14 +84,6 @@ namespace Microsoft.Terminal.Control
|
||||
Active = 1,
|
||||
};
|
||||
|
||||
runtimeclass UpdateSearchResultsEventArgs
|
||||
{
|
||||
SearchState State { get; };
|
||||
Boolean FoundMatch { get; };
|
||||
Int32 TotalMatches { get; };
|
||||
Int32 CurrentMatch { get; };
|
||||
}
|
||||
|
||||
runtimeclass ShowWindowArgs
|
||||
{
|
||||
Boolean ShowOrHide { get; };
|
||||
|
||||
@ -195,6 +195,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
winrt::hstring SearchBoxControl::Text()
|
||||
{
|
||||
return TextBox().Text();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Check if the current search direction is forward
|
||||
// Arguments:
|
||||
@ -202,7 +207,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Return Value:
|
||||
// - bool: the current search direction, determined by the
|
||||
// states of the two direction buttons
|
||||
bool SearchBoxControl::_GoForward()
|
||||
bool SearchBoxControl::GoForward()
|
||||
{
|
||||
return GoForwardButton().IsChecked().GetBoolean();
|
||||
}
|
||||
@ -214,7 +219,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Return Value:
|
||||
// - bool: whether the current search is case sensitive (case button is checked )
|
||||
// or not
|
||||
bool SearchBoxControl::_CaseSensitive()
|
||||
bool SearchBoxControl::CaseSensitive()
|
||||
{
|
||||
return CaseSensitivityButton().IsChecked().GetBoolean();
|
||||
}
|
||||
@ -240,11 +245,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto state = CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift);
|
||||
if (WI_IsFlagSet(state, CoreVirtualKeyStates::Down))
|
||||
{
|
||||
Search.raise(TextBox().Text(), !_GoForward(), _CaseSensitive());
|
||||
Search.raise(Text(), !GoForward(), CaseSensitive());
|
||||
}
|
||||
else
|
||||
{
|
||||
Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive());
|
||||
Search.raise(Text(), GoForward(), CaseSensitive());
|
||||
}
|
||||
e.Handled(true);
|
||||
}
|
||||
@ -335,7 +340,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
// kick off search
|
||||
Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive());
|
||||
Search.raise(Text(), GoForward(), CaseSensitive());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@ -356,7 +361,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
// kick off search
|
||||
Search.raise(TextBox().Text(), _GoForward(), _CaseSensitive());
|
||||
Search.raise(Text(), GoForward(), CaseSensitive());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@ -394,7 +399,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - <none>
|
||||
void SearchBoxControl::TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/)
|
||||
{
|
||||
SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive());
|
||||
SearchChanged.raise(Text(), GoForward(), CaseSensitive());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@ -406,7 +411,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - <none>
|
||||
void SearchBoxControl::CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/)
|
||||
{
|
||||
SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive());
|
||||
SearchChanged.raise(Text(), GoForward(), CaseSensitive());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@ -528,20 +533,4 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
StatusBox().Text(L"");
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enables / disables results navigation buttons
|
||||
// Arguments:
|
||||
// - enable: if true, the buttons should be enabled
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void SearchBoxControl::NavigationEnabled(bool enabled)
|
||||
{
|
||||
GoBackwardButton().IsEnabled(enabled);
|
||||
GoForwardButton().IsEnabled(enabled);
|
||||
}
|
||||
bool SearchBoxControl::NavigationEnabled()
|
||||
{
|
||||
return GoBackwardButton().IsEnabled() || GoForwardButton().IsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,13 +35,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void Open(std::function<void()> callback);
|
||||
void Close();
|
||||
|
||||
winrt::hstring Text();
|
||||
bool GoForward();
|
||||
bool CaseSensitive();
|
||||
void SetFocusOnTextbox();
|
||||
void PopulateTextbox(const winrt::hstring& text);
|
||||
bool ContainsFocus();
|
||||
void SetStatus(int32_t totalMatches, int32_t currentMatch);
|
||||
void ClearStatus();
|
||||
bool NavigationEnabled();
|
||||
void NavigationEnabled(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*/);
|
||||
@ -77,8 +78,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
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);
|
||||
void _CharacterHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::Input::CharacterReceivedRoutedEventArgs& e);
|
||||
};
|
||||
|
||||
@ -16,8 +16,6 @@ namespace Microsoft.Terminal.Control
|
||||
Windows.Foundation.Rect ContentClipRect{ get; };
|
||||
Double OpenAnimationStartPoint{ get; };
|
||||
|
||||
Boolean NavigationEnabled;
|
||||
|
||||
event SearchHandler Search;
|
||||
event SearchHandler SearchChanged;
|
||||
event Windows.Foundation.TypedEventHandler<SearchBoxControl, Windows.UI.Xaml.RoutedEventArgs> Closed;
|
||||
|
||||
@ -204,7 +204,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_revokers.TransparencyChanged = _core.TransparencyChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreTransparencyChanged });
|
||||
_revokers.RaiseNotice = _core.RaiseNotice(winrt::auto_revoke, { get_weak(), &TermControl::_coreRaisedNotice });
|
||||
_revokers.HoveredHyperlinkChanged = _core.HoveredHyperlinkChanged(winrt::auto_revoke, { get_weak(), &TermControl::_hoveredHyperlinkChanged });
|
||||
_revokers.UpdateSearchResults = _core.UpdateSearchResults(winrt::auto_revoke, { get_weak(), &TermControl::_coreUpdateSearchResults });
|
||||
_revokers.OutputIdle = _core.OutputIdle(winrt::auto_revoke, { get_weak(), &TermControl::_coreOutputIdle });
|
||||
_revokers.UpdateSelectionMarkers = _core.UpdateSelectionMarkers(winrt::auto_revoke, { get_weak(), &TermControl::_updateSelectionMarkers });
|
||||
_revokers.coreOpenHyperlink = _core.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler });
|
||||
_revokers.interactivityOpenHyperlink = _interactivity.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler });
|
||||
@ -534,13 +534,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// but since code paths differ, extra work is required to ensure correctness.
|
||||
if (!_core.HasMultiLineSelection())
|
||||
{
|
||||
_core.SnapSearchResultToSelection(true);
|
||||
const auto selectedLine{ _core.SelectedText(true) };
|
||||
_searchBox->PopulateTextbox(selectedLine);
|
||||
}
|
||||
}
|
||||
|
||||
_searchBox->Open([searchBox]() { searchBox.SetFocusOnTextbox(); });
|
||||
_searchBox->Open([weakThis = get_weak()]() {
|
||||
if (const auto self = weakThis.get(); !self->_IsClosing())
|
||||
{
|
||||
self->_searchBox->SetFocusOnTextbox();
|
||||
self->_refreshSearch();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -551,13 +556,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!_searchBox)
|
||||
if (!_searchBox || _searchBox->Visibility() != Visibility::Visible)
|
||||
{
|
||||
CreateSearchBoxControl();
|
||||
}
|
||||
else
|
||||
{
|
||||
_core.Search(_searchBox->TextBox().Text(), goForward, false);
|
||||
_handleSearchResults(_core.Search(_searchBox->Text(), goForward, _searchBox->CaseSensitive(), false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -588,7 +593,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const bool goForward,
|
||||
const bool caseSensitive)
|
||||
{
|
||||
_core.Search(text, goForward, caseSensitive);
|
||||
_handleSearchResults(_core.Search(text, goForward, caseSensitive, false));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@ -605,7 +610,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
if (_searchBox && _searchBox->Visibility() == Visibility::Visible)
|
||||
{
|
||||
_core.Search(text, goForward, caseSensitive);
|
||||
_handleSearchResults(_core.Search(text, goForward, caseSensitive, false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -621,6 +626,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
_searchBox->Close();
|
||||
_core.ClearSearch();
|
||||
|
||||
// Set focus back to terminal control
|
||||
this->Focus(FocusState::Programmatic);
|
||||
@ -3537,69 +3543,63 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return _core.SelectedText(trimTrailingWhitespace);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Triggers an update on scrollbar to redraw search scroll marks.
|
||||
// - Called when the search results have changed, and the scroll marks'
|
||||
// positions need to be updated.
|
||||
void TermControl::_UpdateSearchScrollMarks()
|
||||
void TermControl::_refreshSearch()
|
||||
{
|
||||
// Manually send a scrollbar update, 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 search marks. The Core
|
||||
// might send a scrollbar update 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->Visibility() != Visibility::Visible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto text = _searchBox->Text();
|
||||
if (text.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto goForward = _searchBox->GoForward();
|
||||
const auto caseSensitive = _searchBox->CaseSensitive();
|
||||
_handleSearchResults(_core.Search(text, goForward, caseSensitive, true));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the core raises a UpdateSearchResults event. That's done in response to:
|
||||
// - starting a search query with ControlCore::Search.
|
||||
// - clearing search results due to change in buffer content.
|
||||
// - 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 search state and results that were
|
||||
// or were not found.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TermControl::_coreUpdateSearchResults(const IInspectable& /*sender*/, Control::UpdateSearchResultsEventArgs args)
|
||||
void TermControl::_handleSearchResults(SearchResults results)
|
||||
{
|
||||
co_await wil::resume_foreground(Dispatcher());
|
||||
if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(*this) })
|
||||
if (!_searchBox)
|
||||
{
|
||||
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 */);
|
||||
return;
|
||||
}
|
||||
|
||||
_UpdateSearchScrollMarks();
|
||||
_searchBox->SetStatus(results.TotalMatches, results.CurrentMatch);
|
||||
|
||||
if (_searchBox)
|
||||
if (results.SearchInvalidated)
|
||||
{
|
||||
_searchBox->NavigationEnabled(true);
|
||||
if (args.State() == Control::SearchState::Inactive)
|
||||
if (_showMarksInScrollbar)
|
||||
{
|
||||
_searchBox->ClearStatus();
|
||||
const auto scrollBar = ScrollBar();
|
||||
ScrollBarUpdate update{
|
||||
.newValue = scrollBar.Value(),
|
||||
.newMaximum = scrollBar.Maximum(),
|
||||
.newMinimum = scrollBar.Minimum(),
|
||||
.newViewportSize = scrollBar.ViewportSize(),
|
||||
};
|
||||
_updateScrollBar->Run(update);
|
||||
}
|
||||
else
|
||||
|
||||
if (auto automationPeer{ FrameworkElementAutomationPeer::FromElement(*this) })
|
||||
{
|
||||
_searchBox->SetStatus(args.TotalMatches(), args.CurrentMatch());
|
||||
automationPeer.RaiseNotificationEvent(
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent,
|
||||
results.TotalMatches > 0 ? RS_(L"SearchBox_MatchesAvailable") : RS_(L"SearchBox_NoMatches"), // what to announce if results were found
|
||||
L"SearchBoxResultAnnouncement" /* unique name for this group of notifications */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TermControl::_coreOutputIdle(const IInspectable& /*sender*/, const IInspectable& /*args*/)
|
||||
{
|
||||
_refreshSearch();
|
||||
}
|
||||
|
||||
void TermControl::OwningHwnd(uint64_t owner)
|
||||
{
|
||||
_core.OwningHwnd(owner);
|
||||
|
||||
@ -371,10 +371,10 @@ 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);
|
||||
void _UpdateSearchScrollMarks();
|
||||
void _refreshSearch();
|
||||
void _handleSearchResults(SearchResults results);
|
||||
|
||||
void _hoveredHyperlinkChanged(const IInspectable& sender, const IInspectable& args);
|
||||
winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args);
|
||||
@ -383,7 +383,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);
|
||||
winrt::fire_and_forget _coreUpdateSearchResults(const IInspectable& sender, Control::UpdateSearchResultsEventArgs args);
|
||||
void _coreOutputIdle(const IInspectable& sender, const IInspectable& args);
|
||||
|
||||
til::point _toPosInDips(const Core::Point terminalCellPos);
|
||||
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
|
||||
@ -411,7 +411,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
Control::ControlCore::TransparencyChanged_revoker TransparencyChanged;
|
||||
Control::ControlCore::RaiseNotice_revoker RaiseNotice;
|
||||
Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged;
|
||||
Control::ControlCore::UpdateSearchResults_revoker UpdateSearchResults;
|
||||
Control::ControlCore::OutputIdle_revoker OutputIdle;
|
||||
Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers;
|
||||
Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink;
|
||||
Control::ControlCore::TitleChanged_revoker TitleChanged;
|
||||
|
||||
@ -1230,11 +1230,6 @@ void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::functi
|
||||
_pfnCompletionsChanged.swap(pfn);
|
||||
}
|
||||
|
||||
void Terminal::SetTextLayoutUpdatedCallback(std::function<void()> pfn) noexcept
|
||||
{
|
||||
_pfnTextLayoutUpdated.swap(pfn);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Stores the search highlighted regions in the terminal
|
||||
void Terminal::SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept
|
||||
|
||||
@ -155,7 +155,6 @@ public:
|
||||
bool IsVtInputEnabled() const noexcept override;
|
||||
void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override;
|
||||
void NotifyBufferRotation(const int delta) override;
|
||||
void NotifyTextLayoutUpdated() override;
|
||||
|
||||
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
|
||||
|
||||
@ -231,7 +230,6 @@ public:
|
||||
void SetShowWindowCallback(std::function<void(bool)> pfn) noexcept;
|
||||
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
|
||||
void CompletionsChangedCallback(std::function<void(std::wstring_view, unsigned int)> pfn) noexcept;
|
||||
void SetTextLayoutUpdatedCallback(std::function<void()> pfn) noexcept;
|
||||
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
|
||||
void SetSearchHighlightFocused(const size_t focusedIdx);
|
||||
|
||||
@ -341,7 +339,6 @@ private:
|
||||
std::function<void(bool)> _pfnShowWindowChanged;
|
||||
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
|
||||
std::function<void(std::wstring_view, unsigned int)> _pfnCompletionsChanged;
|
||||
std::function<void()> _pfnTextLayoutUpdated;
|
||||
|
||||
RenderSettings _renderSettings;
|
||||
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
|
||||
|
||||
@ -239,8 +239,6 @@ void Terminal::UseAlternateScreenBuffer(const TextAttribute& attrs)
|
||||
// Update scrollbars
|
||||
_NotifyScrollEvent();
|
||||
|
||||
NotifyTextLayoutUpdated();
|
||||
|
||||
// redraw the screen
|
||||
try
|
||||
{
|
||||
@ -298,8 +296,6 @@ void Terminal::UseMainScreenBuffer()
|
||||
// Update scrollbars
|
||||
_NotifyScrollEvent();
|
||||
|
||||
NotifyTextLayoutUpdated();
|
||||
|
||||
// redraw the screen
|
||||
_activeBuffer().TriggerRedrawAll();
|
||||
}
|
||||
@ -374,15 +370,3 @@ void Terminal::NotifyBufferRotation(const int delta)
|
||||
_NotifyScrollEvent();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Notifies the terminal UI layer that the text layout has changed.
|
||||
// - This will be called when new text is added, or when the text is
|
||||
// rearranged in the buffer due to window resize.
|
||||
void Terminal::NotifyTextLayoutUpdated()
|
||||
{
|
||||
if (_pfnTextLayoutUpdated)
|
||||
{
|
||||
_pfnTextLayoutUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,12 +424,7 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int delta)
|
||||
}
|
||||
}
|
||||
|
||||
void ConhostInternalGetSet::NotifyTextLayoutUpdated()
|
||||
{
|
||||
// Not implemented for conhost.
|
||||
}
|
||||
|
||||
void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/)
|
||||
{
|
||||
// Not implemented for conhost.
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,6 @@ public:
|
||||
|
||||
void NotifyAccessibilityChange(const til::rect& changedRect) override;
|
||||
void NotifyBufferRotation(const int delta) override;
|
||||
void NotifyTextLayoutUpdated() override;
|
||||
|
||||
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ namespace til
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
template<bool leading, typename... Args>
|
||||
template<bool Debounce, bool Leading, typename... Args>
|
||||
class throttled_func
|
||||
{
|
||||
public:
|
||||
@ -118,15 +118,35 @@ namespace til
|
||||
throttled_func& operator=(throttled_func&&) = delete;
|
||||
|
||||
// Throttles the invocation of the function passed to the constructor.
|
||||
// If this is a trailing_throttled_func:
|
||||
// If you call this function again before the underlying
|
||||
// timer has expired, the new arguments will be used.
|
||||
//
|
||||
// If Debounce is true and you call this function again before the
|
||||
// underlying timer has expired, its timeout will be reset.
|
||||
//
|
||||
// If Leading is true and you call this function again before the
|
||||
// underlying timer has expired, the new arguments will be used.
|
||||
template<typename... MakeArgs>
|
||||
void operator()(MakeArgs&&... args)
|
||||
{
|
||||
if (!_storage.emplace(std::forward<MakeArgs>(args)...))
|
||||
const auto hadValue = _storage.emplace(std::forward<MakeArgs>(args)...);
|
||||
|
||||
if constexpr (Debounce)
|
||||
{
|
||||
_leading_edge();
|
||||
SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hadValue)
|
||||
{
|
||||
SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (Leading)
|
||||
{
|
||||
if (!hadValue)
|
||||
{
|
||||
_func();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,19 +185,9 @@ namespace til
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
void _leading_edge()
|
||||
{
|
||||
if constexpr (leading)
|
||||
{
|
||||
_func();
|
||||
}
|
||||
|
||||
SetThreadpoolTimerEx(_timer.get(), &_delay, 0, 0);
|
||||
}
|
||||
|
||||
void _trailing_edge()
|
||||
{
|
||||
if constexpr (leading)
|
||||
if constexpr (Leading)
|
||||
{
|
||||
_storage.reset();
|
||||
}
|
||||
@ -187,7 +197,7 @@ namespace til
|
||||
}
|
||||
}
|
||||
|
||||
inline wil::unique_threadpool_timer _createTimer()
|
||||
wil::unique_threadpool_timer _createTimer()
|
||||
{
|
||||
wil::unique_threadpool_timer timer{ CreateThreadpoolTimer(&_timer_callback, this, nullptr) };
|
||||
THROW_LAST_ERROR_IF(!timer);
|
||||
@ -201,6 +211,10 @@ namespace til
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
using throttled_func_trailing = throttled_func<false, Args...>;
|
||||
using throttled_func_leading = throttled_func<true>;
|
||||
using throttled_func_trailing = throttled_func<false, false, Args...>;
|
||||
using throttled_func_leading = throttled_func<false, true>;
|
||||
|
||||
template<typename... Args>
|
||||
using debounced_func_trailing = throttled_func<true, false, Args...>;
|
||||
using debounced_func_leading = throttled_func<true, true>;
|
||||
} // namespace til
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "AtlasEngine.h"
|
||||
|
||||
#include "Backend.h"
|
||||
#include "../../buffer/out/textBuffer.hpp"
|
||||
#include "../base/FontCache.h"
|
||||
|
||||
// #### NOTE ####
|
||||
@ -95,7 +96,7 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2<T>& out) noexcept
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateHighlight(std::span<const til::point_span> highlights, const std::vector<LineRendition>& renditions) noexcept
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept
|
||||
{
|
||||
const auto viewportOrigin = til::point{ _api.s->viewportOffset.x, _api.s->viewportOffset.y };
|
||||
const auto viewport = til::rect{ 0, 0, _api.s->viewportCellCount.x, _api.s->viewportCellCount.y };
|
||||
@ -103,7 +104,7 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2<T>& out) noexcept
|
||||
for (const auto& hi : highlights)
|
||||
{
|
||||
hi.iterate_rows(cellCountX, [&](til::CoordType row, til::CoordType beg, til::CoordType end) {
|
||||
const auto shift = til::at(renditions, row) != LineRendition::SingleWidth ? 1 : 0;
|
||||
const auto shift = buffer.GetLineRendition(row) != LineRendition::SingleWidth ? 1 : 0;
|
||||
beg <<= shift;
|
||||
end <<= shift;
|
||||
til::rect rect{ beg, row, end + 1, row + 1 };
|
||||
|
||||
@ -33,7 +33,7 @@ namespace Microsoft::Console::Render::Atlas
|
||||
[[nodiscard]] HRESULT InvalidateCursor(const til::rect* psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSystem(const til::rect* prcDirtyClient) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const std::vector<LineRendition>& renditions) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept override;
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
using namespace Microsoft::Console;
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
[[nodiscard]] HRESULT RenderEngineBase::InvalidateHighlight(std::span<const til::point_span> /*highlights*/, const std::vector<LineRendition>& /*renditions*/) noexcept
|
||||
[[nodiscard]] HRESULT RenderEngineBase::InvalidateHighlight(std::span<const til::point_span> /*highlights*/, const TextBuffer& /*renditions*/) noexcept
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -459,24 +459,21 @@ void Renderer::TriggerSelection()
|
||||
void Renderer::TriggerSearchHighlight(const std::vector<til::point_span>& oldHighlights)
|
||||
try
|
||||
{
|
||||
const auto& buffer = _pData->GetTextBuffer();
|
||||
const auto rows = buffer.TotalRowCount();
|
||||
|
||||
std::vector<LineRendition> renditions;
|
||||
renditions.reserve(rows);
|
||||
for (til::CoordType row = 0; row < rows; ++row)
|
||||
{
|
||||
renditions.emplace_back(buffer.GetLineRendition(row));
|
||||
}
|
||||
|
||||
// no need to invalidate focused search highlight separately as they are
|
||||
// included in (all) search highlights.
|
||||
const auto newHighlights = _pData->GetSearchHighlights();
|
||||
|
||||
if (oldHighlights.empty() && newHighlights.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& buffer = _pData->GetTextBuffer();
|
||||
|
||||
FOREACH_ENGINE(pEngine)
|
||||
{
|
||||
LOG_IF_FAILED(pEngine->InvalidateHighlight(oldHighlights, renditions));
|
||||
LOG_IF_FAILED(pEngine->InvalidateHighlight(newHighlights, renditions));
|
||||
LOG_IF_FAILED(pEngine->InvalidateHighlight(oldHighlights, buffer));
|
||||
LOG_IF_FAILED(pEngine->InvalidateHighlight(newHighlights, buffer));
|
||||
}
|
||||
|
||||
NotifyPaintFrame();
|
||||
|
||||
@ -67,7 +67,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] virtual HRESULT InvalidateCursor(const til::rect* psrRegion) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateSystem(const til::rect* prcDirtyClient) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const std::vector<LineRendition>& renditions) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept = 0;
|
||||
|
||||
@ -24,7 +24,7 @@ namespace Microsoft::Console::Render
|
||||
class RenderEngineBase : public IRenderEngine
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const std::vector<LineRendition>& renditions) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept override;
|
||||
|
||||
@ -80,7 +80,6 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
|
||||
virtual void NotifyAccessibilityChange(const til::rect& changedRect) = 0;
|
||||
virtual void NotifyBufferRotation(const int delta) = 0;
|
||||
virtual void NotifyTextLayoutUpdated() = 0;
|
||||
|
||||
virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0;
|
||||
};
|
||||
|
||||
@ -182,7 +182,6 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string)
|
||||
// It's important to do this here instead of in TextBuffer, because here you
|
||||
// have access to the entire line of text, whereas TextBuffer writes it one
|
||||
// character at a time via the OutputCellIterator.
|
||||
_api.NotifyTextLayoutUpdated();
|
||||
textBuffer.TriggerNewTextNotification(string);
|
||||
}
|
||||
|
||||
|
||||
@ -215,11 +215,6 @@ public:
|
||||
Log::Comment(L"NotifyBufferRotation MOCK called...");
|
||||
}
|
||||
|
||||
void NotifyTextLayoutUpdated() override
|
||||
{
|
||||
Log::Comment(L"NotifyTextLayoutUpdated MOCK called...");
|
||||
}
|
||||
|
||||
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override
|
||||
{
|
||||
Log::Comment(L"InvokeCompletions MOCK called...");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user