Skip to content

Commit 5276d9d

Browse files
authored
Process mouse as Pointer events (JetBrains#2748)
This is first part of our two-phase transition for processing mouch and touch events as pointer events. ## Testing This should be tested by QA ## Release Notes ### Fixes - Web - Fix Apple Magic mouse behaviour - https://youtrack.jetbrains.com/issue/CMP-7576
1 parent 15b75c4 commit 5276d9d

5 files changed

Lines changed: 106 additions & 109 deletions

File tree

compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.web.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal val MouseEvent.composeButton get(): PointerButton? {
2222
// `MouseEvent.button` property only guarantees to indicate which buttons are pressed during
2323
// events caused by pressing or releasing one or multiple buttons
2424
when (type) {
25-
"mousedown", "mouseup" -> Unit
25+
"pointerdown", "pointerup" -> Unit
2626
else -> return null
2727
}
2828
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button

compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/ComposeWindowInternal.web.kt

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ import org.w3c.dom.events.Event
116116
import org.w3c.dom.events.KeyboardEvent
117117
import org.w3c.dom.events.MouseEvent
118118
import org.w3c.dom.events.WheelEvent
119+
import org.w3c.dom.pointerevents.PointerEvent
119120

120121
private val actualDensity
121122
get() = window.devicePixelRatio
@@ -374,13 +375,6 @@ internal class ComposeWindow(
374375
private fun initEvents(canvas: HTMLCanvasElement) {
375376
var offset = Offset.Zero
376377

377-
/*
378-
* CMP-9673 [Web] Double touch and mouse events
379-
* If a touch event is followed by mouse events with the same timestamp, the mouse events are ignored.
380-
*/
381-
var finalTouchEventTimestamp: Any? = null
382-
fun MouseEvent.isReal() = timeStamp !== finalTouchEventTimestamp
383-
384378
addTypedEvent<TouchEvent>("touchstart", passive = false) { event ->
385379
canvas.getBoundingClientRect().apply {
386380
offset = Offset(x = left.toFloat(), y = top.toFloat())
@@ -395,32 +389,30 @@ internal class ComposeWindow(
395389

396390
addTypedEvent<TouchEvent>("touchend", passive = false) { event ->
397391
onTouchEvent(event, offset)
398-
finalTouchEventTimestamp = event.timeStamp
399392
}
400393

401394
addTypedEvent<TouchEvent>("touchcancel", passive = false) { event ->
402395
onTouchEvent(event, offset)
403-
finalTouchEventTimestamp = event.timeStamp
404396
}
405397

406-
addTypedEvent<MouseEvent>("mousedown") { event ->
407-
if (event.isReal()) onMouseEvent(event)
398+
addTypedEvent<PointerEvent>("pointerdown") { event ->
399+
onPointerEvent(event)
408400
}
409401

410-
addTypedEvent<MouseEvent>("mouseup") { event ->
411-
if (event.isReal()) onMouseEvent(event)
402+
addTypedEvent<PointerEvent>("pointerup") { event ->
403+
onPointerEvent(event)
412404
}
413405

414-
addTypedEvent<MouseEvent>("mousemove") { event ->
415-
if (event.isReal()) onMouseEvent(event)
406+
addTypedEvent<PointerEvent>("pointermove") { event ->
407+
onPointerEvent(event)
416408
}
417409

418-
addTypedEvent<MouseEvent>("mouseenter") { event ->
419-
if (event.isReal()) onMouseEvent(event)
410+
addTypedEvent<PointerEvent>("pointerenter") { event ->
411+
onPointerEvent(event)
420412
}
421413

422-
addTypedEvent<MouseEvent>("mouseleave") { event ->
423-
if (event.isReal()) onMouseEvent(event)
414+
addTypedEvent<PointerEvent>("pointerleave") { event ->
415+
onPointerEvent(event)
424416
}
425417

426418
addTypedEvent<WheelEvent>("wheel", passive = false) { event ->
@@ -601,18 +593,24 @@ internal class ComposeWindow(
601593
}
602594
}
603595

604-
private fun onMouseEvent(
605-
event: MouseEvent,
596+
private fun onPointerEvent(
597+
event: PointerEvent,
606598
) {
599+
// TODO: we need this guard so that we won't process touch events second time
600+
// see https://youtrack.jetbrains.com/issue/CMP-9745/Switch-to-pointer-events-for-processing-touch-events
601+
if (event.pointerType != "mouse") return
602+
607603
keyboardModeState = KeyboardModeState.Hardware
604+
608605
val eventType = when (event.type) {
609-
"mousedown" -> PointerEventType.Press
610-
"mousemove" -> PointerEventType.Move
611-
"mouseup" -> PointerEventType.Release
612-
"mouseenter" -> PointerEventType.Enter
613-
"mouseleave" -> PointerEventType.Exit
606+
"pointerdown" -> PointerEventType.Press
607+
"pointermove" -> PointerEventType.Move
608+
"pointerup" -> PointerEventType.Release
609+
"pointerenter" -> PointerEventType.Enter
610+
"pointerleave" -> PointerEventType.Exit
614611
else -> PointerEventType.Unknown
615612
}
613+
616614
val result = scene.sendPointerEvent(
617615
eventType = eventType,
618616
position = event.offset,

compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/SelectionContainerTests.kt

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,15 @@ import kotlinx.coroutines.delay
4040
import kotlinx.coroutines.test.runTest
4141
import kotlinx.coroutines.withContext
4242
import org.w3c.dom.HTMLCanvasElement
43-
import org.w3c.dom.events.MouseEvent
44-
import org.w3c.dom.events.MouseEventInit
43+
import org.w3c.dom.pointerevents.PointerEvent
44+
import org.w3c.dom.pointerevents.PointerEventInit
45+
4546

4647
class SelectionContainerTests : OnCanvasTests {
4748

4849
private fun HTMLCanvasElement.doClick() {
49-
dispatchEvent(MouseEvent("mousedown", MouseEventInit(clientX = 8, clientY = 8, buttons = 1, button = 1)))
50-
dispatchEvent(MouseEvent("mouseup", MouseEventInit(clientX = 8, clientY = 8, buttons = 0, button = 1)))
50+
dispatchEvent(PointerEvent("pointerdown", PointerEventInit(clientX = 8, clientY = 8, buttons = 1, button = 1, pointerType = "mouse")))
51+
dispatchEvent(PointerEvent("pointerup", PointerEventInit(clientX = 8, clientY = 8, buttons = 0, button = 1, pointerType = "mouse")))
5152
}
5253

5354
@Test
@@ -81,7 +82,7 @@ class SelectionContainerTests : OnCanvasTests {
8182
}
8283

8384
val canvas = getCanvas()
84-
canvas.dispatchEvent(MouseEvent("mouseenter"))
85+
canvas.dispatchEvent(PointerEvent("pointerenter", PointerEventInit(pointerType = "mouse")))
8586

8687
// single click - no selection expected
8788
canvas.doClick()
@@ -101,8 +102,8 @@ class SelectionContainerTests : OnCanvasTests {
101102

102103
selection = syncChannel.receive()
103104
assertTrue(selection.exists())
104-
assertEquals(0, selection!!.start.offset)
105-
assertEquals(6, selection!!.end.offset)
105+
assertEquals(0, selection.start.offset)
106+
assertEquals(6, selection.end.offset)
106107

107108
withContext(Dispatchers.Default) {
108109
delay(viewConfiguration!!.doubleTapTimeoutMillis)
@@ -143,7 +144,7 @@ class SelectionContainerTests : OnCanvasTests {
143144
}
144145

145146
val canvas = getCanvas()
146-
canvas.dispatchEvent(MouseEvent("mouseenter"))
147+
canvas.dispatchEvent(PointerEvent("pointerenter", PointerEventInit(pointerType = "mouse")))
147148

148149
// triple click
149150
canvas.doClick()
@@ -152,8 +153,8 @@ class SelectionContainerTests : OnCanvasTests {
152153

153154
var selection = syncChannel.receive()
154155
assertTrue(selection.exists())
155-
assertEquals(0, selection!!.start.offset)
156-
assertEquals(27, selection!!.end.offset)
156+
assertEquals(0, selection.start.offset)
157+
assertEquals(27, selection.end.offset)
157158

158159
withContext(Dispatchers.Default) {
159160
delay(viewConfiguration!!.doubleTapTimeoutMillis)
@@ -196,7 +197,7 @@ class SelectionContainerTests : OnCanvasTests {
196197
}
197198

198199
val canvas = getCanvas()
199-
canvas.dispatchEvent(MouseEvent("mouseenter"))
200+
canvas.dispatchEvent(PointerEvent("pointerenter", PointerEventInit(pointerType = "mouse")))
200201

201202
// first single click
202203
canvas.doClick()

compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/input/MouseTextInputTests.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ import kotlin.test.Test
1919
import kotlin.test.assertEquals
2020
import kotlin.test.assertIs
2121
import kotlin.test.assertTrue
22-
import kotlinx.browser.document
2322
import kotlinx.coroutines.Dispatchers
2423
import kotlinx.coroutines.delay
2524
import kotlinx.coroutines.withContext
2625
import kotlinx.coroutines.yield
2726
import org.w3c.dom.HTMLTextAreaElement
28-
import org.w3c.dom.events.MouseEvent
29-
import org.w3c.dom.events.MouseEventInit
27+
import org.w3c.dom.pointerevents.PointerEvent
28+
import org.w3c.dom.pointerevents.PointerEventInit
29+
3030

3131
class MouseTextInputTests: OnCanvasTests {
3232
@Test
@@ -61,11 +61,11 @@ class MouseTextInputTests: OnCanvasTests {
6161
assertTrue(textFieldWidth > 0, "TextField width should be positive")
6262

6363
val canvas = getCanvas()
64-
canvas.dispatchEvent(MouseEvent("mouseenter"))
64+
canvas.dispatchEvent(PointerEvent("pointerenter", PointerEventInit(pointerType = "mouse")))
6565
yield()
66-
canvas.dispatchEvent(MouseEvent("mousedown", MouseEventInit(clientX = 8, clientY = 20, buttons = 1, button = 1)))
66+
canvas.dispatchEvent(PointerEvent("pointerdown", PointerEventInit(clientX = 8, clientY = 20, buttons = 1, button = 1, pointerType = "mouse")))
6767
yield()
68-
canvas.dispatchEvent(MouseEvent("mouseup", MouseEventInit(clientX = 8, clientY = 20, buttons = 0, button = 1)))
68+
canvas.dispatchEvent(PointerEvent("pointerup", PointerEventInit(clientX = 8, clientY = 20, buttons = 0, button = 1, pointerType = "mouse")))
6969

7070
awaitIdle()
7171
assertEquals(TextRange(0, 0), textRange.value)
@@ -92,10 +92,10 @@ class MouseTextInputTests: OnCanvasTests {
9292
val startX = textAreaRect.left.toInt() + 1
9393
val startY = textAreaRect.top.toInt() + 8
9494
val endX = startX + textFieldWidth
95-
canvas.dispatchEvent(MouseEvent("mousemove", MouseEventInit(clientX = startX, clientY = startY, buttons = 1, button = 1)))
96-
canvas.dispatchEvent(MouseEvent("mousedown", MouseEventInit(clientX = startX, clientY = startY, buttons = 1, button = 1)))
97-
canvas.dispatchEvent(MouseEvent("mousemove", MouseEventInit(clientX = endX, clientY = startY, buttons = 1, button = 1)))
98-
canvas.dispatchEvent(MouseEvent("mouseup", MouseEventInit(clientX = endX, clientY = startY, buttons = 0, button = 1)))
95+
canvas.dispatchEvent(PointerEvent("pointermove", PointerEventInit(clientX = startX, clientY = startY, buttons = 1, button = 1, pointerType = "mouse")))
96+
canvas.dispatchEvent(PointerEvent("pointerdown", PointerEventInit(clientX = startX, clientY = startY, buttons = 1, button = 1, pointerType = "mouse")))
97+
canvas.dispatchEvent(PointerEvent("pointermove", PointerEventInit(clientX = endX, clientY = startY, buttons = 1, button = 1, pointerType = "mouse")))
98+
canvas.dispatchEvent(PointerEvent("pointerup", PointerEventInit(clientX = endX, clientY = startY, buttons = 0, button = 1, pointerType = "mouse")))
9999

100100
awaitIdle()
101101
assertEquals(TextRange(0, 14), textRange.value)

0 commit comments

Comments
 (0)