-
Notifications
You must be signed in to change notification settings - Fork 9k
Scroll from selection dragging out of window (#1247) #1523
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
Changes from 25 commits
8078faf
45a48b3
b222607
d1623aa
aa2f5f5
4676618
4eefdcc
a428484
1c60e84
1efcf04
cee54ed
f5576df
5a16a8b
151d228
10f9ef6
de5f211
abd16b8
d05ab94
2d89dfc
21437d2
6dda81b
892109b
cfd93f4
f67b2ac
cf3a0b6
c687948
b4ea1dc
046f04e
e933123
c486bae
1f5c3cc
05fe605
caddcf4
0cde0d9
df8cbd1
9a1924d
3bcf16f
8a22038
42a5259
8267ed2
e72a424
88fabd0
2e4df6d
15fa91f
1e1663f
a89d9b5
9393ed5
fcf4576
a3373d3
d3a1024
a8f436a
5080f8e
a2c44aa
928d94b
a5830de
4b6e53d
a1fbd6d
c1f990f
e8f6ca2
302df52
099d935
1a00e94
6de0c4f
41b0b6c
3d29b63
20da37b
cc9f332
e2fec02
0b4df04
580a5b7
534e726
421cec8
ecc14ee
2beb4ee
045e8ce
29eb59d
bbfc815
ab2a862
6e2580d
1ec19a2
282c5ce
15234ce
08943f7
7f4c3b0
00446d5
9a79d63
ae2b454
406fda4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| _settings{ settings }, | ||
| _closing{ false }, | ||
| _lastScrollOffset{ std::nullopt }, | ||
| _autoScrollVelocity{ 0 }, | ||
| _autoScrollingPointerPoint{ std::nullopt }, | ||
| _autoScrollTimer{}, | ||
| _lastAutoScrollUpdateTime{ std::nullopt }, | ||
| _desiredFont{ DEFAULT_FONT_FACE.c_str(), 0, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8 }, | ||
| _actualFont{ DEFAULT_FONT_FACE.c_str(), 0, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false }, | ||
| _touchAnchor{ std::nullopt }, | ||
|
|
@@ -521,6 +525,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| auto pfnScrollPositionChanged = std::bind(&TermControl::_TerminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); | ||
| _terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged); | ||
|
|
||
| static constexpr auto autoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000)); | ||
| _autoScrollTimer.Interval(autoScrollUpdateInterval); | ||
| _autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll }); | ||
|
|
||
| // Set up blinking cursor | ||
| int blinkTime = GetCaretBlinkTime(); | ||
| if (blinkTime != INFINITE) | ||
|
|
@@ -737,14 +745,33 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
|
|
||
| if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse) | ||
| { | ||
| if (point.Properties().IsLeftButtonPressed()) | ||
| if (_terminal->IsSelectionActive() && point.Properties().IsLeftButtonPressed()) | ||
| { | ||
| const auto cursorPosition = point.Position(); | ||
| const auto terminalPosition = _GetTerminalPosition(cursorPosition); | ||
| _SetEndSelectionPointAtCursor(cursorPosition); | ||
|
|
||
| // save location (for rendering) + render | ||
| _terminal->SetEndSelectionPosition(terminalPosition); | ||
| _renderer->TriggerSelection(); | ||
| const double cursorBelowBottomDist = cursorPosition.Y - _root.Padding().Top - _swapChainPanel.ActualHeight(); | ||
| const double cursorAboveTopDist = -1 * cursorPosition.Y + _root.Padding().Top; | ||
|
|
||
| constexpr double minAutoScrollDist = 2.0; // Arbitrary value | ||
| double newAutoScrollVelocity = 0.0; | ||
| if (cursorBelowBottomDist > minAutoScrollDist) | ||
| { | ||
| newAutoScrollVelocity = _GetAutoScrollSpeed(cursorBelowBottomDist); | ||
| } | ||
| else if (cursorAboveTopDist > minAutoScrollDist) | ||
| { | ||
| newAutoScrollVelocity = -1.0 * _GetAutoScrollSpeed(cursorAboveTopDist); | ||
| } | ||
|
|
||
| if (newAutoScrollVelocity != 0) | ||
| { | ||
| _TryStartAutoScroll(point, newAutoScrollVelocity); | ||
| } | ||
| else | ||
| { | ||
| _TryStopAutoScroll(ptr.PointerId()); | ||
| } | ||
| } | ||
| } | ||
| else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch && _touchAnchor) | ||
|
|
@@ -800,6 +827,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| _touchAnchor = std::nullopt; | ||
| } | ||
|
|
||
| _TryStopAutoScroll(ptr.PointerId()); | ||
|
|
||
| args.Handled(true); | ||
| } | ||
|
|
||
|
|
@@ -812,7 +841,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/, | ||
| Input::PointerRoutedEventArgs const& args) | ||
| { | ||
| auto delta = args.GetCurrentPoint(_root).Properties().MouseWheelDelta(); | ||
| const auto point = args.GetCurrentPoint(_root); | ||
| const auto delta = point.Properties().MouseWheelDelta(); | ||
| // Get the state of the Ctrl & Shift keys | ||
| const auto modifiers = args.KeyModifiers(); | ||
| const auto ctrlPressed = WI_IsFlagSet(modifiers, VirtualKeyModifiers::Control); | ||
|
|
@@ -828,7 +858,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| } | ||
| else | ||
| { | ||
| _MouseScrollHandler(delta); | ||
| _MouseScrollHandler(delta, point); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -885,7 +915,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| // - Scroll the visible viewport in response to a mouse wheel event. | ||
| // Arguments: | ||
| // - mouseDelta: the mouse wheel delta that triggered this event. | ||
| void TermControl::_MouseScrollHandler(const double mouseDelta) | ||
| void TermControl::_MouseScrollHandler(const double mouseDelta, Windows::UI::Input::PointerPoint const& pointerPoint) | ||
| { | ||
| const auto currentOffset = this->GetScrollOffset(); | ||
|
|
||
|
|
@@ -907,6 +937,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| // The scroll bar's ValueChanged handler will actually move the viewport | ||
| // for us. | ||
| _scrollBar.Value(static_cast<int>(newValue)); | ||
|
|
||
| if (_terminal->IsSelectionActive() && pointerPoint.Properties().IsLeftButtonPressed()) | ||
| { | ||
| // If user is mouse selecting and scrolls, they then point at new character. | ||
| // Make sure selection reflects that immediately. | ||
| _SetEndSelectionPointAtCursor(pointerPoint.Position()); | ||
| } | ||
| } | ||
|
|
||
| void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& sender, | ||
|
|
@@ -974,6 +1011,84 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| return false; | ||
| } | ||
|
|
||
| // Method Description: | ||
| // - Starts new pointer related auto scroll behavior, or continues existing one. | ||
| // Does nothing when there is already auto scroll associated with another pointer. | ||
| // Arguments: | ||
| // - pointerPoint: info about pointer that causes auto scroll. Pointer's position | ||
| // is later used to update selection. | ||
| // - scrollVelocity: target velocity of scrolling in characters / sec | ||
| void TermControl::_TryStartAutoScroll(Windows::UI::Input::PointerPoint const& pointerPoint, const double scrollVelocity) | ||
| { | ||
| // Allow only one pointer at the time | ||
| if (!_autoScrollingPointerPoint.has_value() || _autoScrollingPointerPoint.value().PointerId() == pointerPoint.PointerId()) | ||
| { | ||
| _autoScrollingPointerPoint = pointerPoint; | ||
| _autoScrollVelocity = scrollVelocity; | ||
|
|
||
| // If this is first time the auto scroll update is about to be called, | ||
| // kick-start it by initializing its time delta as if it started now | ||
| if (!_lastAutoScrollUpdateTime.has_value()) | ||
| { | ||
| _lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now(); | ||
| } | ||
|
|
||
| // Apparently this check is not necessary but greatly improves performance | ||
| if (!_autoScrollTimer.IsEnabled()) | ||
| { | ||
| _autoScrollTimer.Start(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Method Description: | ||
| // - Stops auto scroll if it's active and is associated with supplied pointer id. | ||
| // Arguments: | ||
| // - pointerId: id of pointer for which to stop auto scroll | ||
| void TermControl::_TryStopAutoScroll(const uint32_t pointerId) | ||
| { | ||
| if (_autoScrollingPointerPoint.has_value() && pointerId == _autoScrollingPointerPoint.value().PointerId()) | ||
| { | ||
| _autoScrollingPointerPoint = std::nullopt; | ||
| _autoScrollVelocity = 0; | ||
| _lastAutoScrollUpdateTime = std::nullopt; | ||
|
|
||
| // Apparently this check is not necessary but greatly improves performance | ||
| if (_autoScrollTimer.IsEnabled()) | ||
| { | ||
| _autoScrollTimer.Stop(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Method Description: | ||
| // - Called continuously to gradually scroll viewport when user is | ||
| // mouse selecting outside it (to 'follow' the cursor). | ||
| // Arguments: | ||
| // - none | ||
| void TermControl::_UpdateAutoScroll(Windows::Foundation::IInspectable const& /* sender */, | ||
| Windows::Foundation::IInspectable const& /* e */) | ||
| { | ||
| if (_autoScrollVelocity != 0) | ||
| { | ||
| const auto timeNow = std::chrono::high_resolution_clock::now(); | ||
|
|
||
| if (_lastAutoScrollUpdateTime.has_value()) | ||
| { | ||
| static constexpr double microSecPerSec = 1000000.0; | ||
| const double deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(timeNow - _lastAutoScrollUpdateTime.value()).count() / microSecPerSec; | ||
| _scrollBar.Value(_scrollBar.Value() + _autoScrollVelocity * deltaTime); | ||
|
|
||
| if (_autoScrollingPointerPoint.has_value()) | ||
| { | ||
| _SetEndSelectionPointAtCursor(_autoScrollingPointerPoint.value().Position()); | ||
| } | ||
| } | ||
|
|
||
| _lastAutoScrollUpdateTime = timeNow; | ||
| } | ||
| } | ||
|
|
||
| // Method Description: | ||
| // - Event handler for the GotFocus event. This is used to start | ||
| // blinking the cursor when the window is focused. | ||
|
|
@@ -1079,6 +1194,25 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| _terminal->SetCursorVisible(!_terminal->IsCursorVisible()); | ||
| } | ||
|
|
||
| // Method Description: | ||
| // - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging. | ||
| // Arguments: | ||
| // - cursorPosition: in pixels, relative to the origin of the control | ||
| void TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition) | ||
| { | ||
| auto terminalPosition = _GetTerminalPosition(cursorPosition); | ||
|
|
||
| const short lastVisibleRow = std::max(_terminal->GetViewport().Height() - 1, 0); | ||
| const short lastVisibleCol = std::max(_terminal->GetViewport().Width() - 1, 0); | ||
|
|
||
| terminalPosition.Y = std::clamp(terminalPosition.Y, short{ 0 }, lastVisibleRow); | ||
| terminalPosition.X = std::clamp(terminalPosition.X, short{ 0 }, lastVisibleCol); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check out
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| // save location (for rendering) + render | ||
| _terminal->SetEndSelectionPosition(terminalPosition); | ||
| _renderer->TriggerSelection(); | ||
| } | ||
|
|
||
| // Method Description: | ||
| // - Process a resize event that was initiated by the user. This can either be due to the user resizing the window (causing the swapchain to resize) or due to the DPI changing (causing us to need to resize the buffer to match) | ||
| // Arguments: | ||
|
|
@@ -1219,6 +1353,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| // cursorTimer timer, now stopped, is destroyed. | ||
| } | ||
|
|
||
| _autoScrollTimer.Stop(); | ||
|
||
|
|
||
| if (auto localConnection{ std::exchange(_connection, nullptr) }) | ||
| { | ||
| localConnection.Close(); | ||
|
|
@@ -1490,6 +1626,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation | |
| return terminalPosition; | ||
| } | ||
|
|
||
| // Method Description: | ||
| // - Calculates speed of single axis of auto scrolling. It has to allow for both | ||
| // fast and precise selection. | ||
| // Arguments: | ||
| // - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative. | ||
| // Return Value: | ||
| // - positive speed in characters / sec | ||
| double TermControl::_GetAutoScrollSpeed(double cursorDistanceFromBorder) const | ||
| { | ||
| // The numbers below just feel well, feel free to change. | ||
| // TODO: Maybe account for space beyond border that user has available | ||
| return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0; | ||
mcpiroman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // clang-format off | ||
| // -------------------------------- WinRT Events --------------------------------- | ||
| // Winrt events need a method for adding a callback to the event and removing the callback. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.