Fix double/triple-click text selection
- Track click count with timing/proximity in onMouseDown - Double-click in text: select word (was only handled in dblclick) - Triple-click in text: select all - Double-click to enter editing: select word at click position - Add selectLine/selectLineAt to TextEditor
This commit is contained in:
parent
20827a27d3
commit
6268d8b119
|
|
@ -127,6 +127,17 @@ export class TextEditor {
|
|||
s.cursor = pos
|
||||
}
|
||||
|
||||
selectLine(pos: number): void {
|
||||
const s = this._state
|
||||
if (!s?.paragraph) return
|
||||
const lineNum = s.paragraph.getLineNumberAt(pos)
|
||||
if (lineNum < 0) return
|
||||
const metrics = s.paragraph.getLineMetricsAt(lineNum)
|
||||
if (!metrics) return
|
||||
s.selectionAnchor = metrics.startIndex
|
||||
s.cursor = metrics.endExcludingWhitespaces
|
||||
}
|
||||
|
||||
selectWordAt(x: number, y: number): void {
|
||||
const s = this._state
|
||||
if (!s?.paragraph) return
|
||||
|
|
@ -134,6 +145,13 @@ export class TextEditor {
|
|||
this.selectWord(pos)
|
||||
}
|
||||
|
||||
selectLineAt(x: number, y: number): void {
|
||||
const s = this._state
|
||||
if (!s?.paragraph) return
|
||||
const pos = s.paragraph.getGlyphPositionAtCoordinate(x, y).pos
|
||||
this.selectLine(pos)
|
||||
}
|
||||
|
||||
insert(text: string, node: SceneNode): void {
|
||||
const s = this._state
|
||||
if (!s) return
|
||||
|
|
|
|||
|
|
@ -229,6 +229,12 @@ export function useCanvasInput(
|
|||
) {
|
||||
const drag = ref<DragState | null>(null)
|
||||
const cursorOverride = ref<string | null>(null)
|
||||
let lastClickTime = 0
|
||||
let lastClickX = 0
|
||||
let lastClickY = 0
|
||||
let clickCount = 0
|
||||
const MULTI_CLICK_DELAY = 500
|
||||
const MULTI_CLICK_RADIUS = 5
|
||||
|
||||
function getCoords(e: MouseEvent) {
|
||||
const canvas = canvasRef.value
|
||||
|
|
@ -243,6 +249,20 @@ export function useCanvasInput(
|
|||
function onMouseDown(e: MouseEvent) {
|
||||
store.setHoveredNode(null)
|
||||
const { sx, sy, cx, cy } = getCoords(e)
|
||||
|
||||
const now = performance.now()
|
||||
if (
|
||||
now - lastClickTime < MULTI_CLICK_DELAY &&
|
||||
Math.abs(sx - lastClickX) < MULTI_CLICK_RADIUS &&
|
||||
Math.abs(sy - lastClickY) < MULTI_CLICK_RADIUS
|
||||
) {
|
||||
clickCount++
|
||||
} else {
|
||||
clickCount = 1
|
||||
}
|
||||
lastClickTime = now
|
||||
lastClickX = sx
|
||||
lastClickY = sy
|
||||
const tool = store.state.activeTool
|
||||
|
||||
if (e.button === 1 || tool === 'HAND') {
|
||||
|
|
@ -276,8 +296,14 @@ export function useCanvasInput(
|
|||
const localX = cx - abs.x
|
||||
const localY = cy - abs.y
|
||||
if (localX >= 0 && localY >= 0 && localX <= editNode.width && localY <= editNode.height) {
|
||||
editor.setCursorAt(localX, localY, e.shiftKey)
|
||||
drag.value = { type: 'text-select', startX: cx, startY: cy } as DragState
|
||||
if (clickCount >= 3) {
|
||||
editor.selectAll()
|
||||
} else if (clickCount === 2) {
|
||||
editor.selectWordAt(localX, localY)
|
||||
} else {
|
||||
editor.setCursorAt(localX, localY, e.shiftKey)
|
||||
drag.value = { type: 'text-select', startX: cx, startY: cy } as DragState
|
||||
}
|
||||
store.requestRender()
|
||||
return
|
||||
}
|
||||
|
|
@ -900,19 +926,9 @@ export function useCanvasInput(
|
|||
}
|
||||
|
||||
function onDblClick(e: MouseEvent) {
|
||||
if (store.state.editingTextId) return
|
||||
|
||||
const { cx, cy } = getCoords(e)
|
||||
|
||||
if (store.state.editingTextId) {
|
||||
const editor = store.textEditor
|
||||
const editNode = store.graph.getNode(store.state.editingTextId)
|
||||
if (editor && editNode) {
|
||||
const abs = store.graph.getAbsolutePosition(editNode.id)
|
||||
editor.selectWordAt(cx - abs.x, cy - abs.y)
|
||||
store.requestRender()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const hit =
|
||||
hitTestSectionTitle(cx, cy) ??
|
||||
hitTestComponentLabel(cx, cy) ??
|
||||
|
|
@ -922,10 +938,15 @@ export function useCanvasInput(
|
|||
if (hit.type === 'TEXT') {
|
||||
store.select([hit.id])
|
||||
store.startTextEditing(hit.id)
|
||||
const editor = store.textEditor
|
||||
if (editor) {
|
||||
const abs = store.graph.getAbsolutePosition(hit.id)
|
||||
editor.selectWordAt(cx - abs.x, cy - abs.y)
|
||||
store.requestRender()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Double-click enters components/instances — select the child inside
|
||||
store.select([hit.id])
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue