1717package androidx.compose.ui
1818
1919import androidx.compose.ui.geometry.Offset
20+ import androidx.compose.ui.input.pointer.PointerEventType
2021import androidx.compose.ui.input.pointer.PointerEventType.Companion.Enter
2122import androidx.compose.ui.input.pointer.PointerEventType.Companion.Exit
2223import androidx.compose.ui.input.pointer.PointerEventType.Companion.Move
@@ -388,8 +389,8 @@ class SyntheticEventSenderTest {
388389 @Test
389390 fun `should update pointer position with move event after hover event` () {
390391 val received = mutableListOf<PointerInputEvent >()
391- val sender = SyntheticEventSender {
392- PointerEventResult ( received.add(it) )
392+ val sender = SyntheticEventSenderConsumingAllMovements {
393+ received.add(it)
393394 }
394395 sender.send(mouseEvent(Enter , 10f , 20f , pressed = false ))
395396
@@ -416,8 +417,8 @@ class SyntheticEventSenderTest {
416417 @Test
417418 fun `should update pointer position with move event after pressed event` () {
418419 val received = mutableListOf<PointerInputEvent >()
419- val sender = SyntheticEventSender {
420- PointerEventResult ( received.add(it) )
420+ val sender = SyntheticEventSenderConsumingAllMovements {
421+ received.add(it)
421422 }
422423 sender.send(mouseEvent(Press , 10f , 20f , pressed = true ))
423424
@@ -444,8 +445,8 @@ class SyntheticEventSenderTest {
444445 @Test
445446 fun `should not update pointer position with move event after touch event` () {
446447 val received = mutableListOf<PointerInputEvent >()
447- val sender = SyntheticEventSender {
448- PointerEventResult ( received.add(it) )
448+ val sender = SyntheticEventSenderConsumingAllMovements {
449+ received.add(it)
449450 }
450451 sender.send(event(Press , 1 to touch(10f , 20f , pressed = true )))
451452
@@ -470,8 +471,8 @@ class SyntheticEventSenderTest {
470471 @Test
471472 fun `should not re-enter after mouse exit` () {
472473 val received = mutableListOf<PointerInputEvent >()
473- val sender = SyntheticEventSender {
474- PointerEventResult ( received.add(it) )
474+ val sender = SyntheticEventSenderConsumingAllMovements {
475+ received.add(it)
475476 }
476477 sender.send(mouseEvent(Move , 10f , 20f , pressed = false ))
477478 sender.send(mouseEvent(Exit , 10f , 20f , pressed = false ))
@@ -488,8 +489,8 @@ class SyntheticEventSenderTest {
488489 @Test
489490 fun `synthetic events should not duplicate scrollDelta` () {
490491 val received = mutableListOf<PointerInputEvent >()
491- val sender = SyntheticEventSender {
492- PointerEventResult ( received.add(it) )
492+ val sender = SyntheticEventSenderConsumingAllMovements {
493+ received.add(it)
493494 }
494495
495496 sender.send(
@@ -513,8 +514,8 @@ class SyntheticEventSenderTest {
513514 @Test
514515 fun `synthetic events should not duplicate panGestureOffset` () {
515516 val received = mutableListOf<PointerInputEvent >()
516- val sender = SyntheticEventSender {
517- PointerEventResult ( received.add(it) )
517+ val sender = SyntheticEventSenderConsumingAllMovements {
518+ received.add(it)
518519 }
519520
520521 sender.send(
@@ -538,8 +539,8 @@ class SyntheticEventSenderTest {
538539 @Test
539540 fun `synthetic events should not duplicate scaleGestureFactor` () {
540541 val received = mutableListOf<PointerInputEvent >()
541- val sender = SyntheticEventSender {
542- PointerEventResult ( received.add(it) )
542+ val sender = SyntheticEventSenderConsumingAllMovements {
543+ received.add(it)
543544 }
544545
545546 sender.send(
@@ -715,8 +716,8 @@ class SyntheticEventSenderTest {
715716 @Test
716717 fun `scale synthetic events should not duplicate scaleGestureFactor` () {
717718 val received = mutableListOf<PointerInputEvent >()
718- val sender = SyntheticEventSender {
719- PointerEventResult ( received.add(it) )
719+ val sender = SyntheticEventSenderConsumingAllMovements {
720+ received.add(it)
720721 }
721722
722723 sender.send(
@@ -733,8 +734,8 @@ class SyntheticEventSenderTest {
733734 @Test
734735 fun `pan synthetic events should not duplicate panGestureOffset` () {
735736 val received = mutableListOf<PointerInputEvent >()
736- val sender = SyntheticEventSender {
737- PointerEventResult ( received.add(it) )
737+ val sender = SyntheticEventSenderConsumingAllMovements {
738+ received.add(it)
738739 }
739740
740741 sender.send(
@@ -748,6 +749,75 @@ class SyntheticEventSenderTest {
748749 assertEquals(Offset (5f , 0f ), totalOffset)
749750 }
750751
752+ // https://youtrack.jetbrains.com/issue/CMP-9964
753+ @Test
754+ fun `mouse move unpressing buttons sends release event` () {
755+ val received = mutableListOf<PointerInputEvent >()
756+ val sender = SyntheticEventSenderConsumingAllMovements {
757+ received.add(it)
758+ }
759+
760+ sender.send(mouseEvent(Press , 10f , 10f , pressed = true , nativeEvent = 1 ))
761+ sender.send(mouseEvent(Move , 10f , 10f , pressed = true , nativeEvent = 2 ))
762+ assertEquals(0 , received.count { it.eventType == Release })
763+
764+ sender.send(mouseEvent(Move , 10f , 10f , pressed = false , nativeEvent = 3 ))
765+ assertEquals(1 , received.count { it.eventType == Release }, " Release event not sent" )
766+
767+ // Also, make sure we don't send an extra release event afterward
768+ sender.send(mouseEvent(Release , 10f , 10f , pressed = false , nativeEvent = 4 ))
769+ assertEquals(1 , received.count { it.eventType == Release }, " Extra release event sent" )
770+
771+ // But it should be sent as an `Unknown` event
772+ assertEquals(4 , received.count { it.nativeEvent != null }, " Missing native event" )
773+ assertEquals(PointerEventType .Unknown , received.last { it.nativeEvent != null }.eventType)
774+ }
775+
776+ @Test
777+ fun `extra mouse press events are not sent` () {
778+ val received = mutableListOf<PointerInputEvent >()
779+ val sender = SyntheticEventSenderConsumingAllMovements {
780+ received.add(it)
781+ }
782+
783+ sender.send(mouseEvent(Press , 10f , 10f , pressed = true , nativeEvent = 1 ))
784+ assertEquals(1 , received.count { it.eventType == Press }, " Press event not sent!?" )
785+ sender.send(mouseEvent(Press , 20f , 20f , pressed = true , nativeEvent = 2 ))
786+ assertEquals(1 , received.count { it.eventType == Press }, " Extra press event sent" )
787+
788+ // But it should be sent as an `Unknown` event
789+ assertEquals(2 , received.count { it.nativeEvent != null }, " Missing native event" )
790+ assertEquals(PointerEventType .Unknown , received.last { it.nativeEvent != null }.eventType)
791+ }
792+
793+ @Test
794+ fun `extra mouse release events are not sent` () {
795+ val received = mutableListOf<PointerInputEvent >()
796+ val sender = SyntheticEventSenderConsumingAllMovements {
797+ received.add(it)
798+ }
799+
800+ sender.send(mouseEvent(Press , 10f , 10f , pressed = true , nativeEvent = 1 ))
801+ assertEquals(1 , received.count { it.eventType == Press }, " Press event not sent!?" )
802+ sender.send(mouseEvent(Release , 10f , 10f , pressed = false , nativeEvent = 2 ))
803+ assertEquals(1 , received.count { it.eventType == Release }, " Release event not sent!?" )
804+ sender.send(mouseEvent(Release , 20f , 20f , pressed = false , nativeEvent = 3 ))
805+ assertEquals(1 , received.count { it.eventType == Release }, " Extra release event sent" )
806+
807+ // But it should be sent as an `Unknown` event
808+ assertEquals(3 , received.count { it.nativeEvent != null }, " Missing native event" )
809+ assertEquals(PointerEventType .Unknown , received.last { it.nativeEvent != null }.eventType)
810+ }
811+
812+ private fun SyntheticEventSenderConsumingAllMovements (send : (PointerInputEvent ) -> Unit ) =
813+ SyntheticEventSender {
814+ send(it)
815+ PointerEventResult (
816+ dispatchedToAPointerInputModifier = true ,
817+ anyMovementConsumed = true
818+ )
819+ }
820+
751821 private fun eventsSentBy (
752822 vararg inputEvents : PointerInputEvent
753823 ): List <PointerInputEvent > {
0 commit comments