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

Fix a pair of TermControl dragging bugs #10650

Merged
51 commits merged into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
e4478ee
Only access ControlInteractivity through the projection
zadjii-msft Apr 29, 2021
400b35f
Only 24 more errors to go
zadjii-msft Apr 30, 2021
42b970b
Totally upend the control AutomationPeer workings
zadjii-msft Apr 30, 2021
4578c13
some comments that should have been in the previous commit. 16 errors…
zadjii-msft Apr 30, 2021
d8cc047
GetHoveredCell/UpdateHoveredCell. 12 remain.
zadjii-msft Apr 30, 2021
21a97b6
Revert "Revert "Use DComp surface handle for Swap Chain management.""
zadjii-msft May 3, 2021
2e861d8
some minor cleanup
zadjii-msft May 3, 2021
e20caae
Merge branch 'dev/migrie/f/oop/use-dcomp-handle' into dev/migrie/inte…
zadjii-msft May 3, 2021
d667f3b
Use the HANDLE for the swapchainin the core projection
zadjii-msft May 3, 2021
62cbf30
it builds and runs so I guess it's done now
zadjii-msft May 3, 2021
445bdf6
Do the delayload dance
zadjii-msft May 6, 2021
1376721
Merge branch 'dev/migrie/f/oop/use-dcomp-handle' into dev/migrie/inte…
zadjii-msft May 6, 2021
a1ce7cb
What a ridiculous hack
zadjii-msft May 7, 2021
03fb764
fix accessibility
zadjii-msft May 7, 2021
5e7d270
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
zadjii-msft May 7, 2021
6e031e5
I want to get the padding out of interactivity, but the control bound…
zadjii-msft May 7, 2021
47410c9
now this works!
zadjii-msft May 7, 2021
0869456
Some cleanup
zadjii-msft May 7, 2021
dace0c4
a bit more cleanup
zadjii-msft May 7, 2021
12b78f5
Merge remote-tracking branch 'origin/main' into dev/migrie/f/oop/use-…
zadjii-msft May 7, 2021
e340fe9
Merge branch 'dev/migrie/f/oop/use-dcomp-handle' into dev/migrie/inte…
zadjii-msft May 7, 2021
b7b23a8
god I knew I left these behind
zadjii-msft May 7, 2021
086710a
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
zadjii-msft May 12, 2021
21a6370
fix the tests
zadjii-msft May 12, 2021
4efdf39
This fixes nvda, so nowwe just have to polish
zadjii-msft May 13, 2021
59e911d
minor typo cleanup
zadjii-msft May 13, 2021
6f07004
some consts
zadjii-msft May 13, 2021
ff3b808
this is almost exclusively nits
zadjii-msft May 13, 2021
559d1de
simple nits
zadjii-msft May 20, 2021
804a114
this one's easy too
zadjii-msft May 20, 2021
0f0af03
An enum does in fact work across the process boundary
zadjii-msft May 21, 2021
95300d4
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
zadjii-msft May 21, 2021
1de9485
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
zadjii-msft May 24, 2021
1bc27d0
fix the build
zadjii-msft May 24, 2021
3cace60
chances are, this will fix the x86 build
zadjii-msft May 24, 2021
265bd9f
I am ashamed, this should not be there
zadjii-msft May 24, 2021
c8a2957
Port the automation peer crash fix to this branch, @carlos-zamora
zadjii-msft May 24, 2021
074f67c
The comment literally says to put this first. Why didn't I put this f…
zadjii-msft May 25, 2021
5a0840a
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
zadjii-msft May 25, 2021
f5665c5
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
DHowett Jun 10, 2021
056c354
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
zadjii-msft Jul 8, 2021
e643885
minor nits from carlos
zadjii-msft Jul 8, 2021
33653e0
Merge branch 'dev/migrie/interactivity-projection-000' of https://git…
zadjii-msft Jul 8, 2021
3d59a67
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
zadjii-msft Jul 8, 2021
5a598f7
Merge remote-tracking branch 'origin/main' into dev/migrie/interactiv…
zadjii-msft Jul 13, 2021
6206d77
This fixes #9109
zadjii-msft Jul 13, 2021
3d30295
comments
zadjii-msft Jul 13, 2021
2aaae9e
Fixes #4603
zadjii-msft Jul 13, 2021
9950d90
Add a test too, because I'm a good engineer
zadjii-msft Jul 13, 2021
5247bf6
Merge remote-tracking branch 'origin/main' into dev/migrie/b/9109-tab…
zadjii-msft Jul 19, 2021
83cfa95
Merge remote-tracking branch 'origin/main' into dev/migrie/b/9109-tab…
zadjii-msft Jul 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/cascadia/TerminalControl/ControlInteractivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused,
const til::point pixelPosition)
const til::point pixelPosition,
const bool pointerPressedInBounds)
{
const til::point terminalPosition = _getTerminalPosition(pixelPosition);

Expand All @@ -288,7 +289,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState));
}
else if (focused && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
// GH#4603 - don't modify the selection if the pointer press didn't
// actually start _in_ the control bounds. Case in point - someone drags
// a file into the bounds of the control. That shouldn't send the
// selection into space.
else if (focused && pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
{
if (_singleClickTouchdownPos)
{
Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalControl/ControlInteractivity.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused,
const til::point pixelPosition);
const til::point pixelPosition,
const bool pointerPressedInBounds);
void TouchMoved(const til::point newTouchPoint,
const bool focused);

Expand Down
4 changes: 3 additions & 1 deletion src/cascadia/TerminalControl/ControlInteractivity.idl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ namespace Microsoft.Terminal.Control
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean focused,
Microsoft.Terminal.Core.Point pixelPosition);
Microsoft.Terminal.Core.Point pixelPosition,
Boolean pointerPressedInBounds);

void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint,
Boolean focused);

Expand Down
21 changes: 18 additions & 3 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Focus(FocusState::Pointer);
}

// Mark that this pointer event actually started within our bounds.
// We'll need this later, for PointerMoved events.
_pointerPressedInBounds = true;

if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{
const auto contactRect = point.Properties().ContactRect();
Expand Down Expand Up @@ -1104,10 +1108,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
_focused,
pixelPosition);

if (_focused && point.Properties().IsLeftButtonPressed())
pixelPosition,
_pointerPressedInBounds);

// GH#9109 - Only start an auto-scroll when the drag actually
// started within our bounds. Otherwise, someone could start a drag
// outside the terminal control, drag into the padding, and trick us
// into starting to scroll.
if (_focused && _pointerPressedInBounds && point.Properties().IsLeftButtonPressed())
{
// We want to find the distance relative to the bounds of the
// SwapChainPanel, not the entire control. If they drag out of
// the bounds of the text, into the padding, we still what that
// to auto-scroll
const double cursorBelowBottomDist = cursorPosition.Y - SwapChainPanel().Margin().Top - SwapChainPanel().ActualHeight();
const double cursorAboveTopDist = -1 * cursorPosition.Y + SwapChainPanel().Margin().Top;

Expand Down Expand Up @@ -1157,6 +1170,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}

_pointerPressedInBounds = false;
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved

const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
const auto cursorPosition = point.Position();
Expand Down
4 changes: 3 additions & 1 deletion src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::shared_ptr<ThrottledFuncTrailing<ScrollBarUpdate>> _updateScrollBar;
bool _isInternalScrollBarUpdate;

// Auto scroll occurs when user, while selecting, drags cursor outside viewport. View is then scrolled to 'follow' the cursor.
// Auto scroll occurs when user, while selecting, drags cursor outside
// viewport. View is then scrolled to 'follow' the cursor.
double _autoScrollVelocity;
std::optional<Windows::UI::Input::PointerPoint> _autoScrollingPointerPoint;
Windows::UI::Xaml::DispatcherTimer _autoScrollTimer;
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
bool _pointerPressedInBounds{ false };

winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation{ nullptr };
Windows::UI::Xaml::DispatcherTimer _bellLightTimer{ nullptr };
Expand Down
90 changes: 83 additions & 7 deletions src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ namespace ControlUnitTests
TEST_METHOD(ScrollWithSelection);
TEST_METHOD(TestScrollWithTrackpad);
TEST_METHOD(TestQuickDragOnSelect);

TEST_METHOD(TestDragSelectOutsideBounds);

TEST_METHOD(PointerClickOutsideActiveRegion);

TEST_CLASS_SETUP(ClassSetup)
Expand Down Expand Up @@ -288,7 +291,8 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1);
cursorPosition1,
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
Expand All @@ -300,7 +304,8 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition2);
cursorPosition2,
true);
Log::Comment(L"Verify that there's now two selections (one on each row)");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(2u, core->_terminal->GetSelectionRects().size());
Expand Down Expand Up @@ -333,7 +338,8 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition4);
cursorPosition4,
true);
Log::Comment(L"Verify that there's now one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
Expand Down Expand Up @@ -388,7 +394,8 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1);
cursorPosition1,
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
Expand Down Expand Up @@ -536,14 +543,83 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1);
cursorPosition1,
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());

Log::Comment(L"Verify that it started on the first cell we clicked on, not the one we dragged to");
COORD expectedAnchor{ 0, 0 };
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
}

void ControlInteractivityTests::TestDragSelectOutsideBounds()
{
// This is a test for GH#4603

auto [settings, conn] = _createSettingsAndConnection();
auto [core, interactivity] = _createCoreAndInteractivity(*settings, *conn);
_standardInit(core, interactivity);

// For this test, don't use any modifiers
const auto modifiers = ControlKeyStates();
const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const Control::MouseButtonState noMouseDown{};

const til::size fontSize{ 9, 21 };
Log::Comment(L"Click on the terminal");
const til::point cursorPosition0{ 6, 0 };
interactivity->PointerPressed(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0);

Log::Comment(L"Verify that there's not yet a selection");
VERIFY_IS_FALSE(core->HasSelection());

VERIFY_IS_TRUE(interactivity->_singleClickTouchdownPos.has_value());
VERIFY_ARE_EQUAL(cursorPosition0, interactivity->_singleClickTouchdownPos.value());

Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width<int>() * 2, 0 };
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1,
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());

Log::Comment(L"Verify that it started on the first cell we clicked on, not the one we dragged to");
COORD expectedAnchor{ 0, 0 };
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
COORD expectedEnd{ 2, 0 };
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());

interactivity->PointerReleased(noMouseDown,
WM_LBUTTONUP,
modifiers,
cursorPosition1);

VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());

Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control");
const til::point cursorPosition2{ fontSize.width<int>() * 10, 0 };
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition2,
false);

Log::Comment(L"The selection should be unchanged.");
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
}

void ControlInteractivityTests::PointerClickOutsideActiveRegion()
Expand All @@ -561,7 +637,6 @@ namespace ControlUnitTests
const Control::MouseButtonState noMouseDown{};

const til::size fontSize{ 9, 21 };

interactivity->_rowsToScroll = 1;
int expectedTop = 0;
int expectedViewHeight = 20;
Expand Down Expand Up @@ -630,7 +705,8 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1);
cursorPosition1,
true);
Log::Comment(L"Verify that there's still no selection");
VERIFY_IS_FALSE(core->HasSelection());
}
Expand Down