15
15
*/
16
16
package androidx.compose.ui.awt
17
17
18
+ import androidx.compose.foundation.ScrollState
18
19
import androidx.compose.foundation.layout.Box
20
+ import androidx.compose.foundation.layout.Column
19
21
import androidx.compose.foundation.layout.fillMaxSize
22
+ import androidx.compose.foundation.layout.fillMaxWidth
23
+ import androidx.compose.foundation.layout.height
20
24
import androidx.compose.foundation.layout.requiredSize
21
25
import androidx.compose.foundation.layout.size
22
26
import androidx.compose.foundation.layout.sizeIn
23
27
import androidx.compose.foundation.lazy.LazyColumn
28
+ import androidx.compose.foundation.verticalScroll
24
29
import androidx.compose.material.Text
25
30
import androidx.compose.runtime.Composable
26
31
import androidx.compose.runtime.LaunchedEffect
@@ -31,12 +36,15 @@ import androidx.compose.runtime.saveable.rememberSaveable
31
36
import androidx.compose.runtime.setValue
32
37
import androidx.compose.ui.ExperimentalComposeUiApi
33
38
import androidx.compose.ui.Modifier
39
+ import androidx.compose.ui.background
34
40
import androidx.compose.ui.geometry.Size
41
+ import androidx.compose.ui.graphics.Color
35
42
import androidx.compose.ui.input.pointer.PointerEventType
36
43
import androidx.compose.ui.input.pointer.onPointerEvent
37
44
import androidx.compose.ui.layout.layout
38
45
import androidx.compose.ui.layout.onGloballyPositioned
39
46
import androidx.compose.ui.sendMouseEvent
47
+ import androidx.compose.ui.sendMouseWheelEvent
40
48
import androidx.compose.ui.unit.Constraints
41
49
import androidx.compose.ui.unit.dp
42
50
import androidx.compose.ui.unit.toSize
@@ -49,8 +57,11 @@ import java.awt.BorderLayout
49
57
import java.awt.Dimension
50
58
import java.awt.GraphicsEnvironment
51
59
import java.awt.event.MouseEvent
60
+ import javax.swing.BoxLayout
52
61
import javax.swing.JFrame
53
62
import javax.swing.JPanel
63
+ import javax.swing.JScrollPane
64
+ import javax.swing.ScrollPaneConstants
54
65
import junit.framework.TestCase.assertTrue
55
66
import kotlin.test.assertEquals
56
67
import kotlin.test.assertFalse
@@ -545,4 +556,120 @@ class ComposePanelTest {
545
556
window.dispose()
546
557
}
547
558
}
559
+
560
+ @Test
561
+ fun `ComposePanel propagates unconsumed mouse wheel scroll events to parent` () {
562
+ System .setProperty(" compose.swing.redispatchMouseWheelEvents" , " true" )
563
+ runApplicationTest {
564
+ val composePanel = ComposePanel ()
565
+ composePanel.preferredSize = Dimension (200 , 200 )
566
+ val scrollState = ScrollState (0 )
567
+ composePanel.setContent {
568
+ Box (Modifier .size(200 .dp).verticalScroll(scrollState).background(Color .Yellow )) {
569
+ Column (Modifier .fillMaxWidth().height(400 .dp)) {
570
+ Text (" Hello World" )
571
+ Text (" Hello World" )
572
+ Text (" Hello World" )
573
+ Text (" Hello World" )
574
+ Text (" Hello World" )
575
+ }
576
+ }
577
+ }
578
+
579
+ val window = JFrame ()
580
+ try {
581
+ window.size = Dimension (200 , 200 )
582
+ val scrollPane = JScrollPane (
583
+ JPanel ().apply {
584
+ layout = BoxLayout (this , BoxLayout .Y_AXIS )
585
+ add(composePanel)
586
+ add(javax.swing.Box .createVerticalStrut(1000 ), BorderLayout .CENTER )
587
+ }
588
+ )
589
+ scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants .HORIZONTAL_SCROLLBAR_NEVER
590
+ window.contentPane.add(scrollPane, BorderLayout .CENTER )
591
+ window.isVisible = true
592
+
593
+ awaitIdle()
594
+
595
+ // Scroll a little and check that compose content was scrolled
596
+ composePanel.sendMouseWheelEvent(wheelRotation = 1.0 )
597
+ awaitIdle()
598
+ assertThat(scrollState.value).isGreaterThan(0 )
599
+
600
+ // Scroll a lot and check that the Swing JScrollPane was scrolled
601
+ // Note that we need two scroll events for now because Compose can't partially consume
602
+ // scroll events. So one event is needed to scroll Compose content to the end, and
603
+ // another one to scroll JScrollPane.
604
+ window.sendMouseWheelEvent(wheelRotation = 1000.0 )
605
+ awaitIdle()
606
+ window.sendMouseWheelEvent(wheelRotation = 1000.0 )
607
+ assertThat(scrollPane.viewport.viewPosition.y).isGreaterThan(0 )
608
+ } finally {
609
+ window.dispose()
610
+ }
611
+ }
612
+ }
613
+
614
+ @Test
615
+ fun `ComposePanel propagates unconsumed mouse wheel scroll events to sibling` () {
616
+ System .setProperty(" compose.swing.redispatchMouseWheelEvents" , " true" )
617
+ runApplicationTest {
618
+ val composePanel = ComposePanel ()
619
+ val scrollState = ScrollState (0 )
620
+ composePanel.setContent {
621
+ Box (Modifier .size(200 .dp).verticalScroll(scrollState).background(Color .Green )) {
622
+ Column (Modifier .fillMaxWidth().height(400 .dp)) {
623
+ Text (" Hello World" )
624
+ Text (" Hello World" )
625
+ Text (" Hello World" )
626
+ Text (" Hello World" )
627
+ Text (" Hello World" )
628
+ }
629
+ }
630
+ }
631
+
632
+ val container = JPanel (null )
633
+ container.size = Dimension (200 , 200 )
634
+
635
+ val scrollPane = JScrollPane (
636
+ JPanel ().apply {
637
+ layout = BoxLayout (this , BoxLayout .Y_AXIS )
638
+ add(javax.swing.Box .createVerticalStrut(1000 ), BorderLayout .CENTER )
639
+ }
640
+ )
641
+ scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants .HORIZONTAL_SCROLLBAR_NEVER
642
+
643
+ composePanel.size = Dimension (200 , 200 )
644
+ scrollPane.size = Dimension (200 , 400 )
645
+
646
+ val window = JFrame ()
647
+ try {
648
+ window.size = Dimension (200 , 400 )
649
+ container.add(composePanel)
650
+ container.add(scrollPane)
651
+
652
+ window.contentPane.add(container, BorderLayout .CENTER )
653
+ window.isVisible = true
654
+
655
+ awaitIdle()
656
+
657
+ // Scroll a little and check that compose content was scrolled
658
+ composePanel.sendMouseWheelEvent(wheelRotation = 1.0 )
659
+ awaitIdle()
660
+ assertThat(scrollState.value).isGreaterThan(0 )
661
+
662
+ // Scroll a lot and check that the Swing JScrollPane was scrolled
663
+ // Note that we need two scroll events for now because Compose can't partially consume
664
+ // scroll events. So one event is needed to scroll Compose content to the end, and
665
+ // another one to scroll JScrollPane.
666
+ composePanel.sendMouseWheelEvent(wheelRotation = 1000.0 )
667
+ awaitIdle()
668
+ window.sendMouseWheelEvent(wheelRotation = 1000.0 )
669
+ assertThat(scrollPane.viewport.viewPosition.y).isGreaterThan(0 )
670
+ } finally {
671
+ window.dispose()
672
+ }
673
+ }
674
+ }
548
675
}
0 commit comments