Skip to content

Commit

Permalink
fix(pointer): set button and buttons properties on PointerEvent (
Browse files Browse the repository at this point in the history
…#1219)

Co-authored-by: Philipp Fritsche <ph.fritsche@gmail.com>
  • Loading branch information
illandril and ph-fritsche authored Jan 21, 2025
1 parent 2edf14d commit 6614f72
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 82 deletions.
47 changes: 25 additions & 22 deletions src/system/pointer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class PointerHost {
this.system = system
this.buttons = new Buttons()
this.mouse = new Mouse()
this.pointers.new('mouse', 'mouse', this.buttons)
}
private readonly mouse
private readonly buttons
Expand All @@ -28,35 +29,32 @@ export class PointerHost {
})()

private readonly pointers = new (class {
private registry = {
mouse: new Pointer({
pointerId: 1,
pointerType: 'mouse',
isPrimary: true,
}),
} as Record<string, Pointer>
private nextId = 2

new(pointerName: string, keyDef: pointerKey) {
private registry: Record<string, Pointer> = {}
private nextId = 1

new(pointerName: string, pointerType: string, buttons: Buttons) {
const isPrimary =
keyDef.pointerType !== 'touch' ||
pointerType !== 'touch' ||
!Object.values(this.registry).some(
p => p.pointerType === 'touch' && !p.isCancelled,
)

if (!isPrimary) {
Object.values(this.registry).forEach(p => {
if (p.pointerType === keyDef.pointerType && !p.isCancelled) {
if (p.pointerType === pointerType && !p.isCancelled) {
p.isMultitouch = true
}
})
}

this.registry[pointerName] = new Pointer({
pointerId: this.nextId++,
pointerType: keyDef.pointerType,
isPrimary,
})
this.registry[pointerName] = new Pointer(
{
pointerId: this.nextId++,
pointerType,
isPrimary,
},
buttons,
)

return this.registry[pointerName]
}
Expand Down Expand Up @@ -84,10 +82,14 @@ export class PointerHost {
keyDef: pointerKey,
position: PointerPosition,
) {
this.devices.get(keyDef.pointerType).addPressed(keyDef)

this.buttons.down(keyDef)

const pointerName = this.getPointerName(keyDef)
const pointer =
keyDef.pointerType === 'touch'
? this.pointers.new(pointerName, keyDef).init(instance, position)
? this.pointers.new(pointerName, keyDef.pointerType, this.buttons)
: this.pointers.get(pointerName)

// TODO: deprecate the following implicit setting of position
Expand All @@ -96,10 +98,11 @@ export class PointerHost {
this.mouse.position = position
}

this.devices.get(keyDef.pointerType).addPressed(keyDef)
if (pointer.pointerType === 'touch') {
pointer.init(instance)
}

this.buttons.down(keyDef)
pointer.down(instance, keyDef)
pointer.down(instance, keyDef.button)

if (pointer.pointerType !== 'touch') {
this.mouse.down(instance, keyDef, pointer.isPrevented)
Expand Down Expand Up @@ -152,7 +155,7 @@ export class PointerHost {
}

if (device.countPressed === 0) {
pointer.up(instance, keyDef)
pointer.up(instance, keyDef.button)
}

if (pointer.pointerType === 'touch') {
Expand Down
36 changes: 25 additions & 11 deletions src/system/pointer/pointer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {type Instance} from '../../setup'
import {assertPointerEvents, getTreeDiff, hasPointerEvents} from '../../utils'
import {isDifferentPointerPosition, pointerKey, PointerPosition} from './shared'
import {isDifferentPointerPosition, PointerPosition} from './shared'
import {Buttons, getMouseEventButton, MouseButton} from './buttons'

type PointerInit = {
pointerId: number
Expand All @@ -9,15 +10,20 @@ type PointerInit = {
}

export class Pointer {
constructor({pointerId, pointerType, isPrimary}: PointerInit) {
constructor(
{pointerId, pointerType, isPrimary}: PointerInit,
buttons: Buttons,
) {
this.pointerId = pointerId
this.pointerType = pointerType
this.isPrimary = isPrimary
this.isMultitouch = !isPrimary
this.buttons = buttons
}
readonly pointerId: number
readonly pointerType: string
readonly isPrimary: boolean
readonly buttons: Buttons

isMultitouch: boolean = false
isCancelled: boolean = false
Expand All @@ -26,9 +32,7 @@ export class Pointer {

position: PointerPosition = {}

init(instance: Instance, position: PointerPosition) {
this.position = position

init(instance: Instance) {
const target = this.getTarget(instance)
const [, enter] = getTreeDiff(null, target)
const init = this.getEventInit()
Expand All @@ -53,7 +57,7 @@ export class Pointer {

const nextTarget = this.getTarget(instance)

const init = this.getEventInit()
const init = this.getEventInit(-1)

const [leave, enter] = getTreeDiff(prevTarget, nextTarget)

Expand Down Expand Up @@ -84,7 +88,7 @@ export class Pointer {
}
}

down(instance: Instance, _keyDef: pointerKey) {
down(instance: Instance, button: MouseButton = 0) {
if (this.isDown) {
return
}
Expand All @@ -96,11 +100,11 @@ export class Pointer {
this.isPrevented = !instance.dispatchUIEvent(
target,
'pointerdown',
this.getEventInit(),
this.getEventInit(button),
)
}

up(instance: Instance, _keyDef: pointerKey) {
up(instance: Instance, button: MouseButton = 0) {
if (!this.isDown) {
return
}
Expand All @@ -110,7 +114,7 @@ export class Pointer {

this.isPrevented = false
this.isDown = false
instance.dispatchUIEvent(target, 'pointerup', this.getEventInit())
instance.dispatchUIEvent(target, 'pointerup', this.getEventInit(button))
}

release(instance: Instance) {
Expand All @@ -133,12 +137,22 @@ export class Pointer {
return this.position.target ?? instance.config.document.body
}

private getEventInit(): PointerEventInit {
private getEventInit(
/**
* The `button` that caused the event.
*
* This should be `-1` if the event is not caused by a button or touch/pen contact,
* e.g. a moving pointer.
*/
button?: MouseButton,
): PointerEventInit {
return {
...this.position.coords,
pointerId: this.pointerId,
pointerType: this.pointerType,
isPrimary: this.isPrimary,
button: getMouseEventButton(button),
buttons: this.buttons.getButtons(),
}
}
}
2 changes: 1 addition & 1 deletion tests/_helpers/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export function addListeners(
isMouseEvent(e)
? `${e.type} - button=${e.button}; buttons=${e.buttons}; detail=${e.detail}`
: isPointerEvent(e)
? `${e.type} - pointerId=${e.pointerId}; pointerType=${e.pointerType}; isPrimary=${e.isPrimary}`
? `${e.type} - pointerId=${e.pointerId}; pointerType=${e.pointerType}; isPrimary=${e.isPrimary}; button=${e.button}; buttons=${e.buttons}`
: e.type,
)
return {snapshot: lines.join('\n')}
Expand Down
70 changes: 35 additions & 35 deletions tests/pointer/click.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ test('click element', async () => {
await user.pointer({keys: '[MouseLeft]', target: element})

expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
mousedown - button=0; buttons=1; detail=1
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
mouseup - button=0; buttons=0; detail=1
click - button=0; buttons=0; detail=1
`)
Expand All @@ -19,7 +19,7 @@ test('secondary button triggers contextmenu', async () => {
await user.pointer({keys: '[MouseRight>]', target: element})

expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=2; buttons=2
mousedown - button=2; buttons=2; detail=1
contextmenu - button=2; buttons=2; detail=0
`)
Expand All @@ -33,14 +33,14 @@ test('double click', async () => {
await user.pointer({keys: '[MouseLeft][MouseLeft]', target: element})

expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
mousedown - button=0; buttons=1; detail=1
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
mouseup - button=0; buttons=0; detail=1
click - button=0; buttons=0; detail=1
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
mousedown - button=0; buttons=1; detail=2
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
mouseup - button=0; buttons=0; detail=2
click - button=0; buttons=0; detail=2
dblclick - button=0; buttons=0; detail=2
Expand Down Expand Up @@ -89,14 +89,14 @@ test('two clicks', async () => {
await user.pointer({keys: '[MouseLeft]'})

expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
mousedown - button=0; buttons=1; detail=1
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
mouseup - button=0; buttons=0; detail=1
click - button=0; buttons=0; detail=1
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
mousedown - button=0; buttons=1; detail=1
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
mouseup - button=0; buttons=0; detail=1
click - button=0; buttons=0; detail=1
`)
Expand All @@ -117,22 +117,22 @@ test('other keys reset click counter', async () => {
})

expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
mousedown - button=0; buttons=1; detail=1
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
mouseup - button=0; buttons=0; detail=1
click - button=0; buttons=0; detail=1
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
mousedown - button=0; buttons=1; detail=2
mousedown - button=2; buttons=3; detail=1
contextmenu - button=2; buttons=3; detail=0
mouseup - button=2; buttons=1; detail=1
auxclick - button=2; buttons=1; detail=1
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
mouseup - button=0; buttons=0; detail=0
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
mousedown - button=0; buttons=1; detail=1
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
mouseup - button=0; buttons=0; detail=1
click - button=0; buttons=0; detail=1
`)
Expand All @@ -148,12 +148,12 @@ test('click per touch device', async () => {
await user.pointer({keys: '[TouchA]', target: element})

expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
pointerover - pointerId=2; pointerType=touch; isPrimary=true
pointerenter - pointerId=2; pointerType=touch; isPrimary=true
pointerdown - pointerId=2; pointerType=touch; isPrimary=true
pointerup - pointerId=2; pointerType=touch; isPrimary=true
pointerout - pointerId=2; pointerType=touch; isPrimary=true
pointerleave - pointerId=2; pointerType=touch; isPrimary=true
pointerover - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerenter - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerdown - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerup - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
pointerout - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
pointerleave - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
mouseover - button=0; buttons=0; detail=0
mouseenter - button=0; buttons=0; detail=0
mousemove - button=0; buttons=0; detail=0
Expand All @@ -172,24 +172,24 @@ test('double click per touch device', async () => {
await user.pointer({keys: '[TouchA][TouchA]', target: element})

expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
pointerover - pointerId=2; pointerType=touch; isPrimary=true
pointerenter - pointerId=2; pointerType=touch; isPrimary=true
pointerdown - pointerId=2; pointerType=touch; isPrimary=true
pointerup - pointerId=2; pointerType=touch; isPrimary=true
pointerout - pointerId=2; pointerType=touch; isPrimary=true
pointerleave - pointerId=2; pointerType=touch; isPrimary=true
pointerover - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerenter - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerdown - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerup - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
pointerout - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
pointerleave - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
mouseover - button=0; buttons=0; detail=0
mouseenter - button=0; buttons=0; detail=0
mousemove - button=0; buttons=0; detail=0
mousedown - button=0; buttons=1; detail=1
mouseup - button=0; buttons=0; detail=1
click - button=0; buttons=0; detail=1
pointerover - pointerId=3; pointerType=touch; isPrimary=true
pointerenter - pointerId=3; pointerType=touch; isPrimary=true
pointerdown - pointerId=3; pointerType=touch; isPrimary=true
pointerup - pointerId=3; pointerType=touch; isPrimary=true
pointerout - pointerId=3; pointerType=touch; isPrimary=true
pointerleave - pointerId=3; pointerType=touch; isPrimary=true
pointerover - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerenter - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerdown - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=1
pointerup - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=0
pointerout - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=0
pointerleave - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=0
mousedown - button=0; buttons=1; detail=2
mouseup - button=0; buttons=0; detail=2
click - button=0; buttons=0; detail=2
Expand Down
Loading

0 comments on commit 6614f72

Please sign in to comment.