From 254dfdfd77c4abde53240f7d8ca3558e08688493 Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Fri, 9 Sep 2022 09:42:46 +0100 Subject: [PATCH] Fix buttons (again) (#976) --- packages/core/src/hooks/useBoundsEvents.tsx | 39 +++++++++-- .../core/src/hooks/useBoundsHandleEvents.tsx | 52 +++++++++++---- packages/core/src/hooks/useCanvasEvents.tsx | 41 ++++++++++-- packages/core/src/hooks/useHandleEvents.tsx | 39 +++++++++-- packages/core/src/hooks/useShapeEvents.tsx | 65 ++++++++++++++----- packages/core/src/hooks/useZoomEvents.ts | 2 +- packages/tldraw/src/hooks/useCursor.ts | 6 +- 7 files changed, 196 insertions(+), 48 deletions(-) diff --git a/packages/core/src/hooks/useBoundsEvents.tsx b/packages/core/src/hooks/useBoundsEvents.tsx index 19dee2a8c..7c9d4dc98 100644 --- a/packages/core/src/hooks/useBoundsEvents.tsx +++ b/packages/core/src/hooks/useBoundsEvents.tsx @@ -10,45 +10,70 @@ export function useBoundsEvents() { if ((e as any).dead) return else (e as any).dead = true if (!inputs.pointerIsValid(e)) return - if (e.buttons === 2) { + + // On right click + if (e.button === 2) { callbacks.onRightPointBounds?.(inputs.pointerDown(e, 'bounds'), e) return } + const info = inputs.pointerDown(e, 'bounds') e.currentTarget?.setPointerCapture(e.pointerId) - if (e.buttons === 1) { + + // On left click + if (e.button === 0) { callbacks.onPointBounds?.(info, e) } + + // On left or middle click callbacks.onPointerDown?.(info, e) }, onPointerUp: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true - if (e.buttons === 2) return + + // On right click + if (e.button === 2) { + return + } + inputs.activePointer = undefined + if (!inputs.pointerIsValid(e)) return + const isDoubleClick = inputs.isDoubleClick() + const info = inputs.pointerUp(e, 'bounds') + if (e.currentTarget.hasPointerCapture(e.pointerId)) { e.currentTarget?.releasePointerCapture(e.pointerId) } - if (isDoubleClick && !(info.altKey || info.metaKey)) { - callbacks.onDoubleClickBounds?.(info, e) - } - if (e.buttons === 1) { + + // On left click up + if (e.button === 0) { + // On double click + if (isDoubleClick && !(info.altKey || info.metaKey)) { + callbacks.onDoubleClickBounds?.(info, e) + } + callbacks.onReleaseBounds?.(info, e) } + + // On left or middle click up callbacks.onPointerUp?.(info, e) }, onPointerMove: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true if (!inputs.pointerIsValid(e)) return + + // On left click move if (e.buttons === 1) { if (e.currentTarget.hasPointerCapture(e.pointerId)) { callbacks.onDragBounds?.(inputs.pointerMove(e, 'bounds'), e) } } + const info = inputs.pointerMove(e, 'bounds') callbacks.onPointerMove?.(info, e) }, diff --git a/packages/core/src/hooks/useBoundsHandleEvents.tsx b/packages/core/src/hooks/useBoundsHandleEvents.tsx index 6c9c543eb..3bde5e930 100644 --- a/packages/core/src/hooks/useBoundsHandleEvents.tsx +++ b/packages/core/src/hooks/useBoundsHandleEvents.tsx @@ -11,15 +11,22 @@ export function useBoundsHandleEvents( (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true - if (e.buttons === 2) return if (!inputs.pointerIsValid(e)) return + const info = inputs.pointerDown(e, id) - if (e.buttons === 1) { - if (inputs.isDoubleClick() && !(info.altKey || info.metaKey)) { - callbacks.onDoubleClickBoundsHandle?.(info, e) - } + + if (e.button === 2) { + // On right click + callbacks.onRightPointBoundsHandle?.(info, e) + return + } + + // On left click + if (e.button === 0) { callbacks.onPointBoundsHandle?.(info, e) } + + // On middle or left click callbacks.onPointerDown?.(info, e) }, [inputs, callbacks, id] @@ -29,12 +36,25 @@ export function useBoundsHandleEvents( (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true - if (e.buttons === 2) return - if (!inputs.pointerIsValid(e)) return + + // On right click + if (e.button === 2 || !inputs.pointerIsValid(e)) return + const info = inputs.pointerUp(e, id) - if (e.buttons === 1) { + + const isDoubleClick = inputs.isDoubleClick() + + // On left click up + if (e.button === 0) { + // On double left click + if (isDoubleClick && !(info.altKey || info.metaKey)) { + callbacks.onDoubleClickBoundsHandle?.(info, e) + } + callbacks.onReleaseBoundsHandle?.(info, e) } + + // On middle or left click up callbacks.onPointerUp?.(info, e) }, [inputs, callbacks, id] @@ -44,14 +64,24 @@ export function useBoundsHandleEvents( (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true - if (e.buttons === 2) return + if (!inputs.pointerIsValid(e)) return + + // On right click + if (e.buttons === 2) { + return + } + + const info = inputs.pointerMove(e, id) + + // On left click drag if (e.buttons === 1) { if (e.currentTarget.hasPointerCapture(e.pointerId)) { - callbacks.onDragBoundsHandle?.(inputs.pointerMove(e, id), e) + callbacks.onDragBoundsHandle?.(info, e) } } - const info = inputs.pointerMove(e, id) + + // On left or middle click drag callbacks.onPointerMove?.(info, e) }, [inputs, callbacks, id] diff --git a/packages/core/src/hooks/useCanvasEvents.tsx b/packages/core/src/hooks/useCanvasEvents.tsx index f055d7af0..8be6e336a 100644 --- a/packages/core/src/hooks/useCanvasEvents.tsx +++ b/packages/core/src/hooks/useCanvasEvents.tsx @@ -10,44 +10,73 @@ export function useCanvasEvents() { if ((e as any).dead) return else (e as any).dead = true if (!inputs.pointerIsValid(e)) return - if (e.buttons === 2) return - if (!inputs.pointerIsValid(e)) return + + // On right click + if (e.button === 2) { + return + } + e.currentTarget.setPointerCapture(e.pointerId) + const info = inputs.pointerDown(e, 'canvas') - if (e.buttons === 1) { + + // On left click down + if (e.button === 0) { callbacks.onPointCanvas?.(info, e) } + + // On left or middle click down callbacks.onPointerDown?.(info, e) }, onPointerMove: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true + if (!inputs.pointerIsValid(e)) return + const info = inputs.pointerMove(e, 'canvas') + + // On left click drag if (e.buttons === 1) { if (e.currentTarget.hasPointerCapture(e.pointerId)) { callbacks.onDragCanvas?.(info, e) } } + + // On left or middle click drag callbacks.onPointerMove?.(info, e) }, onPointerUp: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true - if (e.buttons === 2) return + inputs.activePointer = undefined if (!inputs.pointerIsValid(e)) return - const isDoubleClick = inputs.isDoubleClick() + + // On right click up + if (e.button === 2) { + return + } + const info = inputs.pointerUp(e, 'canvas') + + const isDoubleClick = inputs.isDoubleClick() + + // Release pointer capture, if any if (e.currentTarget.hasPointerCapture(e.pointerId)) { e.currentTarget?.releasePointerCapture(e.pointerId) } - if (e.buttons === 1) { + + // On left click up + if (e.button === 0) { if (isDoubleClick && !(info.altKey || info.metaKey)) { callbacks.onDoubleClickCanvas?.(info, e) } + callbacks.onReleaseCanvas?.(info, e) } + + // On left or middle click up callbacks.onPointerUp?.(info, e) }, onDrop: callbacks.onDrop, diff --git a/packages/core/src/hooks/useHandleEvents.tsx b/packages/core/src/hooks/useHandleEvents.tsx index 879fce3b4..dec004743 100644 --- a/packages/core/src/hooks/useHandleEvents.tsx +++ b/packages/core/src/hooks/useHandleEvents.tsx @@ -10,45 +10,72 @@ export function useHandleEvents(id: string) { if ((e as any).dead) return else (e as any).dead = true if (!inputs.pointerIsValid(e)) return - if (e.buttons === 2) return - if (!inputs.pointerIsValid(e)) return + + // On left click down + if (e.button === 2) { + return + } + e.currentTarget?.setPointerCapture(e.pointerId) + const info = inputs.pointerDown(e, id) - if (e.buttons === 1) { + // On left click down + if (e.button === 0) { callbacks.onPointHandle?.(info, e) } + + // On left or middle click down callbacks.onPointerDown?.(info, e) }, onPointerUp: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true - if (e.buttons === 2) return + if (!inputs.pointerIsValid(e)) return + + // Right click up + if (e.button === 2) { + return + } + const isDoubleClick = inputs.isDoubleClick() const info = inputs.pointerUp(e, id) + if (e.currentTarget.hasPointerCapture(e.pointerId)) { e.currentTarget?.releasePointerCapture(e.pointerId) - if (e.buttons === 1) { + + // On left click up + if (e.button === 0) { if (isDoubleClick && !(info.altKey || info.metaKey)) { callbacks.onDoubleClickHandle?.(info, e) } callbacks.onReleaseHandle?.(info, e) } } + + // On any click up callbacks.onPointerUp?.(info, e) }, onPointerMove: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true if (!inputs.pointerIsValid(e)) return - if (e.buttons === 2) return + // On right click drag + if (e.buttons === 2) { + return + } + const info = inputs.pointerMove(e, id) + + // On left click drag if (e.buttons === 1) { if (e.currentTarget.hasPointerCapture(e.pointerId)) { callbacks.onDragHandle?.(info, e) } } + + // On left or middle click drag callbacks.onPointerMove?.(info, e) }, onPointerEnter: (e: React.PointerEvent) => { diff --git a/packages/core/src/hooks/useShapeEvents.tsx b/packages/core/src/hooks/useShapeEvents.tsx index 01f837062..921a30fca 100644 --- a/packages/core/src/hooks/useShapeEvents.tsx +++ b/packages/core/src/hooks/useShapeEvents.tsx @@ -10,13 +10,19 @@ export function useShapeEvents(id: string) { onPointerDown: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true + if (!inputs.pointerIsValid(e)) return - if (e.buttons === 2) { + + // On right click + if (e.button === 2) { callbacks.onRightPointShape?.(inputs.pointerDown(e, id), e) return } + const info = inputs.pointerDown(e, id) + e.currentTarget?.setPointerCapture(e.pointerId) + // If we click "through" the selection bounding box to hit a shape that isn't selected, // treat the event as a bounding box click. Unfortunately there's no way I know to pipe // the event to the actual bounds background element. @@ -25,49 +31,78 @@ export function useShapeEvents(id: string) { Utils.pointInBounds(info.point, rSelectionBounds.current) && !rPageState.current.selectedIds.includes(id) ) { - if (e.buttons === 1) { + // On left click through bounding box foreground + if (e.button === 0) { callbacks.onPointBounds?.(inputs.pointerDown(e, 'bounds'), e) callbacks.onPointShape?.(info, e) } + + // On left or middle click through bounding box foreground callbacks.onPointerDown?.(info, e) return } - if (e.buttons === 1) { + + // On left click + if (e.button === 0) { callbacks.onPointShape?.(info, e) } + + // On middle click or more callbacks.onPointerDown?.(info, e) }, onPointerUp: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true - if (e.buttons === 2) return - inputs.activePointer = undefined if (!inputs.pointerIsValid(e)) return + + // On right clicks + if (e.button === 2) { + return + } + + inputs.activePointer = undefined + const isDoubleClick = inputs.isDoubleClick() + const info = inputs.pointerUp(e, id) - if (e.currentTarget.hasPointerCapture(e.pointerId)) { + + // Release pointer capture, if any + if (e.pointerId && e.currentTarget.hasPointerCapture(e.pointerId)) { e.currentTarget?.releasePointerCapture(e.pointerId) } - if (e.buttons === 1) { + + // On left click up + if (e.button === 0) { if (isDoubleClick && !(info.altKey || info.metaKey)) { callbacks.onDoubleClickShape?.(info, e) } callbacks.onReleaseShape?.(info, e) } + + // On left or middle click up callbacks.onPointerUp?.(info, e) }, onPointerMove: (e: React.PointerEvent) => { if ((e as any).dead) return else (e as any).dead = true - if (e.buttons === 2) return - if (!inputs.pointerIsValid(e)) return - if (inputs.pointer && e.pointerId !== inputs.pointer.pointerId) return - const info = inputs.pointerMove(e, id) - if (e.buttons === 1) { - if (e.currentTarget.hasPointerCapture(e.pointerId)) { - callbacks.onDragShape?.(info, e) - } + + // On right click drag + if ( + e.buttons === 2 || + !inputs.pointerIsValid(e) || + (inputs.pointer && e.pointerId !== inputs.pointer.pointerId) + ) { + return } + + const info = inputs.pointerMove(e, id) + + // On left click drag + if (e.buttons === 1 && e.currentTarget.hasPointerCapture(e.pointerId)) { + callbacks.onDragShape?.(info, e) + } + + // Otherwise... callbacks.onPointerMove?.(info, e) }, onPointerEnter: (e: React.PointerEvent) => { diff --git a/packages/core/src/hooks/useZoomEvents.ts b/packages/core/src/hooks/useZoomEvents.ts index 3e67b2b1e..2312a87e2 100644 --- a/packages/core/src/hooks/useZoomEvents.ts +++ b/packages/core/src/hooks/useZoomEvents.ts @@ -36,7 +36,7 @@ export function useZoomEvents( const [x, y, z] = normalizeWheel(e) - // alt+scroll or ctrl+scroll = zoom + // alt+scroll or ctrl+scroll = zoom (when not clicking) if ((e.altKey || e.ctrlKey || e.metaKey) && e.buttons === 0) { const point = inputs.pointer?.point ?? [bounds.width / 2, bounds.height / 2] const delta = [...point, z * 0.618] diff --git a/packages/tldraw/src/hooks/useCursor.ts b/packages/tldraw/src/hooks/useCursor.ts index b3ef1aaca..5cc02a45a 100644 --- a/packages/tldraw/src/hooks/useCursor.ts +++ b/packages/tldraw/src/hooks/useCursor.ts @@ -30,11 +30,13 @@ export function useCursor(ref: RefObject) { const onPointerDown = (e: PointerEvent) => { isPointing = true - if (e.buttons === 4) { + // On middle mouse down + if (e.button === 1) { elm.setAttribute('style', 'cursor: grabbing !important') } - if (e.buttons === 1) { + // On left mouse down + if (e.button === 0) { if (isSpacePanning) { elm.setAttribute('style', 'cursor: grabbing !important') }