Skip to content

Commit 223fe91

Browse files
committed
Draw fake selection handles on iOS
FIX: Draw custom selection handles on iOS. Issue codemirror/dev#1538
1 parent 218358b commit 223fe91

File tree

3 files changed

+25
-4
lines changed

3 files changed

+25
-4
lines changed

src/draw-selection.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import {EditorSelection, Extension, Facet, combineConfig, Prec, EditorState} from "@codemirror/state"
1+
import {EditorSelection, SelectionRange, Extension, Facet, combineConfig, Prec, EditorState} from "@codemirror/state"
22
import {ViewUpdate, nativeSelectionHidden} from "./extension"
33
import {EditorView} from "./editorview"
4-
import {layer, RectangleMarker} from "./layer"
4+
import {layer, RectangleMarker, getBase} from "./layer"
5+
import browser from "./browser"
56

67
type SelectionConfig = {
78
/// The length of a full cursor blink cycle, in milliseconds.
@@ -93,11 +94,27 @@ function setBlinkRate(state: EditorState, dom: HTMLElement) {
9394
dom.style.animationDuration = state.facet(selectionConfig).cursorBlinkRate + "ms"
9495
}
9596

97+
const enum Handle { Size = 8, Half = Size >> 1 }
98+
99+
function iosSelectionHandles(view: EditorView, range: SelectionRange) {
100+
let handles: RectangleMarker[] = [], base = getBase(view)
101+
let start = view.coordsAtPos(range.from, 1)
102+
if (start) handles.push(new RectangleMarker("cm-selectionHandle", start.left - base.left - Handle.Half,
103+
start.top - base.top - Handle.Size, Handle.Size, Handle.Size))
104+
let end = view.coordsAtPos(range.to, -1)
105+
if (end) handles.push(new RectangleMarker("cm-selectionHandle", end.left - base.left - Handle.Half,
106+
end.bottom - base.top, Handle.Size, Handle.Size))
107+
return handles
108+
}
109+
96110
const selectionLayer = layer({
97111
above: false,
98112
markers(view) {
99-
return view.state.selection.ranges.map(r => r.empty ? [] : RectangleMarker.forRange(view, "cm-selectionBackground", r))
113+
let {selection} = view.state
114+
let markers = selection.ranges.map(r => r.empty ? [] : RectangleMarker.forRange(view, "cm-selectionBackground", r))
100115
.reduce((a, b) => a.concat(b))
116+
if (browser.ios && !selection.main.empty) markers = markers.concat(iosSelectionHandles(view, selection.main))
117+
return markers
101118
},
102119
update(update, dom) {
103120
return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update)

src/layer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class RectangleMarker implements LayerMarker {
8282
}
8383
}
8484

85-
function getBase(view: EditorView) {
85+
export function getBase(view: EditorView) {
8686
let rect = view.scrollDOM.getBoundingClientRect()
8787
let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX
8888
return {left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY}

src/theme.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ export const baseTheme = buildTheme("." + baseThemeID, {
108108
"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
109109
background: "#233"
110110
},
111+
"&.cm-focused .cm-selectionHandle": {
112+
backgroundColor: "currentColor",
113+
borderRadius: "50%"
114+
},
111115

112116
".cm-cursorLayer": {
113117
pointerEvents: "none"

0 commit comments

Comments
 (0)