From 5ffac9af01b86f73dd7acacd2e72dfb63ffda1a6 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Sun, 19 Jul 2026 19:39:44 +0000 Subject: [PATCH 38/71] =?UTF-8?q?fix(macos):=20scroll-aware=20mouse=20cell?= =?UTF-8?q?=20mapping=20=E2=80=94=20reliable=20selection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pixelToCell() mapped a view pixel to a grid cell without accounting for the viewport scroll, and it clamped only the low end. sendMouseMoveEvent() takes a screen-relative CellLocation (the engine then applies the whole-line scrollback offset via Viewport::translateScreenToGridCoordinate), so a drag while scrolled selected the wrong rows, and a drag past the bottom or right edge produced an out-of-range cell. Mirror Qt's makeMouseCellLocation(): subtract the smooth-scroll pixel offset (Terminal::smoothScrollPixelOffset()) from y before dividing by the cell height, and clamp both row and column to [0, pageSize-1]. Press/release already pass only a PixelCoordinate and are mapped inside the engine, so they were unaffected; only the move path fed an externally computed cell. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01AeG2jwYMX5gdvvtUnx3PJa --- src/contour_macos/TerminalSession.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/contour_macos/TerminalSession.cpp b/src/contour_macos/TerminalSession.cpp index 4f7f73d2..1769e55c 100644 --- a/src/contour_macos/TerminalSession.cpp +++ b/src/contour_macos/TerminalSession.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -120,8 +121,18 @@ vtbackend::CellLocation TerminalSession::pixelToCell(int x, int y) const auto const cell = _renderer.gridMetrics().cellSize; auto const cw = std::max(1, static_cast(unbox(cell.width))); auto const ch = std::max(1, static_cast(unbox(cell.height))); - auto const col = std::max(0, (x - PageMarginPx) / cw); - auto const line = std::max(0, (y - PageMarginPx) / ch); + + // Content is shifted down by the smooth-scroll pixel offset, so subtract it to map the mouse + // to the cell actually under the pointer. The whole-line scrollback offset is applied by the + // engine (Viewport::translateScreenToGridCoordinate) — this returns a screen-relative cell, + // which is exactly what sendMouseMoveEvent() expects. Mirrors Qt's makeMouseCellLocation(). + auto const sy = y - static_cast(_terminal->smoothScrollPixelOffset()); + + auto const page = _terminal->totalPageSize(); + auto const maxCol = std::max(0, unbox(page.columns) - 1); + auto const maxLine = std::max(0, unbox(page.lines) - 1); + auto const col = std::clamp((x - PageMarginPx) / cw, 0, maxCol); + auto const line = std::clamp((sy - PageMarginPx) / ch, 0, maxLine); return vtbackend::CellLocation { vtbackend::LineOffset(line), vtbackend::ColumnOffset(col) }; }