Skip to content

Commit 6614f72

Browse files
illandrilph-fritsche
andauthoredJan 21, 2025··
fix(pointer): set button and buttons properties on PointerEvent (#1219)
Co-authored-by: Philipp Fritsche <ph.fritsche@gmail.com>
1 parent 2edf14d commit 6614f72

File tree

5 files changed

+99
-82
lines changed

5 files changed

+99
-82
lines changed
 

‎src/system/pointer/index.ts

+25-22
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export class PointerHost {
1515
this.system = system
1616
this.buttons = new Buttons()
1717
this.mouse = new Mouse()
18+
this.pointers.new('mouse', 'mouse', this.buttons)
1819
}
1920
private readonly mouse
2021
private readonly buttons
@@ -28,35 +29,32 @@ export class PointerHost {
2829
})()
2930

3031
private readonly pointers = new (class {
31-
private registry = {
32-
mouse: new Pointer({
33-
pointerId: 1,
34-
pointerType: 'mouse',
35-
isPrimary: true,
36-
}),
37-
} as Record<string, Pointer>
38-
private nextId = 2
39-
40-
new(pointerName: string, keyDef: pointerKey) {
32+
private registry: Record<string, Pointer> = {}
33+
private nextId = 1
34+
35+
new(pointerName: string, pointerType: string, buttons: Buttons) {
4136
const isPrimary =
42-
keyDef.pointerType !== 'touch' ||
37+
pointerType !== 'touch' ||
4338
!Object.values(this.registry).some(
4439
p => p.pointerType === 'touch' && !p.isCancelled,
4540
)
4641

4742
if (!isPrimary) {
4843
Object.values(this.registry).forEach(p => {
49-
if (p.pointerType === keyDef.pointerType && !p.isCancelled) {
44+
if (p.pointerType === pointerType && !p.isCancelled) {
5045
p.isMultitouch = true
5146
}
5247
})
5348
}
5449

55-
this.registry[pointerName] = new Pointer({
56-
pointerId: this.nextId++,
57-
pointerType: keyDef.pointerType,
58-
isPrimary,
59-
})
50+
this.registry[pointerName] = new Pointer(
51+
{
52+
pointerId: this.nextId++,
53+
pointerType,
54+
isPrimary,
55+
},
56+
buttons,
57+
)
6058

6159
return this.registry[pointerName]
6260
}
@@ -84,10 +82,14 @@ export class PointerHost {
8482
keyDef: pointerKey,
8583
position: PointerPosition,
8684
) {
85+
this.devices.get(keyDef.pointerType).addPressed(keyDef)
86+
87+
this.buttons.down(keyDef)
88+
8789
const pointerName = this.getPointerName(keyDef)
8890
const pointer =
8991
keyDef.pointerType === 'touch'
90-
? this.pointers.new(pointerName, keyDef).init(instance, position)
92+
? this.pointers.new(pointerName, keyDef.pointerType, this.buttons)
9193
: this.pointers.get(pointerName)
9294

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

99-
this.devices.get(keyDef.pointerType).addPressed(keyDef)
101+
if (pointer.pointerType === 'touch') {
102+
pointer.init(instance)
103+
}
100104

101-
this.buttons.down(keyDef)
102-
pointer.down(instance, keyDef)
105+
pointer.down(instance, keyDef.button)
103106

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

154157
if (device.countPressed === 0) {
155-
pointer.up(instance, keyDef)
158+
pointer.up(instance, keyDef.button)
156159
}
157160

158161
if (pointer.pointerType === 'touch') {

‎src/system/pointer/pointer.ts

+25-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {type Instance} from '../../setup'
22
import {assertPointerEvents, getTreeDiff, hasPointerEvents} from '../../utils'
3-
import {isDifferentPointerPosition, pointerKey, PointerPosition} from './shared'
3+
import {isDifferentPointerPosition, PointerPosition} from './shared'
4+
import {Buttons, getMouseEventButton, MouseButton} from './buttons'
45

56
type PointerInit = {
67
pointerId: number
@@ -9,15 +10,20 @@ type PointerInit = {
910
}
1011

1112
export class Pointer {
12-
constructor({pointerId, pointerType, isPrimary}: PointerInit) {
13+
constructor(
14+
{pointerId, pointerType, isPrimary}: PointerInit,
15+
buttons: Buttons,
16+
) {
1317
this.pointerId = pointerId
1418
this.pointerType = pointerType
1519
this.isPrimary = isPrimary
1620
this.isMultitouch = !isPrimary
21+
this.buttons = buttons
1722
}
1823
readonly pointerId: number
1924
readonly pointerType: string
2025
readonly isPrimary: boolean
26+
readonly buttons: Buttons
2127

2228
isMultitouch: boolean = false
2329
isCancelled: boolean = false
@@ -26,9 +32,7 @@ export class Pointer {
2632

2733
position: PointerPosition = {}
2834

29-
init(instance: Instance, position: PointerPosition) {
30-
this.position = position
31-
35+
init(instance: Instance) {
3236
const target = this.getTarget(instance)
3337
const [, enter] = getTreeDiff(null, target)
3438
const init = this.getEventInit()
@@ -53,7 +57,7 @@ export class Pointer {
5357

5458
const nextTarget = this.getTarget(instance)
5559

56-
const init = this.getEventInit()
60+
const init = this.getEventInit(-1)
5761

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

@@ -84,7 +88,7 @@ export class Pointer {
8488
}
8589
}
8690

87-
down(instance: Instance, _keyDef: pointerKey) {
91+
down(instance: Instance, button: MouseButton = 0) {
8892
if (this.isDown) {
8993
return
9094
}
@@ -96,11 +100,11 @@ export class Pointer {
96100
this.isPrevented = !instance.dispatchUIEvent(
97101
target,
98102
'pointerdown',
99-
this.getEventInit(),
103+
this.getEventInit(button),
100104
)
101105
}
102106

103-
up(instance: Instance, _keyDef: pointerKey) {
107+
up(instance: Instance, button: MouseButton = 0) {
104108
if (!this.isDown) {
105109
return
106110
}
@@ -110,7 +114,7 @@ export class Pointer {
110114

111115
this.isPrevented = false
112116
this.isDown = false
113-
instance.dispatchUIEvent(target, 'pointerup', this.getEventInit())
117+
instance.dispatchUIEvent(target, 'pointerup', this.getEventInit(button))
114118
}
115119

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

136-
private getEventInit(): PointerEventInit {
140+
private getEventInit(
141+
/**
142+
* The `button` that caused the event.
143+
*
144+
* This should be `-1` if the event is not caused by a button or touch/pen contact,
145+
* e.g. a moving pointer.
146+
*/
147+
button?: MouseButton,
148+
): PointerEventInit {
137149
return {
138150
...this.position.coords,
139151
pointerId: this.pointerId,
140152
pointerType: this.pointerType,
141153
isPrimary: this.isPrimary,
154+
button: getMouseEventButton(button),
155+
buttons: this.buttons.getButtons(),
142156
}
143157
}
144158
}

‎tests/_helpers/listeners.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export function addListeners(
100100
isMouseEvent(e)
101101
? `${e.type} - button=${e.button}; buttons=${e.buttons}; detail=${e.detail}`
102102
: isPointerEvent(e)
103-
? `${e.type} - pointerId=${e.pointerId}; pointerType=${e.pointerType}; isPrimary=${e.isPrimary}`
103+
? `${e.type} - pointerId=${e.pointerId}; pointerType=${e.pointerType}; isPrimary=${e.isPrimary}; button=${e.button}; buttons=${e.buttons}`
104104
: e.type,
105105
)
106106
return {snapshot: lines.join('\n')}

‎tests/pointer/click.ts

+35-35
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ test('click element', async () => {
55
await user.pointer({keys: '[MouseLeft]', target: element})
66

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

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

3535
expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
36-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
36+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
3737
mousedown - button=0; buttons=1; detail=1
38-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
38+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
3939
mouseup - button=0; buttons=0; detail=1
4040
click - button=0; buttons=0; detail=1
41-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
41+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
4242
mousedown - button=0; buttons=1; detail=2
43-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
43+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
4444
mouseup - button=0; buttons=0; detail=2
4545
click - button=0; buttons=0; detail=2
4646
dblclick - button=0; buttons=0; detail=2
@@ -89,14 +89,14 @@ test('two clicks', async () => {
8989
await user.pointer({keys: '[MouseLeft]'})
9090

9191
expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
92-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
92+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
9393
mousedown - button=0; buttons=1; detail=1
94-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
94+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
9595
mouseup - button=0; buttons=0; detail=1
9696
click - button=0; buttons=0; detail=1
97-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
97+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
9898
mousedown - button=0; buttons=1; detail=1
99-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
99+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
100100
mouseup - button=0; buttons=0; detail=1
101101
click - button=0; buttons=0; detail=1
102102
`)
@@ -117,22 +117,22 @@ test('other keys reset click counter', async () => {
117117
})
118118

119119
expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
120-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
120+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
121121
mousedown - button=0; buttons=1; detail=1
122-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
122+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
123123
mouseup - button=0; buttons=0; detail=1
124124
click - button=0; buttons=0; detail=1
125-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
125+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
126126
mousedown - button=0; buttons=1; detail=2
127127
mousedown - button=2; buttons=3; detail=1
128128
contextmenu - button=2; buttons=3; detail=0
129129
mouseup - button=2; buttons=1; detail=1
130130
auxclick - button=2; buttons=1; detail=1
131-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
131+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
132132
mouseup - button=0; buttons=0; detail=0
133-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
133+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
134134
mousedown - button=0; buttons=1; detail=1
135-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
135+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
136136
mouseup - button=0; buttons=0; detail=1
137137
click - button=0; buttons=0; detail=1
138138
`)
@@ -148,12 +148,12 @@ test('click per touch device', async () => {
148148
await user.pointer({keys: '[TouchA]', target: element})
149149

150150
expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
151-
pointerover - pointerId=2; pointerType=touch; isPrimary=true
152-
pointerenter - pointerId=2; pointerType=touch; isPrimary=true
153-
pointerdown - pointerId=2; pointerType=touch; isPrimary=true
154-
pointerup - pointerId=2; pointerType=touch; isPrimary=true
155-
pointerout - pointerId=2; pointerType=touch; isPrimary=true
156-
pointerleave - pointerId=2; pointerType=touch; isPrimary=true
151+
pointerover - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
152+
pointerenter - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
153+
pointerdown - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
154+
pointerup - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
155+
pointerout - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
156+
pointerleave - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
157157
mouseover - button=0; buttons=0; detail=0
158158
mouseenter - button=0; buttons=0; detail=0
159159
mousemove - button=0; buttons=0; detail=0
@@ -172,24 +172,24 @@ test('double click per touch device', async () => {
172172
await user.pointer({keys: '[TouchA][TouchA]', target: element})
173173

174174
expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
175-
pointerover - pointerId=2; pointerType=touch; isPrimary=true
176-
pointerenter - pointerId=2; pointerType=touch; isPrimary=true
177-
pointerdown - pointerId=2; pointerType=touch; isPrimary=true
178-
pointerup - pointerId=2; pointerType=touch; isPrimary=true
179-
pointerout - pointerId=2; pointerType=touch; isPrimary=true
180-
pointerleave - pointerId=2; pointerType=touch; isPrimary=true
175+
pointerover - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
176+
pointerenter - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
177+
pointerdown - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
178+
pointerup - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
179+
pointerout - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
180+
pointerleave - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
181181
mouseover - button=0; buttons=0; detail=0
182182
mouseenter - button=0; buttons=0; detail=0
183183
mousemove - button=0; buttons=0; detail=0
184184
mousedown - button=0; buttons=1; detail=1
185185
mouseup - button=0; buttons=0; detail=1
186186
click - button=0; buttons=0; detail=1
187-
pointerover - pointerId=3; pointerType=touch; isPrimary=true
188-
pointerenter - pointerId=3; pointerType=touch; isPrimary=true
189-
pointerdown - pointerId=3; pointerType=touch; isPrimary=true
190-
pointerup - pointerId=3; pointerType=touch; isPrimary=true
191-
pointerout - pointerId=3; pointerType=touch; isPrimary=true
192-
pointerleave - pointerId=3; pointerType=touch; isPrimary=true
187+
pointerover - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=1
188+
pointerenter - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=1
189+
pointerdown - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=1
190+
pointerup - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=0
191+
pointerout - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=0
192+
pointerleave - pointerId=3; pointerType=touch; isPrimary=true; button=0; buttons=0
193193
mousedown - button=0; buttons=1; detail=2
194194
mouseup - button=0; buttons=0; detail=2
195195
click - button=0; buttons=0; detail=2

‎tests/pointer/drag.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ test('drag sequence', async () => {
1010
])
1111

1212
expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
13-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
13+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
1414
mousedown - button=0; buttons=1; detail=1
15-
pointermove - pointerId=1; pointerType=mouse; isPrimary=true
15+
pointermove - pointerId=1; pointerType=mouse; isPrimary=true; button=-1; buttons=1
1616
mousemove - button=0; buttons=1; detail=0
17-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
17+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
1818
mouseup - button=0; buttons=0; detail=1
1919
click - button=0; buttons=0; detail=1
2020
`)
@@ -31,11 +31,11 @@ test('drag sequence w/ differing client coordinates', async () => {
3131
])
3232

3333
expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
34-
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true
34+
pointerdown - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=1
3535
mousedown - button=0; buttons=1; detail=1
36-
pointermove - pointerId=1; pointerType=mouse; isPrimary=true
36+
pointermove - pointerId=1; pointerType=mouse; isPrimary=true; button=-1; buttons=1
3737
mousemove - button=0; buttons=1; detail=0
38-
pointerup - pointerId=1; pointerType=mouse; isPrimary=true
38+
pointerup - pointerId=1; pointerType=mouse; isPrimary=true; button=0; buttons=0
3939
mouseup - button=0; buttons=0; detail=1
4040
click - button=0; buttons=0; detail=1
4141
`)
@@ -51,13 +51,13 @@ test('drag touch', async () => {
5151
])
5252

5353
expect(getClickEventsSnapshot()).toMatchInlineSnapshot(`
54-
pointerover - pointerId=2; pointerType=touch; isPrimary=true
55-
pointerenter - pointerId=2; pointerType=touch; isPrimary=true
56-
pointerdown - pointerId=2; pointerType=touch; isPrimary=true
57-
pointermove - pointerId=2; pointerType=touch; isPrimary=true
58-
pointerup - pointerId=2; pointerType=touch; isPrimary=true
59-
pointerout - pointerId=2; pointerType=touch; isPrimary=true
60-
pointerleave - pointerId=2; pointerType=touch; isPrimary=true
54+
pointerover - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
55+
pointerenter - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
56+
pointerdown - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=1
57+
pointermove - pointerId=2; pointerType=touch; isPrimary=true; button=-1; buttons=1
58+
pointerup - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
59+
pointerout - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
60+
pointerleave - pointerId=2; pointerType=touch; isPrimary=true; button=0; buttons=0
6161
mouseover - button=0; buttons=0; detail=0
6262
mouseenter - button=0; buttons=0; detail=0
6363
mousemove - button=0; buttons=0; detail=0

0 commit comments

Comments
 (0)
Please sign in to comment.