[firefox] Fix the pointer getting stuck down when you press the control key (#1390)
This PR fixes a bug in firefox where the pointer can get stuck down while pressing the control key. It achieves this by taking a previous fix (specifically for `useShapeEvents`), and it applies the fix at a deeper level (within `app.dispatch`). ## Before ![2023-05-16 at 15 33 02 - Crimson Coyote](https://github.com/tldraw/tldraw/assets/15892272/7d4b5bb1-a2e5-400c-9935-fddcc9645e52) ## After ![2023-05-16 at 15 34 03 - Purple Panda](https://github.com/tldraw/tldraw/assets/15892272/34a598b2-bf6d-4847-8ce9-a3d52c418174) ### Change Type - [x] `patch` — Bug Fix ### Test Plan 1. Use firefox. 2. On the canvas... pointer down (to start a selection box). 3. Control key down. 4. Pointer up. 5. Make sure that that the selection box is gone. ^ Repeat the above for: Pointer down on a shape. Pointer down on a handle. ### Release Notes - [Firefox] Fixed a bug where the pointer could get stuck down when the control key is held down.
This commit is contained in:
parent
f59bfe01b1
commit
9c28d8a6bd
4 changed files with 27 additions and 29 deletions
|
@ -156,6 +156,8 @@ export class App extends EventEmitter<TLEventMap> {
|
||||||
set canMoveCamera(canMove: boolean);
|
set canMoveCamera(canMove: boolean);
|
||||||
get canRedo(): boolean;
|
get canRedo(): boolean;
|
||||||
get canUndo(): boolean;
|
get canUndo(): boolean;
|
||||||
|
// @internal (undocumented)
|
||||||
|
capturedPointerId: null | number;
|
||||||
centerOnPoint(x: number, y: number, opts?: AnimationOptions): this;
|
centerOnPoint(x: number, y: number, opts?: AnimationOptions): this;
|
||||||
// @internal
|
// @internal
|
||||||
protected _clickManager: ClickManager;
|
protected _clickManager: ClickManager;
|
||||||
|
|
|
@ -3527,6 +3527,9 @@ export class App extends EventEmitter<TLEventMap> {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
private _selectedIdsAtPointerDown: TLShapeId[] = []
|
private _selectedIdsAtPointerDown: TLShapeId[] = []
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
capturedPointerId: number | null = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch an event to the app.
|
* Dispatch an event to the app.
|
||||||
*
|
*
|
||||||
|
@ -3742,6 +3745,12 @@ export class App extends EventEmitter<TLEventMap> {
|
||||||
case 'pointer_down': {
|
case 'pointer_down': {
|
||||||
this._selectedIdsAtPointerDown = this.selectedIds.slice()
|
this._selectedIdsAtPointerDown = this.selectedIds.slice()
|
||||||
|
|
||||||
|
// Firefox bug fix...
|
||||||
|
// If it's a left-mouse-click, we store the pointer id for later user
|
||||||
|
if (info.button === 0) {
|
||||||
|
this.capturedPointerId = info.pointerId
|
||||||
|
}
|
||||||
|
|
||||||
// Add the button from the buttons set
|
// Add the button from the buttons set
|
||||||
inputs.buttons.add(info.button)
|
inputs.buttons.add(info.button)
|
||||||
|
|
||||||
|
@ -3833,6 +3842,14 @@ export class App extends EventEmitter<TLEventMap> {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Firefox bug fix...
|
||||||
|
// If it's the same pointer that we stored earlier...
|
||||||
|
// ... then it's probably still a left-mouse-click!
|
||||||
|
if (this.capturedPointerId === info.pointerId) {
|
||||||
|
this.capturedPointerId = null
|
||||||
|
info.button = 0
|
||||||
|
}
|
||||||
|
|
||||||
if (inputs.isPanning) {
|
if (inputs.isPanning) {
|
||||||
if (info.button === 1) {
|
if (info.button === 1) {
|
||||||
if (!this.inputs.keys.has(' ')) {
|
if (!this.inputs.keys.has(' ')) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ export function useCanvasEvents() {
|
||||||
|
|
||||||
function onPointerUp(e: React.PointerEvent) {
|
function onPointerUp(e: React.PointerEvent) {
|
||||||
if ((e as any).isKilled) return
|
if ((e as any).isKilled) return
|
||||||
if (e.button !== 0 && e.button !== 1 && e.button !== 5) return
|
if (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return
|
||||||
lastX = e.clientX
|
lastX = e.clientX
|
||||||
lastY = e.clientY
|
lastY = e.clientY
|
||||||
|
|
||||||
|
|
|
@ -6,38 +6,18 @@ import { preventDefault, releasePointerCapture, setPointerCapture } from '../uti
|
||||||
import { getPointerInfo } from '../utils/svg'
|
import { getPointerInfo } from '../utils/svg'
|
||||||
import { useApp } from './useApp'
|
import { useApp } from './useApp'
|
||||||
|
|
||||||
const pointerEventHandler = (
|
const pointerEventHandler = (app: App, shapeId: TLShapeId, name: TLPointerEventName) => {
|
||||||
app: App,
|
|
||||||
shapeId: TLShapeId,
|
|
||||||
name: TLPointerEventName,
|
|
||||||
capturedPointerIdLookup: Set<string>
|
|
||||||
) => {
|
|
||||||
return (e: React.PointerEvent) => {
|
return (e: React.PointerEvent) => {
|
||||||
if (name !== 'pointer_move' && app.pageState.editingId === shapeId) (e as any).isKilled = true
|
if (name !== 'pointer_move' && app.pageState.editingId === shapeId) (e as any).isKilled = true
|
||||||
if ((e as any).isKilled) return
|
if ((e as any).isKilled) return
|
||||||
|
|
||||||
let pointerInfo = getPointerInfo(e, app.getContainer())
|
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'pointer_down': {
|
case 'pointer_down': {
|
||||||
if (e.button !== 0 && e.button !== 1 && e.button !== 2) return
|
if (e.button !== 0 && e.button !== 1 && e.button !== 2) return
|
||||||
setPointerCapture(e.currentTarget, e)
|
setPointerCapture(e.currentTarget, e)
|
||||||
if (e.button === 0) {
|
|
||||||
capturedPointerIdLookup.add(`pointer_down:${e.pointerId}:0`)
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'pointer_up': {
|
case 'pointer_up': {
|
||||||
const key = `pointer_down:${e.pointerId}:0`
|
|
||||||
// Due to an issue with how firefox handles click events, see <https://linear.app/tldraw/issue/TLD-1056/firefox-pointer-events-do-not-fire-when-control-clicking>
|
|
||||||
if (capturedPointerIdLookup.has(key)) {
|
|
||||||
// Because we've tracked the pointer event as a pointer_down with button 0, we can assume this is the invalid right click FF issue.
|
|
||||||
pointerInfo = {
|
|
||||||
...pointerInfo,
|
|
||||||
button: 0,
|
|
||||||
}
|
|
||||||
capturedPointerIdLookup.delete(key)
|
|
||||||
}
|
|
||||||
releasePointerCapture(e.currentTarget, e)
|
releasePointerCapture(e.currentTarget, e)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -55,7 +35,7 @@ const pointerEventHandler = (
|
||||||
target: 'shape',
|
target: 'shape',
|
||||||
shape,
|
shape,
|
||||||
name,
|
name,
|
||||||
...pointerInfo,
|
...getPointerInfo(e, app.getContainer()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +44,6 @@ export function useShapeEvents(id: TLShapeId) {
|
||||||
const app = useApp()
|
const app = useApp()
|
||||||
|
|
||||||
return React.useMemo(() => {
|
return React.useMemo(() => {
|
||||||
const capturedPointerIdLookup = new Set<string>()
|
|
||||||
function onTouchStart(e: React.TouchEvent) {
|
function onTouchStart(e: React.TouchEvent) {
|
||||||
;(e as any).isKilled = true
|
;(e as any).isKilled = true
|
||||||
preventDefault(e)
|
preventDefault(e)
|
||||||
|
@ -75,7 +54,7 @@ export function useShapeEvents(id: TLShapeId) {
|
||||||
preventDefault(e)
|
preventDefault(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePointerMove = pointerEventHandler(app, id, 'pointer_move', capturedPointerIdLookup)
|
const handlePointerMove = pointerEventHandler(app, id, 'pointer_move')
|
||||||
|
|
||||||
// Track the last screen point
|
// Track the last screen point
|
||||||
let lastX: number, lastY: number
|
let lastX: number, lastY: number
|
||||||
|
@ -90,10 +69,10 @@ export function useShapeEvents(id: TLShapeId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onPointerDown: pointerEventHandler(app, id, 'pointer_down', capturedPointerIdLookup),
|
onPointerDown: pointerEventHandler(app, id, 'pointer_down'),
|
||||||
onPointerUp: pointerEventHandler(app, id, 'pointer_up', capturedPointerIdLookup),
|
onPointerUp: pointerEventHandler(app, id, 'pointer_up'),
|
||||||
onPointerEnter: pointerEventHandler(app, id, 'pointer_enter', capturedPointerIdLookup),
|
onPointerEnter: pointerEventHandler(app, id, 'pointer_enter'),
|
||||||
onPointerLeave: pointerEventHandler(app, id, 'pointer_leave', capturedPointerIdLookup),
|
onPointerLeave: pointerEventHandler(app, id, 'pointer_leave'),
|
||||||
onPointerMove,
|
onPointerMove,
|
||||||
onTouchStart,
|
onTouchStart,
|
||||||
onTouchEnd,
|
onTouchEnd,
|
||||||
|
|
Loading…
Reference in a new issue