Canvas: make double-click-to-plant work on desktop #135
@@ -329,6 +329,16 @@ export const Canvas = forwardRef<
|
|||||||
if (!drag.current) drag.current = { t: 'pan', sx: e.clientX, sy: e.clientY, tx: st.vp.tx, ty: st.vp.ty, moved: false, th: thresh(e) }
|
if (!drag.current) drag.current = { t: 'pan', sx: e.clientX, sy: e.clientY, tx: st.vp.tx, ty: st.vp.ty, moved: false, th: thresh(e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The last press on an object, for double-click detection. Detected here
|
||||||
|
// rather than with onDoubleClick on the object's <g>: track() captures the
|
||||||
|
// pointer on the SVG root, and capture retargets the compatibility click
|
||||||
|
// and dblclick events to the root, so a dblclick handler on the object
|
||||||
|
// never fires — which is why "double-click a bed to plant it" did nothing
|
||||||
|
// from the Organic rebuild until this was noticed (2026-08-23).
|
||||||
|
const lastPress = useRef<{ id: number; at: number; x: number; y: number } | null>(null)
|
||||||
|
const DOUBLE_PRESS_MS = 400
|
||||||
|
const DOUBLE_PRESS_PX = 12
|
||||||
|
|
||||||
const objDown = (o: EditorObject) => (e: ReactPointerEvent) => {
|
const objDown = (o: EditorObject) => (e: ReactPointerEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
track(e)
|
track(e)
|
||||||
@@ -342,6 +352,17 @@ export const Canvas = forwardRef<
|
|||||||
}
|
}
|
||||||
// Dimmed siblings stay inert inside a focused bed.
|
// Dimmed siblings stay inert inside a focused bed.
|
||||||
if (st.focusId != null && o.id !== st.focusId) return
|
if (st.focusId != null && o.id !== st.focusId) return
|
||||||
|
const prev = lastPress.current
|
||||||
|
const now = performance.now()
|
||||||
|
lastPress.current = { id: o.id, at: now, x: e.clientX, y: e.clientY }
|
||||||
|
if (prev && prev.id === o.id && now - prev.at < DOUBLE_PRESS_MS && Math.hypot(e.clientX - prev.x, e.clientY - prev.y) < DOUBLE_PRESS_PX) {
|
||||||
|
// Second press of a double-click: focus the bed to plant it. No drag
|
||||||
|
// starts, so the matching pointerup has nothing to select into Plot.
|
||||||
|
lastPress.current = null
|
||||||
|
drag.current = null
|
||||||
|
if (o.plantable) focusObject(o)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!latest.current.canEdit) {
|
if (!latest.current.canEdit) {
|
||||||
st.setSel({ type: 'object', id: o.id })
|
st.setSel({ type: 'object', id: o.id })
|
||||||
st.setTab('plot')
|
st.setTab('plot')
|
||||||
@@ -562,7 +583,6 @@ export const Canvas = forwardRef<
|
|||||||
opacity={dim ? DIM_OBJECT : 1}
|
opacity={dim ? DIM_OBJECT : 1}
|
||||||
style={{ cursor: dim ? 'default' : canEdit ? 'grab' : 'pointer' }}
|
style={{ cursor: dim ? 'default' : canEdit ? 'grab' : 'pointer' }}
|
||||||
onPointerDown={objDown(o)}
|
onPointerDown={objDown(o)}
|
||||||
onDoubleClick={o.plantable && !dim ? () => focusObject(o) : undefined}
|
|
||||||
>
|
>
|
||||||
{o.shape === 'circle' ? (
|
{o.shape === 'circle' ? (
|
||||||
<ellipse rx={o.widthCm / 2} ry={o.heightCm / 2} {...common} />
|
<ellipse rx={o.widthCm / 2} ry={o.heightCm / 2} {...common} />
|
||||||
|
|||||||
Reference in New Issue
Block a user