From 8140092162f55a50327e2b7dc09bbf08cdef05e6 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 17 Mar 2026 14:46:36 -0700 Subject: [PATCH] Improve UX for selection during VTMM (#19973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Selection in VT mouse mode has been feeling a little weird. I've made a few changes in this space to improve the overall experience when in vt mouse mode: - don't round selection positions: this means that you now highlight the cell you're on if you're going right, and the adjacent cell if you're going left - fix drag-left excluding the current cell - #9608: shift+click now clears any existing selection instead of extending it. This feels more intuitive since Shift already works as the override modifier Somewhat related to #18106 ## Validation ✅ alt selections feel consistent ✅ selecting in VTMM feels accurate (selects the cell we started on) ✅ creating new selections (aka clearing old selection) in VTMM feels intuitive (forwards and backwards) Closes #9608 (cherry picked from commit 2e33056fd82fcad0bbbe426478d6a686bf612483) Service-Card-Id: PVTI_lADOAF3p4s4BBcTlzgna3WE Service-Version: 1.24 --- src/cascadia/TerminalControl/ControlCore.cpp | 5 ++++- .../TerminalControl/ControlInteractivity.cpp | 21 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index fb7d904703..04cc91ca33 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1994,7 +1994,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // the selection (we need to reset selection on double-click or // triple-click, so it captures the word or the line, rather than // extending the selection) - if (_terminal->IsSelectionActive() && (!shiftEnabled || isOnOriginalPosition)) + // - GH#9608: VT mouse mode is enabled. In this mode, Shift is used + // to override mouse input, so Shift+Click should start a fresh + // selection rather than extending the previous one. + if (_terminal->IsSelectionActive() && (!shiftEnabled || isOnOriginalPosition || _terminal->IsTrackingMouseInput())) { // Reset the selection _terminal->ClearSelection(); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index ec249d68f6..37e4bae5e5 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -300,8 +300,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation } const auto isOnOriginalPosition = _lastMouseClickPosNoSelection == pixelPosition; - // Rounded coordinates for text selection - _core->LeftClickOnTerminal(_getTerminalPosition(til::point{ pixelPosition }, true), + // Rounded coordinates for text selection. + // Don't round in VT mouse mode; cell-level precision matters more + const auto round = !_core->IsVtMouseModeEnabled(); + _core->LeftClickOnTerminal(_getTerminalPosition(til::point{ pixelPosition }, round), multiClickMapper, altEnabled, shiftEnabled, @@ -311,8 +313,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (_core->HasSelection()) { // GH#9787: if selection is active we don't want to track the touchdown position - // so that dragging the mouse will extend the selection rather than starting the new one - _singleClickTouchdownPos = std::nullopt; + // so that dragging the mouse will extend the selection rather than starting the new one. + // In VT mouse mode, keep tracking the touchdown point so that PointerMoved + // can re-anchor the selection based on drag direction (the dx < 0 adjustment). + // Without this, dragging left wouldn't include the initially clicked cell + // because floored coordinates place the anchor on the cell's left edge. + if (!_core->IsVtMouseModeEnabled()) + { + _singleClickTouchdownPos = std::nullopt; + } } } else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown)) @@ -702,7 +711,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - cursorPosition: in pixels, relative to the origin of the control void ControlInteractivity::SetEndSelectionPoint(const Core::Point pixelPosition) { - _core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }, true)); + // Don't round in VT mouse mode; cell-level precision matters more + const auto round = !_core->IsVtMouseModeEnabled(); + _core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }, round)); _selectionNeedsToBeCopied = true; }