Skip to content
This repository was archived by the owner on Apr 15, 2026. It is now read-only.

Commit 07d54a0

Browse files
committed
Use last touch position to assign a side to touch selections in wrapping editors
FIX: Improve touch tap-selection on line wrapping boundaries. Issue codemirror/dev#1677
1 parent 038f0ae commit 07d54a0

2 files changed

Lines changed: 26 additions & 10 deletions

File tree

src/domchange.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class DOMChange {
2121

2222
constructor(view: EditorView, start: number, end: number, readonly typeOver: boolean) {
2323
this.domChanged = start > -1
24-
let {impreciseHead: iHead, impreciseAnchor: iAnchor} = view.docView
24+
let {impreciseHead: iHead, impreciseAnchor: iAnchor} = view.docView, curSel = view.state.selection
2525
if (view.state.readOnly && start > -1) {
2626
// Ignore changes when the editor is read-only
2727
this.newSel = null
@@ -35,18 +35,18 @@ export class DOMChange {
3535
let domSel = view.observer.selectionRange
3636
let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
3737
!contains(view.contentDOM, domSel.focusNode)
38-
? view.state.selection.main.head
38+
? curSel.main.head
3939
: view.docView.posFromDOM(domSel.focusNode!, domSel.focusOffset)
4040
let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
4141
!contains(view.contentDOM, domSel.anchorNode)
42-
? view.state.selection.main.anchor
42+
? curSel.main.anchor
4343
: view.docView.posFromDOM(domSel.anchorNode!, domSel.anchorOffset)
4444
// iOS will refuse to select the block gaps when doing
4545
// select-all.
4646
// Chrome will put the selection *inside* them, confusing
4747
// posFromDOM
4848
let vp = view.viewport
49-
if ((browser.ios || browser.chrome) && view.state.selection.main.empty && head != anchor &&
49+
if ((browser.ios || browser.chrome) && curSel.main.empty && head != anchor &&
5050
(vp.from > 0 || vp.to < view.state.doc.length)) {
5151
let from = Math.min(head, anchor), to = Math.max(head, anchor)
5252
let offFrom = vp.from - from, offTo = vp.to - to
@@ -55,10 +55,19 @@ export class DOMChange {
5555
anchor = view.state.doc.length
5656
}
5757
}
58-
if (view.inputState.composing > -1 && view.state.selection.ranges.length > 1)
59-
this.newSel = view.state.selection.replaceRange(EditorSelection.range(anchor, head))
60-
else
58+
if (view.inputState.composing > -1 && curSel.ranges.length > 1) {
59+
this.newSel = curSel.replaceRange(EditorSelection.range(anchor, head))
60+
} else if (view.lineWrapping && anchor == head && !(curSel.main.empty && curSel.main.head == head) &&
61+
view.inputState.lastTouchTime > Date.now() - 100) {
62+
// If this is a cursor selection change in a line-wrapping
63+
// editor that may have been a touch, use the last touch
64+
// position to assign a side to the cursor.
65+
let before = view.coordsAtPos(head, -1), assoc = 0
66+
if (before) assoc = view.inputState.lastTouchY <= before.bottom ? -1 : 1
67+
this.newSel = EditorSelection.create([EditorSelection.cursor(head, assoc)])
68+
} else {
6169
this.newSel = EditorSelection.single(anchor, head)
70+
}
6271
}
6372
}
6473
}

src/input.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export class InputState {
1313
lastKeyCode: number = 0
1414
lastKeyTime: number = 0
1515
lastTouchTime = 0
16+
lastTouchX = 0
17+
lastTouchY = 0
1618
lastFocusTime = 0
1719
lastScrollTop = 0
1820
lastScrollLeft = 0
@@ -506,9 +508,14 @@ handlers.keydown = (view, event: KeyboardEvent) => {
506508
return false
507509
}
508510

509-
observers.touchstart = (view, e) => {
510-
view.inputState.lastTouchTime = Date.now()
511-
view.inputState.setSelectionOrigin("select.pointer")
511+
observers.touchstart = (view, e: TouchEvent) => {
512+
let iState = view.inputState, touch = e.targetTouches[0]
513+
iState.lastTouchTime = Date.now()
514+
if (touch) {
515+
iState.lastTouchX = touch.clientX
516+
iState.lastTouchY = touch.clientY
517+
}
518+
iState.setSelectionOrigin("select.pointer")
512519
}
513520

514521
observers.touchmove = view => {

0 commit comments

Comments
 (0)