From 33d29fa30ace884653d797a8f92d69758ffcaae7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 28 Apr 2021 14:11:55 -0500 Subject: [PATCH] This fixes #9955.b --- .../TerminalControl/ControlInteractivity.cpp | 60 +++++++++++++++---- .../TerminalControl/ControlInteractivity.h | 5 +- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 7db1d486992..323bd9aaac5 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -35,6 +35,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation _selectionNeedsToBeCopied{ false } { _core = winrt::make_self(settings, connection); + + _core->ScrollPositionChanged({ this, &ControlInteractivity::_coreScrollPositionChanged }); } void ControlInteractivity::UpdateSettings() @@ -306,8 +308,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // panning down) const float numRows = -1.0f * (dy / fontSizeInDips.height()); - const auto currentOffset = ::base::ClampedNumeric(_core->ScrollOffset()); - const auto newValue = numRows + currentOffset; + const double currentOffset = ::base::ClampedNumeric(_core->ScrollOffset()); + const double newValue = numRows + currentOffset; // Update the Core's viewport position, and raise a // ScrollPositionChanged event to update the scrollbar @@ -436,23 +438,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation const til::point pixelPosition, const bool isLeftButtonPressed) { - const auto currentOffset = _core->ScrollOffset(); + // GH#9955.b: Start scrolling from our internal scrollbar position. This + // lets us accumulate fractional numbers of rows to scroll with each + // event. Especially for precision trackpads, we might be getting scroll + // deltas smaller than a single row, but we still want lots of those to + // accumulate. + const double currentOffset = _internalScrollbarPosition; // negative = down, positive = up // However, for us, the signs are flipped. // With one of the precision mice, one click is always a multiple of 120 (WHEEL_DELTA), // but the "smooth scrolling" mode results in non-int values - const auto rowDelta = mouseDelta / (-1.0 * WHEEL_DELTA); + const double rowDelta = mouseDelta / (-1.0 * 120.0); // WHEEL_PAGESCROLL is a Win32 constant that represents the "scroll one page // at a time" setting. If we ignore it, we will scroll a truly absurd number // of rows. - const auto rowsToScroll{ _rowsToScroll == WHEEL_PAGESCROLL ? _core->ViewHeight() : _rowsToScroll }; + const double rowsToScroll{ _rowsToScroll == WHEEL_PAGESCROLL ? ::base::saturated_cast(_core->ViewHeight()) : _rowsToScroll }; double newValue = (rowsToScroll * rowDelta) + (currentOffset); // Update the Core's viewport position, and raise a // ScrollPositionChanged event to update the scrollbar - _updateScrollbar(::base::saturated_cast(newValue)); + _updateScrollbar(newValue); if (isLeftButtonPressed) { @@ -478,15 +485,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - newValue: The new top of the viewport // Return Value: // - - void ControlInteractivity::_updateScrollbar(const int newValue) + void ControlInteractivity::_updateScrollbar(const double newValue) { - _core->UserScrollViewport(newValue); + // Set this as the new value of our internal scrollbar representation. + // We're doing this so we can accumulate fractional amounts of a row to + // scroll each time the mouse scrolls. + _internalScrollbarPosition = newValue; + + // If the new scrollbar position, rounded to an int, is at a different + // row, then actually update the scroll position in the core, and raise + // a ScrollPositionChanged to inform the control. + int viewTop = ::base::saturated_cast(::std::round(_internalScrollbarPosition)); + if (viewTop != _core->ScrollOffset()) + { + _core->UserScrollViewport(viewTop); + + // _core->ScrollOffset() is now set to newValue + _ScrollPositionChangedHandlers(*this, + winrt::make(_core->ScrollOffset(), + _core->ViewHeight(), + _core->BufferHeight())); + } + } - // _core->ScrollOffset() is now set to newValue - _ScrollPositionChangedHandlers(*this, - winrt::make(_core->ScrollOffset(), - _core->ViewHeight(), - _core->BufferHeight())); + // Method Description: + // - Event handler for the core's ScrollPositionChanged event. This is + // called when the core changes its viewport position, due to more text + // being output. We'll use this event to update our own internal scrollbar + // tracker to the position the viewport is at now. + // Arguments: + // - args: args containing infor about the position of the viewport in the buffer. + // Return Value: + // - + void ControlInteractivity::_coreScrollPositionChanged(const IInspectable& /*sender*/, + const Control::ScrollPositionChangedArgs& args) + { + _internalScrollbarPosition = args.ViewTop(); } void ControlInteractivity::_hyperlinkHandler(const std::wstring_view uri) diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index e4fae12ef86..87114add6ce 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -83,6 +83,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation private: winrt::com_ptr _core{ nullptr }; unsigned int _rowsToScroll; + double _internalScrollbarPosition{ 0.0 }; // If this is set, then we assume we are in the middle of panning the // viewport via touch input. @@ -123,9 +124,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool _canSendVTMouseInput(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers); void _sendPastedTextToConnection(std::wstring_view wstr); - void _updateScrollbar(const int newValue); + void _updateScrollbar(const double newValue); til::point _getTerminalPosition(const til::point& pixelPosition); + void _coreScrollPositionChanged(const IInspectable& /*sender*/, const Control::ScrollPositionChangedArgs& args); + TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs);