Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

First three interactivity fixes #9980

Merged
11 commits merged into from
May 4, 2021
80 changes: 58 additions & 22 deletions src/cascadia/TerminalControl/ControlInteractivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_selectionNeedsToBeCopied{ false }
{
_core = winrt::make_self<ControlCore>(settings, connection);

_core->ScrollPositionChanged({ this, &ControlInteractivity::_coreScrollPositionChanged });
}

void ControlInteractivity::UpdateSettings()
Expand Down Expand Up @@ -197,7 +199,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (multiClickMapper == 1)
{
_singleClickTouchdownPos = pixelPosition;
_singleClickTouchdownTerminalPos = terminalPosition;

if (!_core->HasSelection())
{
Expand Down Expand Up @@ -266,10 +267,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto fontSizeInDips{ _core->FontSizeInDips() };
if (distance >= (std::min(fontSizeInDips.width(), fontSizeInDips.height()) / 4.f))
{
_core->SetSelectionAnchor(terminalPosition);
// GH#9955.c: Make sure to use the temrinal location of the
// _touchdown_ point here. We want to start the selection
// from where the user initially clicked, not where they are
// now.
_core->SetSelectionAnchor(_getTerminalPosition(touchdownPoint));

// stop tracking the touchdown point
_singleClickTouchdownPos = std::nullopt;
_singleClickTouchdownTerminalPos = std::nullopt;
}
}

Expand Down Expand Up @@ -303,12 +308,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// panning down)
const float numRows = -1.0f * (dy / fontSizeInDips.height<float>());

const auto currentOffset = ::base::ClampedNumeric<double>(_core->ScrollOffset());
const auto newValue = numRows + currentOffset;
const double currentOffset = ::base::ClampedNumeric<double>(_core->ScrollOffset());
const double newValue = numRows + currentOffset;
Comment on lines +309 to +310
Copy link
Member

Choose a reason for hiding this comment

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

I thought you'd want to keep auto currentOffset. That way you get a safe chromium math add for newValue.


// Update the Core's viewport position, and raise a
// ScrollPositionChanged event to update the scrollbar
_updateScrollbar(newValue);
UpdateScrollbar(newValue);

// Use this point as our new scroll anchor.
_touchAnchor = newTouchPoint;
Expand Down Expand Up @@ -341,7 +346,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}

_singleClickTouchdownPos = std::nullopt;
_singleClickTouchdownTerminalPos = std::nullopt;
}

void ControlInteractivity::TouchReleased()
Expand Down Expand Up @@ -396,7 +400,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
else
{
_mouseScrollHandler(delta, terminalPosition, state.isLeftButtonDown);
_mouseScrollHandler(delta, pixelPosition, state.isLeftButtonDown);
}
return false;
}
Expand Down Expand Up @@ -428,35 +432,40 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Scroll the visible viewport in response to a mouse wheel event.
// Arguments:
// - mouseDelta: the mouse wheel delta that triggered this event.
// - point: the location of the mouse during this event
// - pixelPosition: the location of the mouse during this event
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// - pixelPosition: the location of the mouse during this event
// - pixelPosition: the pixel location of the mouse during this event

Copy link
Member

Choose a reason for hiding this comment

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

eh. i hate when the name is obvious and we are forced to document the obvious name

// - isLeftButtonPressed: true iff the left mouse button was pressed during this event.
void ControlInteractivity::_mouseScrollHandler(const double mouseDelta,
const til::point terminalPosition,
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);
Copy link
Member

Choose a reason for hiding this comment

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

It seems very important to continue using the correct constant.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yea I just got spooked by int math vs float math. I'll revert this bit


// 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<double>(_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<int>(newValue));
UpdateScrollbar(newValue);

if (isLeftButtonPressed)
{
// If user is mouse selecting and scrolls, they then point at new
// character. Make sure selection reflects that immediately.
SetEndSelectionPoint(terminalPosition);
SetEndSelectionPoint(pixelPosition);
}
}

Expand All @@ -476,15 +485,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - newValue: The new top of the viewport
// Return Value:
// - <none>
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 = std::clamp<double>(newValue, 0.0, _core->BufferHeight());

// 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<int>(::std::round(_internalScrollbarPosition));
Copy link
Member

Choose a reason for hiding this comment

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

Hmm. Does rounding match the pre-split behavior?

Copy link
Member Author

Choose a reason for hiding this comment

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

It might actually be better, a little smoother in both directions now. Hard to say for sure comparing Debug vs Release

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
int viewTop = ::base::saturated_cast<int>(::std::round(_internalScrollbarPosition));
const auto viewTop = ::base::saturated_cast<int>(::std::round(_internalScrollbarPosition));

if (viewTop != _core->ScrollOffset())
{
_core->UserScrollViewport(viewTop);

// _core->ScrollOffset() is now set to newValue
_ScrollPositionChangedHandlers(*this,
winrt::make<ScrollPositionChangedArgs>(_core->ScrollOffset(),
_core->ViewHeight(),
_core->BufferHeight()));
// _core->ScrollOffset() is now set to newValue
_ScrollPositionChangedHandlers(*this,
winrt::make<ScrollPositionChangedArgs>(_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:
// - <none>
void ControlInteractivity::_coreScrollPositionChanged(const IInspectable& /*sender*/,
const Control::ScrollPositionChangedArgs& args)
{
_internalScrollbarPosition = args.ViewTop();
Copy link
Member

Choose a reason for hiding this comment

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

Just making sure: this fires every single time a line is printed to the terminal for the first 9001 lines. Is adding another event recipient worth it? Is there a way you can do this without having a double version of the viewport top every time it changes? :/

Copy link
Member Author

Choose a reason for hiding this comment

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

You know, I was being cautious here. I'm not positive this is needed. The event might get caught by the TermControl, throttled, then have TermControl call UpdateScrollbar, which might fix it for us.

}

void ControlInteractivity::_hyperlinkHandler(const std::wstring_view uri)
Expand Down
8 changes: 6 additions & 2 deletions src/cascadia/TerminalControl/ControlInteractivity.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const int32_t delta,
const til::point pixelPosition,
const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state);

void UpdateScrollbar(const double newValue);

#pragma endregion

bool CopySelectionToClipboard(bool singleLine,
Expand All @@ -83,6 +86,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
private:
winrt::com_ptr<ControlCore> _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.
Expand All @@ -97,7 +101,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Timestamp _lastMouseClickTimestamp;
std::optional<til::point> _lastMouseClickPos;
std::optional<til::point> _singleClickTouchdownPos;
std::optional<til::point> _singleClickTouchdownTerminalPos;
std::optional<til::point> _lastMouseClickPosNoSelection;
// This field tracks whether the selection has changed meaningfully
// since it was last copied. It's generally used to prevent copyOnSelect
Expand All @@ -124,9 +127,10 @@ 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);
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);
Expand Down
4 changes: 2 additions & 2 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1302,8 +1302,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}

const auto newValue = static_cast<int>(args.NewValue());
_core->UserScrollViewport(newValue);
Copy link
Member

Choose a reason for hiding this comment

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

hey, good!

const auto newValue = args.NewValue();
_interactivity->UpdateScrollbar(newValue);

// User input takes priority over terminal events so cancel
// any pending scroll bar update if the user scrolls.
Expand Down
Loading