Move PointerId, focus and in-bounds handling down into Interactivity (#20017)

These do not notionally belong to TermControl; as we prepare to move
automatic scrolling down into the Interactivity layer (for
WpfTerminalControl to depend on,) we're going to need to track these in
Interactivity too.

This also converts the arguments from the winrt-specific
Foundation::Point to Core::Point, and simplifies some of the signatures
to no longer pass things we do not need to pass.

Right now, this should result in no functional change.

Some of the tests are sensitive to the fact that we never loaded
`SPI_GETWHEELSCROLLLINES`
and instead defaulted to `1`. They've been hammered back into shape.
This commit is contained in:
Dustin L. Howett
2026-03-31 18:38:32 -05:00
committed by GitHub
parent 14f4271954
commit c334f91f80
5 changed files with 139 additions and 98 deletions

View File

@@ -169,6 +169,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::GotFocus() void ControlInteractivity::GotFocus()
{ {
_focused = true;
if (_uiaEngine.get()) if (_uiaEngine.get())
{ {
THROW_IF_FAILED(_uiaEngine->Enable()); THROW_IF_FAILED(_uiaEngine->Enable());
@@ -181,6 +183,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::LostFocus() void ControlInteractivity::LostFocus()
{ {
_focused = false;
if (_uiaEngine.get()) if (_uiaEngine.get())
{ {
THROW_IF_FAILED(_uiaEngine->Disable()); THROW_IF_FAILED(_uiaEngine->Disable());
@@ -248,7 +252,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
PasteFromClipboard.raise(*this, std::move(args)); PasteFromClipboard.raise(*this, std::move(args));
} }
void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState, void ControlInteractivity::PointerPressed(const uint32_t /*pointerId*/,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const uint64_t timestamp, const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
@@ -261,6 +266,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto shiftEnabled = modifiers.IsShiftPressed(); const auto shiftEnabled = modifiers.IsShiftPressed();
const auto ctrlEnabled = modifiers.IsCtrlPressed(); const auto ctrlEnabled = modifiers.IsCtrlPressed();
// Mark that this pointer event actually started within our bounds.
// We'll need this later, for PointerMoved events.
_pointerPressedInBounds = true;
// GH#9396: we prioritize hyper-link over VT mouse events // GH#9396: we prioritize hyper-link over VT mouse events
auto hyperlink = _core->GetHyperlink(terminalPosition.to_core_point()); auto hyperlink = _core->GetHyperlink(terminalPosition.to_core_point());
if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) && if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
@@ -355,24 +364,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
} }
void ControlInteractivity::TouchPressed(const winrt::Windows::Foundation::Point contactPoint) void ControlInteractivity::TouchPressed(const Core::Point contactPoint)
{ {
_touchAnchor = contactPoint; _touchAnchor = contactPoint;
} }
bool ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState, bool ControlInteractivity::PointerMoved(const uint32_t /*pointerId*/,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused, const Core::Point pixelPosition)
const Core::Point pixelPosition,
const bool pointerPressedInBounds)
{ {
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false); const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
// Returning true from this function indicates that the caller should do no further processing of this movement. // Returning true from this function indicates that the caller should do no further processing of this movement.
bool handledCompletely = false; bool handledCompletely = false;
// Short-circuit isReadOnly check to avoid warning dialog // Short-circuit isReadOnly check to avoid warning dialog
if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers)) if (_focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
{ {
_sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState); _sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
handledCompletely = true; handledCompletely = true;
@@ -381,7 +389,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// actually start _in_ the control bounds. Case in point - someone drags // actually start _in_ the control bounds. Case in point - someone drags
// a file into the bounds of the control. That shouldn't send the // a file into the bounds of the control. That shouldn't send the
// selection into space. // selection into space.
else if (focused && pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown)) else if (_focused && _pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
{ {
if (_singleClickTouchdownPos) if (_singleClickTouchdownPos)
{ {
@@ -427,10 +435,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return handledCompletely; return handledCompletely;
} }
void ControlInteractivity::TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint, void ControlInteractivity::TouchMoved(const Core::Point newTouchPoint)
const bool focused)
{ {
if (focused && if (_focused &&
_touchAnchor) _touchAnchor)
{ {
const auto anchor = _touchAnchor.value(); const auto anchor = _touchAnchor.value();
@@ -464,11 +471,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
} }
void ControlInteractivity::PointerReleased(Control::MouseButtonState buttonState, void ControlInteractivity::PointerReleased(const uint32_t /*pointerId*/,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition) const Core::Point pixelPosition)
{ {
_pointerPressedInBounds = false;
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false); const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
// Short-circuit isReadOnly check to avoid warning dialog // Short-circuit isReadOnly check to avoid warning dialog
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers)) if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))

View File

@@ -51,23 +51,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::Console::Render::IRenderData* GetRenderData() const; ::Microsoft::Console::Render::IRenderData* GetRenderData() const;
#pragma region Input Methods #pragma region Input Methods
void PointerPressed(Control::MouseButtonState buttonState, void PointerPressed(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const uint64_t timestamp, const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition); const Core::Point pixelPosition);
void TouchPressed(const winrt::Windows::Foundation::Point contactPoint); void TouchPressed(const Core::Point contactPoint);
bool PointerMoved(Control::MouseButtonState buttonState, bool PointerMoved(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused, const Core::Point pixelPosition);
const Core::Point pixelPosition, void TouchMoved(const Core::Point newTouchPoint);
const bool pointerPressedInBounds);
void TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint,
const bool focused);
void PointerReleased(Control::MouseButtonState buttonState, void PointerReleased(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind, const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition); const Core::Point pixelPosition);
@@ -115,7 +115,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// If this is set, then we assume we are in the middle of panning the // If this is set, then we assume we are in the middle of panning the
// viewport via touch input. // viewport via touch input.
std::optional<winrt::Windows::Foundation::Point> _touchAnchor; std::optional<Core::Point> _touchAnchor;
using Timestamp = uint64_t; using Timestamp = uint64_t;
@@ -142,6 +142,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
uint64_t _id; uint64_t _id;
static std::atomic<uint64_t> _nextId; static std::atomic<uint64_t> _nextId;
bool _focused{ false };
bool _pointerPressedInBounds{ false };
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime); unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
void _updateSystemParameterSettings() noexcept; void _updateSystemParameterSettings() noexcept;

View File

@@ -36,24 +36,24 @@ namespace Microsoft.Terminal.Control
void RequestPasteTextFromClipboard(); void RequestPasteTextFromClipboard();
void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point); void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point);
void PointerPressed(MouseButtonState buttonState, void PointerPressed(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind, UInt32 pointerUpdateKind,
UInt64 timestamp, UInt64 timestamp,
Microsoft.Terminal.Core.ControlKeyStates modifiers, Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition); Microsoft.Terminal.Core.Point pixelPosition);
void TouchPressed(Windows.Foundation.Point contactPoint); void TouchPressed(Microsoft.Terminal.Core.Point contactPoint);
Boolean PointerMoved(MouseButtonState buttonState, Boolean PointerMoved(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind, UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers, Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean focused, Microsoft.Terminal.Core.Point pixelPosition);
Microsoft.Terminal.Core.Point pixelPosition,
Boolean pointerPressedInBounds);
void TouchMoved(Windows.Foundation.Point newTouchPoint, void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint);
Boolean focused);
void PointerReleased(MouseButtonState buttonState, void PointerReleased(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind, UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers, Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition); Microsoft.Terminal.Core.Point pixelPosition);

View File

@@ -1990,12 +1990,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// NB: I don't think this is correct because the touch should be in the center of the rect. // NB: I don't think this is correct because the touch should be in the center of the rect.
// I suspect the point.Position() would be correct. // I suspect the point.Position() would be correct.
const auto contactRect = point.Properties().ContactRect(); const auto contactRect = point.Properties().ContactRect();
_interactivity.TouchPressed({ contactRect.X, contactRect.Y }); til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchPressed(newTouchPoint.to_core_point());
} }
else else
{ {
const auto cursorPosition = point.Position(); const auto cursorPosition = point.Position();
_interactivity.PointerPressed(TermControl::GetPressedMouseButtons(point), _interactivity.PointerPressed(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point), TermControl::GetPointerUpdateKind(point),
point.Timestamp(), point.Timestamp(),
ControlKeyStates{ args.KeyModifiers() }, ControlKeyStates{ args.KeyModifiers() },
@@ -2034,12 +2036,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse || if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen) type == Windows::Devices::Input::PointerDeviceType::Pen)
{ {
auto suppressFurtherHandling = _interactivity.PointerMoved(TermControl::GetPressedMouseButtons(point), auto suppressFurtherHandling = _interactivity.PointerMoved(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point), TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()), ControlKeyStates(args.KeyModifiers()),
_focused, pixelPosition);
pixelPosition,
_pointerPressedInBounds);
// GH#9109 - Only start an auto-scroll when the drag actually // GH#9109 - Only start an auto-scroll when the drag actually
// started within our bounds. Otherwise, someone could start a drag // started within our bounds. Otherwise, someone could start a drag
@@ -2078,7 +2079,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
else if (type == Windows::Devices::Input::PointerDeviceType::Touch) else if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{ {
const auto contactRect = point.Properties().ContactRect(); const auto contactRect = point.Properties().ContactRect();
_interactivity.TouchMoved({ contactRect.X, contactRect.Y }, _focused); til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchMoved(newTouchPoint.to_core_point());
} }
args.Handled(true); args.Handled(true);
@@ -2111,7 +2114,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse || if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen) type == Windows::Devices::Input::PointerDeviceType::Pen)
{ {
_interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point), _interactivity.PointerReleased(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point), TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()), ControlKeyStates(args.KeyModifiers()),
pixelPosition); pixelPosition);

View File

@@ -313,10 +313,13 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal"); Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 0, 0 }; const til::point terminalPosition0{ 0, 0 };
const auto cursorPosition0 = terminalPosition0 * fontSize; const auto cursorPosition0 = terminalPosition0 * fontSize;
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -329,29 +332,28 @@ namespace ControlUnitTests
// move not quite a whole cell, but enough to start a selection // move not quite a whole cell, but enough to start a selection
const til::point terminalPosition1{ 0, 0 }; const til::point terminalPosition1{ 0, 0 };
const til::point cursorPosition1{ 6, 0 }; const til::point cursorPosition1{ 6, 0 };
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition1.to_core_point());
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection"); Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
Log::Comment(L"Drag the mouse down a whole row"); Log::Comment(L"Drag the mouse down a whole row");
const til::point terminalPosition2{ 1, 1 }; const til::point terminalPosition2{ 1, 1 };
const auto cursorPosition2 = terminalPosition2 * fontSize; const auto cursorPosition2 = terminalPosition2 * fontSize;
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition2.to_core_point());
cursorPosition2.to_core_point(),
true);
Log::Comment(L"Verify that there's now two selections (one on each row)"); Log::Comment(L"Verify that there's now two selections (one on each row)");
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
Log::Comment(L"Release the mouse"); Log::Comment(L"Release the mouse");
interactivity->PointerReleased(noMouseDown, interactivity->PointerReleased(0,
noMouseDown,
WM_LBUTTONUP, //pointerUpdateKind WM_LBUTTONUP, //pointerUpdateKind
modifiers, modifiers,
cursorPosition2.to_core_point()); cursorPosition2.to_core_point());
@@ -361,7 +363,8 @@ namespace ControlUnitTests
Log::Comment(L"click outside the current selection"); Log::Comment(L"click outside the current selection");
const til::point terminalPosition3{ 2, 2 }; const til::point terminalPosition3{ 2, 2 };
const auto cursorPosition3 = terminalPosition3 * fontSize; const auto cursorPosition3 = terminalPosition3 * fontSize;
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -372,12 +375,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse"); Log::Comment(L"Drag the mouse");
const til::point terminalPosition4{ 3, 2 }; const til::point terminalPosition4{ 3, 2 };
const auto cursorPosition4 = terminalPosition4 * fontSize; const auto cursorPosition4 = terminalPosition4 * fontSize;
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition4.to_core_point());
cursorPosition4.to_core_point(),
true);
Log::Comment(L"Verify that there's now one selection"); Log::Comment(L"Verify that there's now one selection");
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
} }
@@ -408,10 +410,17 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
// This test is sensitive to the number of rows scrolled per scroll wheel,
// which is reloaded from the system parameters when focus is received.
// Reset it.
interactivity->_rowsToScroll = 1;
Log::Comment(L"Click on the terminal"); Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 5, 5 }; const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize }; const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -426,12 +435,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse just a little"); Log::Comment(L"Drag the mouse just a little");
// move not quite a whole cell, but enough to start a selection // move not quite a whole cell, but enough to start a selection
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } }; const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition1.to_core_point());
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection"); Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
@@ -559,9 +567,12 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal"); Log::Comment(L"Click on the terminal");
const til::point cursorPosition0{ 6, 0 }; const til::point cursorPosition0{ 6, 0 };
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -575,12 +586,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast."); Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 }; const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition1.to_core_point());
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection"); Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
@@ -602,10 +612,13 @@ namespace ControlUnitTests
const auto leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown }; const auto leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const Control::MouseButtonState noMouseDown{}; const Control::MouseButtonState noMouseDown{};
interactivity->GotFocus();
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
Log::Comment(L"Click on the terminal"); Log::Comment(L"Click on the terminal");
const til::point cursorPosition0{ 6, 0 }; const til::point cursorPosition0{ 6, 0 };
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -619,12 +632,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast."); Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 }; const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition1.to_core_point());
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection"); Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
@@ -634,7 +646,8 @@ namespace ControlUnitTests
til::point expectedEnd{ 3, 0 }; // add 1 to x-coordinate because end is exclusive til::point expectedEnd{ 3, 0 }; // add 1 to x-coordinate because end is exclusive
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd()); VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
interactivity->PointerReleased(noMouseDown, interactivity->PointerReleased(0,
noMouseDown,
WM_LBUTTONUP, WM_LBUTTONUP,
modifiers, modifiers,
cursorPosition1.to_core_point()); cursorPosition1.to_core_point());
@@ -644,12 +657,11 @@ namespace ControlUnitTests
Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control"); Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control");
const til::point cursorPosition2{ fontSize.width * 10, 0 }; const til::point cursorPosition2{ fontSize.width * 10, 0 };
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition2.to_core_point());
cursorPosition2.to_core_point(),
false);
Log::Comment(L"The selection should be unchanged."); Log::Comment(L"The selection should be unchanged.");
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor()); VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
@@ -676,6 +688,12 @@ namespace ControlUnitTests
auto expectedViewHeight = 20; auto expectedViewHeight = 20;
auto expectedBufferHeight = 20; auto expectedBufferHeight = 20;
interactivity->GotFocus();
// This test is sensitive to the number of rows scrolled per scroll wheel,
// which is reloaded from the system parameters when focus is received.
// Reset it.
interactivity->_rowsToScroll = 1;
auto scrollChangedHandler = [&](auto&&, const Control::ScrollPositionChangedArgs& args) mutable { auto scrollChangedHandler = [&](auto&&, const Control::ScrollPositionChangedArgs& args) mutable {
VERIFY_ARE_EQUAL(expectedTop, args.ViewTop()); VERIFY_ARE_EQUAL(expectedTop, args.ViewTop());
VERIFY_ARE_EQUAL(expectedViewHeight, args.ViewHeight()); VERIFY_ARE_EQUAL(expectedViewHeight, args.ViewHeight());
@@ -722,7 +740,8 @@ namespace ControlUnitTests
Log::Comment(L"Click on the terminal"); Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 4, 4 }; const til::point terminalPosition0{ 4, 4 };
const auto cursorPosition0 = terminalPosition0 * fontSize; const auto cursorPosition0 = terminalPosition0 * fontSize;
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -735,12 +754,11 @@ namespace ControlUnitTests
// move the mouse as if to make a selection // move the mouse as if to make a selection
const til::point terminalPosition1{ 10, 4 }; const til::point terminalPosition1{ 10, 4 };
const auto cursorPosition1 = terminalPosition1 * fontSize; const auto cursorPosition1 = terminalPosition1 * fontSize;
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition1.to_core_point());
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's still no selection"); Log::Comment(L"Verify that there's still no selection");
VERIFY_IS_FALSE(core->HasSelection()); VERIFY_IS_FALSE(core->HasSelection());
} }
@@ -770,10 +788,13 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal"); Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 5, 5 }; const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize }; const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -788,12 +809,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse just a little"); Log::Comment(L"Drag the mouse just a little");
// move not quite a whole cell, but enough to start a selection // move not quite a whole cell, but enough to start a selection
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } }; const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition1.to_core_point());
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection"); Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
@@ -850,12 +870,11 @@ namespace ControlUnitTests
// character in the buffer (if, albeit in a new location). // character in the buffer (if, albeit in a new location).
// //
// This helps test GH #14462, a regression from #10749. // This helps test GH #14462, a regression from #10749.
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition0.to_core_point());
cursorPosition0.to_core_point(),
true);
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
{ {
const auto anchor{ core->_terminal->GetSelectionAnchor() }; const auto anchor{ core->_terminal->GetSelectionAnchor() };
@@ -876,12 +895,11 @@ namespace ControlUnitTests
expectedAnchor.y -= 1; expectedAnchor.y -= 1;
expectedEnd.y -= 1; expectedEnd.y -= 1;
VERIFY_ARE_EQUAL(scrollbackLength - 3, core->_terminal->GetScrollOffset()); VERIFY_ARE_EQUAL(scrollbackLength - 3, core->_terminal->GetScrollOffset());
interactivity->PointerMoved(leftMouseDown, interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
modifiers, modifiers,
true, // focused, cursorPosition1.to_core_point());
cursorPosition1.to_core_point(),
true);
VERIFY_IS_TRUE(core->HasSelection()); VERIFY_IS_TRUE(core->HasSelection());
{ {
const auto anchor{ core->_terminal->GetSelectionAnchor() }; const auto anchor{ core->_terminal->GetSelectionAnchor() };
@@ -929,7 +947,8 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 5, 5 }; const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize }; const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -978,7 +997,8 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 }; const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 5, 5 }; const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize }; const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -994,7 +1014,8 @@ namespace ControlUnitTests
// The viewport is only 30 wide, so clamping 35 to the buffer size gets // The viewport is only 30 wide, so clamping 35 to the buffer size gets
// us 29, which converted is (32 + 29 + 1) = 62 = '>' // us 29, which converted is (32 + 29 + 1) = 62 = '>'
expectedOutput.push_back(L"\x1b[M >&"); expectedOutput.push_back(L"\x1b[M >&");
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -1009,7 +1030,8 @@ namespace ControlUnitTests
// will be clamped to the top line. // will be clamped to the top line.
expectedOutput.push_back(L"\x1b[M &!"); // 5, 1 expectedOutput.push_back(L"\x1b[M &!"); // 5, 1
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -1025,7 +1047,8 @@ namespace ControlUnitTests
VERIFY_ARE_EQUAL(0, core->ScrollOffset()); VERIFY_ARE_EQUAL(0, core->ScrollOffset());
Log::Comment(L" --- Click on a spot that's still outside the buffer ---"); Log::Comment(L" --- Click on a spot that's still outside the buffer ---");
expectedOutput.push_back(L"\x1b[M >&"); expectedOutput.push_back(L"\x1b[M >&");
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,
@@ -1039,7 +1062,8 @@ namespace ControlUnitTests
Log::Comment(L" --- Click on a spot that's NOW INSIDE the buffer ---"); Log::Comment(L" --- Click on a spot that's NOW INSIDE the buffer ---");
// (32 + 35 + 1) = 68 = 'D' // (32 + 35 + 1) = 68 = 'D'
expectedOutput.push_back(L"\x1b[M D&"); expectedOutput.push_back(L"\x1b[M D&");
interactivity->PointerPressed(leftMouseDown, interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp 0, // timestamp
modifiers, modifiers,