Improve Search Highlighting (#16611)

### The changeset involves:
- Decoupling Selection and Search Highlighting code paths.
- We no longer invalidate search highlights when:
  - Left-clicking on terminal
  - A new selection is made
  - Left-clicking on Search-box
- Dispatching Find Next/Prev Match Action. (The search highlight was
removed after pressing the first key of the Action's key combination)
- And, anything that doesn't change buffer content, shouldn't invalidate
the highlighted region (E.g. Cursor movement)
- Highlighting foreground color is *actually* applied to the highlighted
text.
- Double-clicking on SearchBox no longer starts a text selection in the
terminal.
- Selected text is properly populated in the Search Box (#16355)

Closes: #16355


![image](https://github.com/microsoft/terminal/assets/55626797/8fd0345b-a8b2-4bc2-a25e-15d710127b63)

## Some Implementation Details

### Detecting text layout changes in the Control layer

As Search Highlight regions need to be removed when new text is added,
or the existing text is re-arranged due to window resize or similar
events, a new event `TextLayoutUpdated` is added that notifies
`CoreControl` of any text layout changes. The event is used to
invalidate and remove all search highlight regions from the buffer
(because the regions might not be _fresh_ anymore.

The new event is raised when:
1. `AdaptDispatch` writes new text into the buffer.
2. MainBuffer is switched to AltBuffer or vice-versa.
3. The user resized the window.
4. Font size changed.
5. Zoom level changed.

(Intensionally,) It's not raised when:
1. Buffer is scrolled.
2. The text cursor is moved.

When `ControlCore` receives a `TextLayoutUpdated` event, it clears the
Search Highlights in the *render data*, and raises an
`UpdateSearchResults` event to notify `TermControl` to update the Search
UI (`SearchBoxControl`).

In the future, we can use `TextLayoutUpdated` event to start a new
search which would refresh the results automatically after a slight
delay (throttled). *VSCode already does this today*.

### How does AtlasEngine draw the highlighted regions?

We follow a similar idea as for drawing the Selection region. When new
regions are available, the old+new regions are marked invalidated.
Later, a call to `_drawHighlighted()` is made at the end of
`PaintBufferLine()` to override the highlighted regions' colors with
highlight colors. The highlighting colors replace the buffer colors
while search highlights are active.

Note that to paint search highlights, we currently invalidate the row
completely. This forces text shaping for the rows in the viewport that
have at least one highlighted region. This is done to keep the (already
lengthy) PR... simple. We could take advantage of the fact that only
colors have changed and not the characters (or glyphs). I'm expecting
that this could be improved like:
1. When search regions are added, we add the highlighting colors to the
color bitmaps without causing text shaping.
2. When search regions are removed, we re-fill the color bitmaps with
the original colors from the Buffer.

## Validation Steps:
- New text, window resize, font size changes, zooming, and pasting
content into the terminal removes search highlights.
- highlighting colors override the foreground and background color of
the text (in the rendered output).
- Blinking, faded, reverse video, Intense text is highlighted as
expected.
This commit is contained in:
Tushar Singh 2024-04-17 21:41:31 +05:30 committed by GitHub
parent d632c39cc3
commit 90b8bb7c2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 571 additions and 336 deletions

View File

@ -8,7 +8,7 @@
using namespace Microsoft::Console::Types;
bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool reverse, bool caseInsensitive)
bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool reverse, bool caseInsensitive, std::vector<til::point_span>* prevResults)
{
const auto& textBuffer = renderData.GetTextBuffer();
const auto lastMutationId = textBuffer.GetLastMutationId();
@ -26,10 +26,13 @@ bool Search::ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, c
_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;
return true;
}
@ -111,28 +114,6 @@ const til::point_span* Search::GetCurrent() const noexcept
return nullptr;
}
void Search::HighlightResults() const
{
std::vector<til::inclusive_rect> toSelect;
const auto& textBuffer = _renderData->GetTextBuffer();
for (const auto& r : _results)
{
const auto rbStart = textBuffer.BufferToScreenPosition(r.start);
const auto rbEnd = textBuffer.BufferToScreenPosition(r.end);
til::inclusive_rect re;
re.top = rbStart.y;
re.bottom = rbEnd.y;
re.left = rbStart.x;
re.right = rbEnd.x;
toSelect.emplace_back(re);
}
_renderData->SelectSearchRegions(std::move(toSelect));
}
// Routine Description:
// - Takes the found word and selects it in the screen buffer
@ -161,4 +142,4 @@ const std::vector<til::point_span>& Search::Results() const noexcept
ptrdiff_t Search::CurrentMatch() const noexcept
{
return _index;
}
}

View File

@ -25,7 +25,11 @@ class Search final
public:
Search() = default;
bool ResetIfStale(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool reverse, bool caseInsensitive);
bool ResetIfStale(Microsoft::Console::Render::IRenderData& renderData,
const std::wstring_view& needle,
bool reverse,
bool caseInsensitive,
std::vector<til::point_span>* prevResults = nullptr);
void MoveToCurrentSelection();
void MoveToPoint(til::point anchor) noexcept;
@ -33,7 +37,6 @@ public:
void FindNext() noexcept;
const til::point_span* GetCurrent() const noexcept;
void HighlightResults() const;
bool SelectCurrent() const;
const std::vector<til::point_span>& Results() const noexcept;

View File

@ -120,6 +120,9 @@ 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);
@ -1123,6 +1126,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (SUCCEEDED(hr) && hr != S_FALSE)
{
_connection.Resize(vp.Height(), vp.Width());
// let the UI know that the text layout has been updated
_terminal->NotifyTextLayoutUpdated();
}
}
@ -1638,6 +1644,16 @@ 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:
@ -1701,43 +1717,45 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
const auto lock = _terminal->LockForWriting();
if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive))
std::vector<til::point_span> oldResults;
if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive, &oldResults))
{
_searcher.HighlightResults();
_searcher.MoveToCurrentSelection();
_cachedSearchResultRows = {};
if (SnapSearchResultToSelection())
{
_searcher.MoveToCurrentSelection();
SnapSearchResultToSelection(false);
}
_terminal->SetSearchHighlights(_searcher.Results());
_terminal->SetSearchHighlightFocused(_searcher.CurrentMatch());
}
else
{
_searcher.FindNext();
_terminal->SetSearchHighlightFocused(_searcher.CurrentMatch());
}
_renderer->TriggerSearchHighlight(oldResults);
const auto foundMatch = _searcher.SelectCurrent();
auto foundResults = winrt::make_self<implementation::FoundResultsArgs>(foundMatch);
if (foundMatch)
auto evArgs = winrt::make_self<implementation::UpdateSearchResultsEventArgs>();
if (!text.empty())
{
// this is used for search,
// DO NOT call _updateSelectionUI() here.
// We don't want to show the markers so manually tell it to clear it.
_terminal->SetBlockSelection(false);
UpdateSelectionMarkers.raise(*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);
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()));
}
}
_renderer->TriggerSelection();
// Raise a FoundMatch event, which the control will use to notify
// narrator if there was any results in the buffer
FoundMatch.raise(*this, *foundResults);
// 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);
}
Windows::Foundation::Collections::IVector<int32_t> ControlCore::SearchResultRows()
{
const auto lock = _terminal->LockForReading();
if (!_cachedSearchResultRows)
{
auto results = std::vector<int32_t>();
@ -1761,8 +1779,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::ClearSearch()
{
_terminal->AlwaysNotifyOnBufferRotation(false);
_searcher = {};
// 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 = {};
}
}
// Method Description:
// - Tells ControlCore to snap the current search result index to currently
// selected text if the search was started using it.
void ControlCore::SnapSearchResultToSelection(bool shouldSnap) noexcept
{
_snapSearchResultToSelection = shouldSnap;
}
// Method Description:
// - Returns true, if we should snap the current search result index to
// the currently selected text after a new search is started, else false.
bool ControlCore::SnapSearchResultToSelection() const noexcept
{
return _snapSearchResultToSelection;
}
void ControlCore::Close()

View File

@ -221,6 +221,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive);
void ClearSearch();
void SnapSearchResultToSelection(bool snap) noexcept;
bool SnapSearchResultToSelection() const noexcept;
Windows::Foundation::Collections::IVector<int32_t> SearchResultRows();
@ -281,7 +283,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::NoticeEventArgs> RaiseNotice;
til::typed_event<IInspectable, Control::TransparencyChangedEventArgs> TransparencyChanged;
til::typed_event<> ReceivedOutput;
til::typed_event<IInspectable, Control::FoundResultsArgs> FoundMatch;
til::typed_event<IInspectable, Control::UpdateSearchResultsEventArgs> UpdateSearchResults;
til::typed_event<IInspectable, Control::ShowWindowArgs> ShowWindowChanged;
til::typed_event<IInspectable, Control::UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
@ -321,6 +323,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr };
::Search _searcher;
bool _snapSearchResultToSelection;
winrt::handle _lastSwapChainHandle{ nullptr };
@ -379,6 +382,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _terminalCursorPositionChanged();
void _terminalTaskbarProgressChanged();
void _terminalShowWindowChanged(bool showOrHide);
void _terminalTextLayoutUpdated();
void _terminalPlayMidiNote(const int noteNumber,
const int velocity,
const std::chrono::microseconds duration);

View File

@ -131,6 +131,7 @@ namespace Microsoft.Terminal.Control
void Search(String text, Boolean goForward, Boolean caseSensitive);
void ClearSearch();
IVector<Int32> SearchResultRows { get; };
Boolean SnapSearchResultToSelection;
Microsoft.Terminal.Core.Color ForegroundColor { get; };
Microsoft.Terminal.Core.Color BackgroundColor { get; };
@ -179,7 +180,7 @@ namespace Microsoft.Terminal.Control
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, FoundResultsArgs> FoundMatch;
event Windows.Foundation.TypedEventHandler<Object, UpdateSearchResultsEventArgs> UpdateSearchResults;
event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseTerminalRequested;

View File

@ -12,7 +12,7 @@
#include "ScrollPositionChangedArgs.g.cpp"
#include "RendererWarningArgs.g.cpp"
#include "TransparencyChangedEventArgs.g.cpp"
#include "FoundResultsArgs.g.cpp"
#include "UpdateSearchResultsEventArgs.g.cpp"
#include "ShowWindowArgs.g.cpp"
#include "UpdateSelectionMarkersEventArgs.g.cpp"
#include "CompletionsChangedEventArgs.g.cpp"

View File

@ -12,7 +12,7 @@
#include "ScrollPositionChangedArgs.g.h"
#include "RendererWarningArgs.g.h"
#include "TransparencyChangedEventArgs.g.h"
#include "FoundResultsArgs.g.h"
#include "UpdateSearchResultsEventArgs.g.h"
#include "ShowWindowArgs.g.h"
#include "UpdateSelectionMarkersEventArgs.g.h"
#include "CompletionsChangedEventArgs.g.h"
@ -141,14 +141,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(double, Opacity);
};
struct FoundResultsArgs : public FoundResultsArgsT<FoundResultsArgs>
struct UpdateSearchResultsEventArgs : public UpdateSearchResultsEventArgsT<UpdateSearchResultsEventArgs>
{
public:
FoundResultsArgs(const bool foundMatch) :
_FoundMatch(foundMatch)
{
}
UpdateSearchResultsEventArgs() = default;
WINRT_PROPERTY(SearchState, State, SearchState::Inactive);
WINRT_PROPERTY(bool, FoundMatch);
WINRT_PROPERTY(int32_t, TotalMatches);
WINRT_PROPERTY(int32_t, CurrentMatch);

View File

@ -78,8 +78,15 @@ namespace Microsoft.Terminal.Control
Double Opacity { get; };
}
runtimeclass FoundResultsArgs
enum SearchState
{
Inactive = 0,
Active = 1,
};
runtimeclass UpdateSearchResultsEventArgs
{
SearchState State { get; };
Boolean FoundMatch { get; };
Int32 TotalMatches { get; };
Int32 CurrentMatch { get; };

View File

@ -26,18 +26,6 @@ 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)
{
SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive());
}
});
_focusableElements.insert(TextBox());
_focusableElements.insert(CloseButton());
@ -421,6 +409,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
SearchChanged.raise(TextBox().Text(), _GoForward(), _CaseSensitive());
}
// Method Description:
// - Handler for searchbox pointer-pressed.
// - Marks pointer events as handled so they don't bubble up to the terminal.
void SearchBoxControl::SearchBoxPointerPressedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e)
{
e.Handled(true);
}
// Method Description:
// - Handler for searchbox pointer-released.
// - Marks pointer events as handled so they don't bubble up to the terminal.
void SearchBoxControl::SearchBoxPointerReleasedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e)
{
e.Handled(true);
}
// Method Description:
// - Formats a status message representing the search state:
// * "Searching" - if totalMatches is negative
@ -518,6 +522,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
StatusBox().Text(status);
}
// Method Description:
// - Removes the status message in the status box.
void SearchBoxControl::ClearStatus()
{
StatusBox().Text(L"");
}
// Method Description:
// - Enables / disables results navigation buttons
// Arguments:

View File

@ -39,6 +39,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void PopulateTextbox(const winrt::hstring& text);
bool ContainsFocus();
void SetStatus(int32_t totalMatches, int32_t currentMatch);
void ClearStatus();
bool NavigationEnabled();
void NavigationEnabled(bool enabled);
@ -48,6 +49,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
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);
void SearchBoxPointerPressedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void SearchBoxPointerReleasedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
til::event<SearchHandler> Search;
til::event<SearchHandler> SearchChanged;

View File

@ -12,6 +12,7 @@ namespace Microsoft.Terminal.Control
void PopulateTextbox(String text);
Boolean ContainsFocus();
void SetStatus(Int32 totalMatches, Int32 currentMatch);
void ClearStatus();
Windows.Foundation.Rect ContentClipRect{ get; };
Double OpenAnimationStartPoint{ get; };

View File

@ -181,6 +181,8 @@
BorderThickness="{ThemeResource FlyoutBorderThemeThickness}"
CornerRadius="{ThemeResource OverlayCornerRadius}"
Orientation="Horizontal"
PointerPressed="SearchBoxPointerPressedHandler"
PointerReleased="SearchBoxPointerReleasedHandler"
Shadow="{StaticResource SharedShadow}"
Translation="0,0,16">
<StackPanel.RenderTransform>

View File

@ -85,7 +85,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.FoundMatch = _core.FoundMatch(winrt::auto_revoke, { get_weak(), &TermControl::_coreFoundMatch });
_revokers.UpdateSearchResults = _core.UpdateSearchResults(winrt::auto_revoke, { get_weak(), &TermControl::_coreUpdateSearchResults });
_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 });
@ -416,6 +416,7 @@ 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);
}
@ -501,7 +502,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const RoutedEventArgs& /*args*/)
{
_core.ClearSearch();
_searchBox->Close();
// Set focus back to terminal control
@ -3523,17 +3523,41 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
// Method Description:
// - Called when the core raises a FoundMatch event. That's done in response
// to us starting a search query with ControlCore::Search.
// - 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()
{
// 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);
}
// 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 results that were or were not found.
// - args: contains information about the search state and results that were
// or were not found.
// Return Value:
// - <none>
winrt::fire_and_forget TermControl::_coreFoundMatch(const IInspectable& /*sender*/, Control::FoundResultsArgs args)
winrt::fire_and_forget TermControl::_coreUpdateSearchResults(const IInspectable& /*sender*/, Control::UpdateSearchResultsEventArgs args)
{
co_await wil::resume_foreground(Dispatcher());
if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(*this) })
@ -3545,25 +3569,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
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);
_UpdateSearchScrollMarks();
if (_searchBox)
{
_searchBox->SetStatus(args.TotalMatches(), args.CurrentMatch());
_searchBox->NavigationEnabled(true);
if (args.State() == Control::SearchState::Inactive)
{
_searchBox->ClearStatus();
}
else
{
_searchBox->SetStatus(args.TotalMatches(), args.CurrentMatch());
}
}
}

View File

@ -352,6 +352,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
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();
// TSFInputControl Handlers
void _CompositionCompleted(winrt::hstring text);
@ -365,7 +366,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 _coreFoundMatch(const IInspectable& sender, Control::FoundResultsArgs args);
winrt::fire_and_forget _coreUpdateSearchResults(const IInspectable& sender, Control::UpdateSearchResultsEventArgs args);
til::point _toPosInDips(const Core::Point terminalCellPos);
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
@ -394,7 +395,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::ControlCore::TransparencyChanged_revoker TransparencyChanged;
Control::ControlCore::RaiseNotice_revoker RaiseNotice;
Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged;
Control::ControlCore::FoundMatch_revoker FoundMatch;
Control::ControlCore::UpdateSearchResults_revoker UpdateSearchResults;
Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers;
Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink;
Control::ControlCore::TitleChanged_revoker TitleChanged;

View File

@ -1259,6 +1259,35 @@ 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
{
_assertLocked();
_searchHighlights = highlights;
}
// Method Description:
// - Stores the focused search highlighted region in the terminal
// - If the region isn't empty, it will be brought into view
void Terminal::SetSearchHighlightFocused(const size_t focusedIdx)
{
_assertLocked();
_searchHighlightFocused = focusedIdx;
// bring the focused region into the view if the index is in valid range
if (focusedIdx < _searchHighlights.size())
{
const auto focused = til::at(_searchHighlights, focusedIdx);
_ScrollToPoints(focused.start, focused.end);
}
}
Scheme Terminal::GetColorScheme() const
{
const auto& renderSettings = GetRenderSettings();

View File

@ -155,6 +155,7 @@ 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;
@ -210,12 +211,12 @@ public:
std::pair<COLORREF, COLORREF> GetAttributeColors(const TextAttribute& attr) const noexcept override;
std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept override;
std::vector<Microsoft::Console::Types::Viewport> GetSearchSelectionRects() noexcept override;
std::span<const til::point_span> GetSearchHighlights() const noexcept override;
const til::point_span* GetSearchHighlightFocused() const noexcept override;
const bool IsSelectionActive() const noexcept override;
const bool IsBlockSelection() const noexcept override;
void ClearSelection() override;
void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override;
void SelectSearchRegions(std::vector<til::inclusive_rect> source) override;
const til::point GetSelectionAnchor() const noexcept override;
const til::point GetSelectionEnd() const noexcept override;
const std::wstring_view GetConsoleTitle() const noexcept override;
@ -232,6 +233,9 @@ 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);
void BlinkCursor() noexcept;
void SetCursorOn(const bool isOn) noexcept;
@ -340,6 +344,7 @@ 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;
@ -349,6 +354,9 @@ private:
std::wstring _startingTitle;
std::optional<til::color> _startingTabColor;
std::vector<til::point_span> _searchHighlights;
size_t _searchHighlightFocused = 0;
CursorType _defaultCursorShape = CursorType::Legacy;
til::enumset<Mode> _systemMode{ Mode::AutoWrap };
@ -381,7 +389,6 @@ private:
til::point pivot;
};
std::optional<SelectionAnchors> _selection;
std::vector<til::inclusive_rect> _searchSelections;
bool _blockSelection = false;
std::wstring _wordDelimiters;
SelectionExpansion _multiClickSelectionMode = SelectionExpansion::Char;
@ -447,6 +454,7 @@ private:
Microsoft::Console::Types::Viewport _GetVisibleViewport() const noexcept;
void _PreserveUserScrollOffset(const int viewportDelta) noexcept;
til::CoordType _ScrollToPoints(const til::point coordStart, const til::point coordEnd);
void _NotifyScrollEvent();
@ -460,7 +468,6 @@ private:
#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
std::vector<til::inclusive_rect> _GetSelectionRects() const noexcept;
std::vector<til::inclusive_rect> _GetSearchSelectionRects(Microsoft::Console::Types::Viewport viewport) const noexcept;
std::vector<til::point_span> _GetSelectionSpans() const noexcept;
std::pair<til::point, til::point> _PivotSelection(const til::point targetPos, bool& targetStart) const noexcept;
std::pair<til::point, til::point> _ExpandSelectionAnchors(std::pair<til::point, til::point> anchors) const;

View File

@ -239,6 +239,8 @@ void Terminal::UseAlternateScreenBuffer(const TextAttribute& attrs)
// Update scrollbars
_NotifyScrollEvent();
NotifyTextLayoutUpdated();
// redraw the screen
try
{
@ -296,6 +298,8 @@ void Terminal::UseMainScreenBuffer()
// Update scrollbars
_NotifyScrollEvent();
NotifyTextLayoutUpdated();
// redraw the screen
_activeBuffer().TriggerRedrawAll();
}
@ -370,3 +374,15 @@ 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();
}
}

View File

@ -63,40 +63,6 @@ std::vector<til::inclusive_rect> Terminal::_GetSelectionRects() const noexcept
return result;
}
// Method Description:
// - Helper to determine the selected region of the buffer. Used for rendering.
// Return Value:
// - A vector of rectangles representing the regions to select, line by line. They are absolute coordinates relative to the buffer origin.
std::vector<til::inclusive_rect> Terminal::_GetSearchSelectionRects(Microsoft::Console::Types::Viewport viewport) const noexcept
{
std::vector<til::inclusive_rect> result;
try
{
auto lowerIt = std::lower_bound(_searchSelections.begin(), _searchSelections.end(), viewport.Top(), [](const til::inclusive_rect& rect, til::CoordType value) {
return rect.top < value;
});
auto upperIt = std::upper_bound(_searchSelections.begin(), _searchSelections.end(), viewport.BottomExclusive(), [](til::CoordType value, const til::inclusive_rect& rect) {
return value < rect.top;
});
for (auto selection = lowerIt; selection != upperIt; ++selection)
{
const auto start = til::point{ selection->left, selection->top };
const auto end = til::point{ selection->right, selection->bottom };
const auto adj = _activeBuffer().GetTextRects(start, end, _blockSelection, false);
for (auto a : adj)
{
result.emplace_back(a);
}
}
return result;
}
CATCH_LOG();
return result;
}
// Method Description:
// - Identical to GetTextRects if it's a block selection, else returns a single span for the whole selection.
// Return Value:
@ -858,7 +824,6 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, til::point& pos) noex
void Terminal::ClearSelection()
{
_assertLocked();
_searchSelections.clear();
_selection = std::nullopt;
_selectionMode = SelectionInteractionMode::None;
_selectionIsTargetingUrl = false;

View File

@ -150,32 +150,37 @@ catch (...)
return {};
}
std::vector<Microsoft::Console::Types::Viewport> Terminal::GetSearchSelectionRects() noexcept
try
// Method Description:
// - Helper to determine the search highlights in the buffer. Used for rendering.
// Return Value:
// - A vector of rectangles representing the regions to select, line by line. They are absolute coordinates relative to the buffer origin.
std::span<const til::point_span> Terminal::GetSearchHighlights() const noexcept
{
std::vector<Viewport> result;
_assertLocked();
return _searchHighlights;
}
for (const auto& lineRect : _GetSearchSelectionRects(_GetVisibleViewport()))
const til::point_span* Terminal::GetSearchHighlightFocused() const noexcept
{
_assertLocked();
if (_searchHighlightFocused < _searchHighlights.size())
{
result.emplace_back(Viewport::FromInclusive(lineRect));
return &til::at(_searchHighlights, _searchHighlightFocused);
}
return result;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return {};
return nullptr;
}
void Terminal::SelectNewRegion(const til::point coordStart, const til::point coordEnd)
// Method Description:
// - If necessary, scrolls the viewport such that the start point is in the
// viewport, and if that's already the case, also brings the end point inside
// the viewport
// Arguments:
// - coordStart - The start point
// - coordEnd - The end point
// Return Value:
// - The updated scroll offset
til::CoordType Terminal::_ScrollToPoints(const til::point coordStart, const til::point coordEnd)
{
#pragma warning(push)
#pragma warning(disable : 26496) // cpp core checks wants these const, but they're decremented below.
auto realCoordStart = coordStart;
auto realCoordEnd = coordEnd;
#pragma warning(pop)
auto notifyScrollChange = false;
if (coordStart.y < _VisibleStartIndex())
{
@ -199,28 +204,18 @@ void Terminal::SelectNewRegion(const til::point coordStart, const til::point coo
_NotifyScrollEvent();
}
realCoordStart.y -= _VisibleStartIndex();
realCoordEnd.y -= _VisibleStartIndex();
SetSelectionAnchor(realCoordStart);
SetSelectionEnd(realCoordEnd, SelectionExpansion::Char);
return _scrollOffset;
}
void Terminal::SelectSearchRegions(std::vector<til::inclusive_rect> rects)
void Terminal::SelectNewRegion(const til::point coordStart, const til::point coordEnd)
{
_searchSelections.clear();
for (auto& rect : rects)
{
rect.top -= _VisibleStartIndex();
rect.bottom -= _VisibleStartIndex();
const auto newScrollOffset = _ScrollToPoints(coordStart, coordEnd);
const auto realStart = _ConvertToBufferCell(til::point{ rect.left, rect.top });
const auto realEnd = _ConvertToBufferCell(til::point{ rect.right, rect.bottom });
auto rr = til::inclusive_rect{ realStart.x, realStart.y, realEnd.x, realEnd.y };
_searchSelections.emplace_back(rr);
}
// update the selection coordinates so they're relative to the new scroll-offset
const auto newCoordStart = til::point{ coordStart.x, coordStart.y - newScrollOffset };
const auto newCoordEnd = til::point{ coordEnd.x, coordEnd.y - newScrollOffset };
SetSelectionAnchor(newCoordStart);
SetSelectionEnd(newCoordEnd, SelectionExpansion::Char);
}
const std::wstring_view Terminal::GetConsoleTitle() const noexcept

View File

@ -51,7 +51,6 @@ namespace
HRESULT PaintBufferLine(std::span<const Cluster> /*clusters*/, til::point /*coord*/, bool /*fTrimLeft*/, bool /*lineWrapped*/) noexcept { return S_OK; }
HRESULT PaintBufferGridLines(GridLineSet /*lines*/, COLORREF /*gridlineColor*/, COLORREF /*underlineColor*/, size_t /*cchLine*/, til::point /*coordTarget*/) noexcept { return S_OK; }
HRESULT PaintSelection(const til::rect& /*rect*/) noexcept { return S_OK; }
HRESULT PaintSelections(const std::vector<til::rect>& /*rects*/) noexcept { return S_OK; }
HRESULT PaintCursor(const CursorOptions& /*options*/) noexcept { return S_OK; }
HRESULT UpdateDrawingBrushes(const TextAttribute& /*textAttributes*/, const RenderSettings& /*renderSettings*/, gsl::not_null<IRenderData*> /*pData*/, bool /*usingSoftFont*/, bool /*isSettingDefaultBrushes*/) noexcept { return S_OK; }
HRESULT UpdateFont(const FontInfoDesired& /*FontInfoDesired*/, _Out_ FontInfo& /*FontInfo*/) noexcept { return S_OK; }

View File

@ -424,7 +424,12 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int delta)
}
}
void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/)
void ConhostInternalGetSet::NotifyTextLayoutUpdated()
{
// Not implemented for conhost.
}
void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, unsigned int /*replaceLength*/)
{
// Not implemented for conhost.
}

View File

@ -68,6 +68,7 @@ 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;

View File

@ -81,10 +81,10 @@ std::vector<Viewport> RenderData::GetSelectionRects() noexcept
// Method Description:
// - Retrieves one rectangle per line describing the area of the viewport
// that should be highlighted in some way to represent a user-interactive selection
// that should be highlighted
// Return Value:
// - Vector of Viewports describing the area selected
std::vector<Viewport> RenderData::GetSearchSelectionRects() noexcept
// - Vector of rects describing the highlighted area
std::span<const til::point_span> RenderData::GetSearchHighlights() const noexcept
{
return {};
}
@ -381,8 +381,9 @@ void RenderData::SelectNewRegion(const til::point coordStart, const til::point c
Selection::Instance().SelectNewRegion(coordStart, coordEnd);
}
void RenderData::SelectSearchRegions(std::vector<til::inclusive_rect> source)
const til::point_span* RenderData::GetSearchHighlightFocused() const noexcept
{
return nullptr;
}
// Routine Description:

View File

@ -26,7 +26,6 @@ public:
const FontInfo& GetFontInfo() const noexcept override;
std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept override;
std::vector<Microsoft::Console::Types::Viewport> GetSearchSelectionRects() noexcept override;
void LockConsole() noexcept override;
void UnlockConsole() noexcept override;
@ -55,7 +54,8 @@ public:
const bool IsBlockSelection() const noexcept override;
void ClearSelection() override;
void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override;
void SelectSearchRegions(std::vector<til::inclusive_rect> source) override;
std::span<const til::point_span> GetSearchHighlights() const noexcept override;
const til::point_span* GetSearchHighlightFocused() const noexcept override;
const til::point GetSelectionAnchor() const noexcept override;
const til::point GetSelectionEnd() const noexcept override;
const bool IsUiaDataInitialized() const noexcept override { return true; }

View File

@ -274,11 +274,6 @@ public:
return std::vector<Microsoft::Console::Types::Viewport>{};
}
std::vector<Microsoft::Console::Types::Viewport> GetSearchSelectionRects() noexcept override
{
return std::vector<Microsoft::Console::Types::Viewport>{};
}
void LockConsole() noexcept override
{
}
@ -360,8 +355,14 @@ public:
{
}
void SelectSearchRegions(std::vector<til::inclusive_rect> /*source*/) override
std::span<const til::point_span> GetSearchHighlights() const noexcept override
{
return {};
}
const til::point_span* GetSearchHighlightFocused() const noexcept override
{
return nullptr;
}
const til::point GetSelectionAnchor() const noexcept

View File

@ -288,6 +288,40 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
}
// Calls func(row, begX, endX) for each row and begX and begY are inclusive coordinates,
// because point_span itself also uses inclusive coordinates.
// In other words, it turns a
// ```
// +----------------+
// | #########|
// |################|
// |#### |
// +----------------+
// ```
// into:
// ```
// func(0, 8, 15)
// func(1, 0, 15)
// func(2, 0, 4)
// ```
constexpr void iterate_rows(til::CoordType width, auto&& func) const
{
// Copy the members so that the compiler knows it doesn't
// need to re-read them on every loop iteration.
const auto w = width - 1;
const auto ax = std::clamp(start.x, 0, w);
const auto ay = start.y;
const auto bx = std::clamp(end.x, 0, w);
const auto by = end.y;
for (auto y = ay; y <= by; ++y)
{
const auto x1 = y != ay ? 0 : ax;
const auto x2 = y != by ? w : bx;
func(y, x1, x2);
}
}
};
}

View File

@ -201,6 +201,20 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
}
constexpr rect to_origin(const rect& other) const
{
return to_origin(other.origin());
}
constexpr rect to_origin(const point& origin) const
{
const auto l = details::extract(::base::CheckSub(left, origin.x));
const auto t = details::extract(::base::CheckSub(top, origin.y));
const auto r = details::extract(::base::CheckSub(right, origin.x));
const auto b = details::extract(::base::CheckSub(bottom, origin.y));
return { l, t, r, b };
}
explicit constexpr operator bool() const noexcept
{
return left >= 0 && top >= 0 && right > left && bottom > top;

View File

@ -161,11 +161,6 @@ CATCH_RETURN()
return S_OK;
}
[[nodiscard]] HRESULT BgfxEngine::PaintSelections(const std::vector<til::rect>& /*rects*/) noexcept
{
return S_OK;
}
[[nodiscard]] HRESULT BgfxEngine::PaintCursor(const CursorOptions& options) noexcept
try
{

View File

@ -53,7 +53,6 @@ namespace Microsoft::Console::Render
const bool lineWrapped) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
[[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;

View File

@ -95,6 +95,28 @@ 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
{
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 };
const auto cellCountX = static_cast<til::CoordType>(_api.s->viewportCellCount.x);
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;
beg <<= shift;
end <<= shift;
til::rect rect{ beg, row, end + 1, row + 1 };
rect = rect.to_origin(viewportOrigin);
rect &= viewport;
_api.invalidatedRows.start = gsl::narrow_cast<u16>(std::min<int>(_api.invalidatedRows.start, std::max<int>(0, rect.top)));
_api.invalidatedRows.end = gsl::narrow_cast<u16>(std::max<int>(_api.invalidatedRows.end, std::max<int>(0, rect.bottom)));
});
}
return S_OK;
}
[[nodiscard]] HRESULT AtlasEngine::InvalidateScroll(const til::point* const pcoordDelta) noexcept
{
// InvalidateScroll() is a "synchronous" API. Any Invalidate()s after

View File

@ -285,8 +285,36 @@ CATCH_RETURN()
return S_OK;
}
[[nodiscard]] HRESULT AtlasEngine::PrepareRenderInfo(const RenderFrameInfo& info) noexcept
[[nodiscard]] HRESULT AtlasEngine::PrepareRenderInfo(RenderFrameInfo info) noexcept
{
// remove the highlighted regions that falls outside of the dirty region
{
const auto& highlights = info.searchHighlights;
// get the buffer origin relative to the viewport, and use it to calculate
// the dirty region to be relative to the buffer origin
const til::CoordType offsetX = _p.s->viewportOffset.x;
const til::CoordType offsetY = _p.s->viewportOffset.y;
const til::point bufferOrigin{ -offsetX, -offsetY };
const auto dr = _api.dirtyRect.to_origin(bufferOrigin);
const auto hiBeg = std::lower_bound(highlights.begin(), highlights.end(), dr.top, [](const auto& ps, const auto& drTop) { return ps.end.y < drTop; });
const auto hiEnd = std::upper_bound(hiBeg, highlights.end(), dr.bottom, [](const auto& drBottom, const auto& ps) { return drBottom < ps.start.y; });
_api.searchHighlights = { hiBeg, hiEnd };
// do the same for the focused search highlight
if (info.searchHighlightFocused)
{
const auto focusedStartY = info.searchHighlightFocused->start.y;
const auto focusedEndY = info.searchHighlightFocused->end.y;
const auto isFocusedInside = (focusedStartY >= dr.top && focusedStartY < dr.bottom) || (focusedEndY >= dr.top && focusedEndY < dr.bottom) || (focusedStartY < dr.top && focusedEndY >= dr.bottom);
if (isFocusedInside)
{
_api.searchHighlightFocused = { info.searchHighlightFocused, 1 };
}
}
}
return S_OK;
}
@ -308,6 +336,126 @@ CATCH_RETURN()
return S_OK;
}
void AtlasEngine::_fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor) noexcept
{
const auto bitmap = _p.colorBitmap.begin() + _p.colorBitmapRowStride * y;
const auto shift = gsl::narrow_cast<u8>(_p.rows[y]->lineRendition != LineRendition::SingleWidth);
auto beg = bitmap + (x1 << shift);
auto end = bitmap + (x2 << shift);
const u32 colors[] = {
u32ColorPremultiply(bgColor),
fgColor,
};
// This fills the color in the background bitmap, and then in the foreground bitmap.
for (size_t i = 0; i < 2; ++i)
{
const auto color = colors[i];
for (auto it = beg; it != end; ++it)
{
if (*it != color)
{
_p.colorBitmapGenerations[i].bump();
std::fill(it, end, color);
break;
}
}
// go to the same range in the same row, but in the foreground bitmap
beg += _p.colorBitmapDepthStride;
end += _p.colorBitmapDepthStride;
}
}
// Method Description:
// - Applies the given highlighting colors to the columns in the highlighted regions within a given range.
// - Resumes from the last partially painted region if any.
// Arguments:
// - highlights: the list of highlighted regions (yet to be painted)
// - row: the row for which highlighted regions are to be painted
// - begX: the starting (inclusive) column to paint from (in Buffer coord)
// - endX: the ending (exclusive) column to paint up to (in Buffer coord)
// - fgColor: the foreground highlight color
// - bgColor: the background highlight color
// Returns:
// - S_OK if we painted successfully, else an appropriate HRESULT error code
[[nodiscard]] HRESULT AtlasEngine::_drawHighlighted(std::span<const til::point_span>& highlights, const u16 row, const u16 begX, const u16 endX, const u32 fgColor, const u32 bgColor) noexcept
try
{
if (highlights.empty())
{
return S_OK;
}
const til::CoordType y = row;
const til::CoordType x1 = begX;
const til::CoordType x2 = endX;
const auto offset = til::point{ _p.s->viewportOffset.x, _p.s->viewportOffset.y };
auto it = highlights.begin();
const auto itEnd = highlights.end();
auto hiStart = it->start - offset;
auto hiEnd = it->end - offset;
// Nothing to paint if we haven't reached the row where the highlight region begins
if (y < hiStart.y)
{
return S_OK;
}
// We might have painted a multi-line highlight region in the last call,
// so we need to make sure we paint the whole region before moving on to
// the next region.
if (y > hiStart.y)
{
const auto isFinalRow = y == hiEnd.y;
const auto end = isFinalRow ? std::min(hiEnd.x + 1, x2) : x2;
_fillColorBitmap(row, x1, end, fgColor, bgColor);
// Return early if we couldn't paint the whole region. We will resume
// from here in the next call.
if (!isFinalRow || end == x2)
{
return S_OK;
}
++it;
}
// Pick a highlighted region and (try) to paint it
while (it != itEnd)
{
hiStart = it->start - offset;
hiEnd = it->end - offset;
const auto isStartInside = y == hiStart.y && hiStart.x < x2;
const auto isEndInside = y == hiEnd.y && hiEnd.x < x2;
if (isStartInside && isEndInside)
{
_fillColorBitmap(row, hiStart.x, static_cast<size_t>(hiEnd.x) + 1, fgColor, bgColor);
++it;
}
else
{
// Paint the region partially if start is within the range.
if (isStartInside)
{
const auto start = std::max(x1, hiStart.x);
_fillColorBitmap(y, start, x2, fgColor, bgColor);
}
break;
}
}
// Shorten the list by removing processed regions
highlights = { it, itEnd };
return S_OK;
}
CATCH_RETURN()
[[nodiscard]] HRESULT AtlasEngine::PaintBufferLine(std::span<const Cluster> clusters, til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept
try
{
@ -345,34 +493,12 @@ try
_api.bufferLineColumn.emplace_back(columnEnd);
}
{
const auto row = _p.colorBitmap.begin() + _p.colorBitmapRowStride * y;
auto beg = row + (static_cast<size_t>(x) << shift);
auto end = row + (static_cast<size_t>(columnEnd) << shift);
// Apply the current foreground and background colors to the cells
_fillColorBitmap(y, x, columnEnd, _api.currentForeground, _api.currentBackground);
const u32 colors[] = {
u32ColorPremultiply(_api.currentBackground),
_api.currentForeground,
};
for (size_t i = 0; i < 2; ++i)
{
const auto color = colors[i];
for (auto it = beg; it != end; ++it)
{
if (*it != color)
{
_p.colorBitmapGenerations[i].bump();
std::fill(it, end, color);
break;
}
}
beg += _p.colorBitmapDepthStride;
end += _p.colorBitmapDepthStride;
}
}
// Apply the highlighting colors to the highlighted cells
RETURN_IF_FAILED(_drawHighlighted(_api.searchHighlights, y, x, columnEnd, highlightFg, highlightBg));
RETURN_IF_FAILED(_drawHighlighted(_api.searchHighlightFocused, y, x, columnEnd, highlightFocusFg, highlightFocusBg));
_api.lastPaintBufferLineCoord = { x, y };
return S_OK;
@ -418,40 +544,6 @@ try
}
CATCH_RETURN()
[[nodiscard]] HRESULT AtlasEngine::PaintSelections(const std::vector<til::rect>& rects) noexcept
try
{
if (rects.empty())
{
return S_OK;
}
for (const auto& rect : rects)
{
const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.top, 0, _p.s->viewportCellCount.y));
const auto from = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.left, 0, _p.s->viewportCellCount.x - 1));
const auto to = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.right, from, _p.s->viewportCellCount.x));
if (rect.bottom <= 0 || rect.top >= _p.s->viewportCellCount.y)
{
continue;
}
const auto bg = &_p.backgroundBitmap[_p.colorBitmapRowStride * y];
const auto fg = &_p.foregroundBitmap[_p.colorBitmapRowStride * y];
std::fill(bg + from, bg + to, 0xff3296ff);
std::fill(fg + from, fg + to, 0xff000000);
}
for (int i = 0; i < 2; ++i)
{
_p.colorBitmapGenerations[i].bump();
}
return S_OK;
}
CATCH_RETURN()
[[nodiscard]] HRESULT AtlasEngine::PaintCursor(const CursorOptions& options) noexcept
try
{

View File

@ -33,19 +33,19 @@ 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 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;
[[nodiscard]] HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept override;
[[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override;
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
[[nodiscard]] HRESULT PrepareRenderInfo(RenderFrameInfo info) noexcept override;
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
[[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept override;
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
[[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
[[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null<IRenderData*> pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept override;
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
@ -91,6 +91,8 @@ namespace Microsoft::Console::Render::Atlas
void _mapCharacters(const wchar_t* text, u32 textLength, u32* mappedLength, IDWriteFontFace2** mappedFontFace) const;
void _mapComplex(IDWriteFontFace2* mappedFontFace, u32 idx, u32 length, ShapedRow& row);
ATLAS_ATTR_COLD void _mapReplacementCharacter(u32 from, u32 to, ShapedRow& row);
void _fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor) noexcept;
[[nodiscard]] HRESULT _drawHighlighted(std::span<const til::point_span>& highlights, const u16 row, const u16 begX, const u16 endX, const u32 fgColor, const u32 bgColor) noexcept;
// AtlasEngine.api.cpp
void _resolveTransparencySettings() noexcept;
@ -117,6 +119,11 @@ namespace Microsoft::Console::Render::Atlas
static constexpr range<u16> invalidatedRowsNone{ u16max, u16min };
static constexpr range<u16> invalidatedRowsAll{ u16min, u16max };
static constexpr u32 highlightBg = 0xff00ffff;
static constexpr u32 highlightFg = 0xff000000;
static constexpr u32 highlightFocusBg = 0xff3296ff;
static constexpr u32 highlightFocusFg = 0xff000000;
std::unique_ptr<IBackend> _b;
RenderingPayload _p;
@ -173,6 +180,10 @@ namespace Microsoft::Console::Render::Atlas
// UpdateHyperlinkHoveredId()
u16 hyperlinkHoveredId = 0;
// These tracks the highlighted regions on the screen that are yet to be painted.
std::span<const til::point_span> searchHighlights;
std::span<const til::point_span> searchHighlightFocused;
// dirtyRect is a computed value based on invalidatedRows.
til::rect dirtyRect;
// These "invalidation" fields are reset in EndPaint()

View File

@ -7,6 +7,11 @@
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
{
return S_OK;
}
HRESULT RenderEngineBase::InvalidateTitle(const std::wstring_view proposedTitle) noexcept
{
if (proposedTitle != _lastFrameTitle)
@ -42,7 +47,7 @@ HRESULT RenderEngineBase::UpdateSoftFont(const std::span<const uint16_t> /*bitPa
return S_FALSE;
}
HRESULT RenderEngineBase::PrepareRenderInfo(const RenderFrameInfo& /*info*/) noexcept
HRESULT RenderEngineBase::PrepareRenderInfo(RenderFrameInfo /*info*/) noexcept
{
return S_FALSE;
}

View File

@ -360,7 +360,6 @@ void Renderer::TriggerSelection()
{
// Get selection rectangles
auto rects = _GetSelectionRects();
auto searchSelections = _GetSearchSelectionRects();
// Make a viewport representing the coordinates that are currently presentable.
const til::rect viewport{ _pData->GetViewport().Dimensions() };
@ -373,20 +372,45 @@ void Renderer::TriggerSelection()
FOREACH_ENGINE(pEngine)
{
LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSearchSelection));
LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection));
LOG_IF_FAILED(pEngine->InvalidateSelection(searchSelections));
LOG_IF_FAILED(pEngine->InvalidateSelection(rects));
}
_previousSelection = std::move(rects);
_previousSearchSelection = std::move(searchSelections);
NotifyPaintFrame();
}
CATCH_LOG();
}
// Routine Description:
// - Called when the search highlight areas in the console have changed.
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();
FOREACH_ENGINE(pEngine)
{
LOG_IF_FAILED(pEngine->InvalidateHighlight(oldHighlights, renditions));
LOG_IF_FAILED(pEngine->InvalidateHighlight(newHighlights, renditions));
}
NotifyPaintFrame();
}
CATCH_LOG()
// Routine Description:
// - Called when we want to check if the viewport has moved and scroll accordingly if so.
// Arguments:
@ -1129,8 +1153,9 @@ void Renderer::_PaintCursor(_In_ IRenderEngine* const pEngine)
[[nodiscard]] HRESULT Renderer::_PrepareRenderInfo(_In_ IRenderEngine* const pEngine)
{
RenderFrameInfo info;
info.cursorInfo = _currentCursorOptions;
return pEngine->PrepareRenderInfo(info);
info.searchHighlights = _pData->GetSearchHighlights();
info.searchHighlightFocused = _pData->GetSearchHighlightFocused();
return pEngine->PrepareRenderInfo(std::move(info));
}
// Routine Description:
@ -1212,19 +1237,10 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
// Get selection rectangles
const auto rectangles = _GetSelectionRects();
const auto searchRectangles = _GetSearchSelectionRects();
std::vector<til::rect> dirtySearchRectangles;
for (auto& dirtyRect : dirtyAreas)
{
for (const auto& sr : searchRectangles)
{
if (const auto rectCopy = sr & dirtyRect)
{
dirtySearchRectangles.emplace_back(rectCopy);
}
}
for (const auto& rect : rectangles)
{
if (const auto rectCopy = rect & dirtyRect)
@ -1233,11 +1249,6 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
}
}
}
if (!dirtySearchRectangles.empty())
{
LOG_IF_FAILED(pEngine->PaintSelections(std::move(dirtySearchRectangles)));
}
}
CATCH_LOG();
}
@ -1303,28 +1314,6 @@ std::vector<til::rect> Renderer::_GetSelectionRects() const
return result;
}
std::vector<til::rect> Renderer::_GetSearchSelectionRects() const
{
const auto& buffer = _pData->GetTextBuffer();
auto rects = _pData->GetSearchSelectionRects();
// Adjust rectangles to viewport
auto view = _pData->GetViewport();
std::vector<til::rect> result;
result.reserve(rects.size());
for (auto rect : rects)
{
// Convert buffer offsets to the equivalent range of screen cells
// expected by callers, taking line rendition into account.
const auto lineRendition = buffer.GetLineRendition(rect.Top());
rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition));
result.emplace_back(view.ConvertToOrigin(rect).ToExclusive());
}
return result;
}
// Method Description:
// - Offsets all of the selection rectangles we might be holding onto
// as the previously selected area. If the whole viewport scrolls,

View File

@ -54,6 +54,7 @@ namespace Microsoft::Console::Render
void TriggerTeardown() noexcept;
void TriggerSelection();
void TriggerSearchHighlight(const std::vector<til::point_span>& oldHighlights);
void TriggerScroll();
void TriggerScroll(const til::point* const pcoordDelta);
@ -110,7 +111,6 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool usingSoftFont, const bool isSettingDefaultBrushes);
[[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine);
std::vector<til::rect> _GetSelectionRects() const;
std::vector<til::rect> _GetSearchSelectionRects() const;
void _ScrollPreviousSelection(const til::point delta);
[[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine);
bool _isInHoveredInterval(til::point coordTarget) const noexcept;
@ -129,7 +129,6 @@ namespace Microsoft::Console::Render
std::optional<CursorOptions> _currentCursorOptions;
std::vector<Cluster> _clusterBuffer;
std::vector<til::rect> _previousSelection;
std::vector<til::rect> _previousSearchSelection;
std::function<void()> _pfnBackgroundColorChanged;
std::function<void()> _pfnFrameColorChanged;
std::function<void()> _pfnRendererEnteredErrorState;

View File

@ -57,7 +57,6 @@ namespace Microsoft::Console::Render
const size_t cchLine,
const til::point coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
[[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;

View File

@ -834,13 +834,6 @@ CATCH_RETURN();
return S_OK;
}
[[nodiscard]] HRESULT GdiEngine::PaintSelections(const std::vector<til::rect>& rects) noexcept
{
UNREFERENCED_PARAMETER(rects);
return S_OK;
}
#ifdef DBG
void GdiEngine::_CreateDebugWindow()

View File

@ -47,7 +47,8 @@ namespace Microsoft::Console::Render
virtual const TextBuffer& GetTextBuffer() const noexcept = 0;
virtual const FontInfo& GetFontInfo() const noexcept = 0;
virtual std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept = 0;
virtual std::vector<Microsoft::Console::Types::Viewport> GetSearchSelectionRects() noexcept = 0;
virtual std::span<const til::point_span> GetSearchHighlights() const noexcept = 0;
virtual const til::point_span* GetSearchHighlightFocused() const noexcept = 0;
virtual void LockConsole() noexcept = 0;
virtual void UnlockConsole() noexcept = 0;
@ -72,7 +73,6 @@ namespace Microsoft::Console::Render
virtual const bool IsBlockSelection() const = 0;
virtual void ClearSelection() = 0;
virtual void SelectNewRegion(const til::point coordStart, const til::point coordEnd) = 0;
virtual void SelectSearchRegions(std::vector<til::inclusive_rect> source) = 0;
virtual const til::point GetSelectionAnchor() const noexcept = 0;
virtual const til::point GetSelectionEnd() const noexcept = 0;
virtual const bool IsUiaDataInitialized() const noexcept = 0;

View File

@ -29,7 +29,8 @@ namespace Microsoft::Console::Render
{
struct RenderFrameInfo
{
std::optional<CursorOptions> cursorInfo;
std::span<const til::point_span> searchHighlights;
const til::point_span* searchHighlightFocused;
};
enum class GridLines
@ -66,19 +67,19 @@ 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 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;
[[nodiscard]] virtual HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept = 0;
[[nodiscard]] virtual HRESULT NotifyNewText(const std::wstring_view newText) noexcept = 0;
[[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0;
[[nodiscard]] virtual HRESULT PrepareRenderInfo(RenderFrameInfo info) noexcept = 0;
[[nodiscard]] virtual HRESULT ResetLineTransform() noexcept = 0;
[[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBufferLine(std::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF gridlineColor, COLORREF underlineColor, size_t cchLine, til::point coordTarget) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintSelection(const til::rect& rect) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept = 0;
[[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null<IRenderData*> pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept = 0;
[[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept = 0;

View File

@ -24,6 +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 InvalidateTitle(const std::wstring_view proposedTitle) noexcept override;
[[nodiscard]] HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept override;
@ -34,7 +35,7 @@ namespace Microsoft::Console::Render
const til::size cellSize,
const size_t centeringHint) noexcept override;
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
[[nodiscard]] HRESULT PrepareRenderInfo(RenderFrameInfo info) noexcept override;
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition,

View File

@ -386,11 +386,6 @@ void UiaEngine::WaitUntilCanRender() noexcept
return S_FALSE;
}
[[nodiscard]] HRESULT UiaEngine::PaintSelections(const std::vector<til::rect>& /*rect*/) noexcept
{
return S_FALSE;
}
// Routine Description:
// - Draws the cursor on the screen
// For UIA, this doesn't mean anything. So do nothing.

View File

@ -51,7 +51,6 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT PaintBufferLine(const std::span<const Cluster> clusters, const til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
[[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
[[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, const gsl::not_null<IRenderData*> pData, const bool usingSoftFont, const bool isSettingDefaultBrushes) noexcept override;
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;

View File

@ -245,11 +245,6 @@ using namespace Microsoft::Console::Types;
return S_OK;
}
[[nodiscard]] HRESULT VtEngine::PaintSelections(const std::vector<til::rect>& /*rect*/) noexcept
{
return S_OK;
}
// Routine Description:
// - Write a VT sequence to change the current colors of text. Writes true RGB
// color sequences.

View File

@ -64,7 +64,6 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT PaintBufferLine(std::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
[[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
[[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override;

View File

@ -298,11 +298,6 @@ CATCH_RETURN()
return S_OK;
}
[[nodiscard]] HRESULT WddmConEngine::PaintSelections(const std::vector<til::rect>& /*rects*/) noexcept
{
return S_OK;
}
[[nodiscard]] HRESULT WddmConEngine::PaintCursor(const CursorOptions& /*options*/) noexcept
{
return S_OK;

View File

@ -46,7 +46,6 @@ namespace Microsoft::Console::Render
const bool lineWrapped) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
[[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;

View File

@ -80,6 +80,7 @@ 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;
};

View File

@ -178,9 +178,11 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string)
_ApplyCursorMovementFlags(cursor);
// Notify UIA of new text.
// Notify terminal and UIA of new text.
// It's important to do this here instead of in TextBuffer, because here you
// have access to the entire line of text.
// 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);
}

View File

@ -215,6 +215,11 @@ 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...");