Fix drag selection freezing over the gutter - #126
Open
BetaYao wants to merge 1 commit into
Open
Conversation
`mouseDragged` clamped the pointer to `max(0, ...)`, but the layout manager can only resolve offsets at or right of `edgeInsets.left` — the strip a gutter is drawn over. Any position further left made `textOffsetAtPoint` return nil, which tripped the guard and returned early, so the whole drag event was dropped: the selection stopped updating (and autoscroll stopped firing) for as long as the pointer sat over the gutter. Dragging up and to the left therefore stranded the selection at the last sample taken inside the text area, typically a few characters into the top line. This is the residue of CodeEditApp/CodeEditSourceEditor#316 and CodeEditApp#100, which fixed the right edge but not the left. Clamp into the inset region instead, so positions over the gutter map to the start of a line. Also anchor the drag on mouse down rather than on the first drag event, which otherwise starts the selection wherever the pointer had already traveled to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
mouseDraggedclamps the pointer into the view before asking the layout manager for a text offset:The lower bound is wrong.
TextLayoutManager.textOffsetAtPoint(_:)resolves positions againstedgeInsets.left— it callsfragment.findContent(atX: xPos - edgeInsets.left), andfindContentwalks fragments starting atxPos: 0, so any negative value returnsnil. The resolvable region starts atedgeInsets.left, not at0.Clamping to
0therefore leaves a dead band of exactlyedgeInsets.leftpoints on the leading edge. That band is not hypothetical:CodeEditSourceEditorsetstextInsets.left = gutterView.frame.width, so it is the full width of the gutter, ~45–60pt.When the pointer enters that band mid-drag,
textOffsetAtPointreturnsnil, theguardfails, andmouseDraggedreturns early — dropping the entire event. The selection stops updating, andautoscroll(with:)never runs either, since it sits after the guard.The user-visible effect is that dragging up and to the left freezes the selection at the last sample taken inside the text area — typically a few characters into the top line, or a whole line short if the drag was quick. Moving the pointer further up along the left margin does nothing at all.
Fix: extract the clamping into
clampToTextArea(_:)and clamp into[edgeInsets.left, frame.width - edgeInsets.right]. Positions over the gutter now map to the start of a line, which matches whatNSTextViewdoes.Also in this PR:
mouseDragAnchorwas set on the firstmouseDraggedrather than onmouseDown, so the selection anchored wherever the pointer had already traveled to by the time that event arrived — a character or two from where the user actually clicked on a quick drag. It is now set inmouseDown. ThemouseDragAnchor == nilbranch inmouseDraggedis kept as a fallback for paths that don't go through the click handlers. This is safe with respect to drag-and-drop of selected text, which is driven by the separateDragSelectionGesturerecognizer inTextView+Drag.swiftand does not depend on thesuper.mouseDragged(with:)call in that branch.I'm happy to split the anchor change into its own PR if you'd prefer — they're both one-line behavioral fixes in the same drag path, so I kept them together.
To Reproduce
CodeEditSourceEditor(or any host that sets a non-zeroedgeInsets.left) with the gutter visible.Related Issues
This is the residue of #100 and CodeEditApp/CodeEditSourceEditor#316. #108 fixed the right edge and the vertical bounds, but the leading edge still bails out, so #316 ("Gutter Intercepts TextView Drag") is only partially fixed — the dead band shrank from "the gutter plus everything right of the view" to "the gutter".
Tests
Added
TextViewMouseTestscovering:mouseDownsets a resolvable drag anchorThe existing suite (72 tests) passes unchanged.
Checklist
Screenshots
Not attached — the bug is a pointer-tracking behavior rather than a rendering issue, so the repro steps above are more useful than a still. Happy to record a screen capture if that helps.