15
15
*/
16
16
package androidx.compose.ui.awt
17
17
18
+ import androidx.compose.foundation.ScrollState
18
19
import androidx.compose.foundation.focusable
19
20
import androidx.compose.foundation.layout.Box
21
+ import androidx.compose.foundation.layout.Column
20
22
import androidx.compose.foundation.layout.fillMaxSize
23
+ import androidx.compose.foundation.layout.fillMaxWidth
24
+ import androidx.compose.foundation.layout.height
21
25
import androidx.compose.foundation.layout.requiredSize
22
26
import androidx.compose.foundation.layout.size
23
27
import androidx.compose.foundation.layout.sizeIn
24
28
import androidx.compose.foundation.lazy.LazyColumn
25
29
import androidx.compose.foundation.text.input.rememberTextFieldState
30
+ import androidx.compose.foundation.verticalScroll
26
31
import androidx.compose.material.Text
27
32
import androidx.compose.material3.TextField
28
33
import androidx.compose.runtime.Composable
@@ -51,6 +56,7 @@ import androidx.compose.ui.layout.onGloballyPositioned
51
56
import androidx.compose.ui.sendCharTypedEvents
52
57
import androidx.compose.ui.sendKeyEvent
53
58
import androidx.compose.ui.sendMouseEvent
59
+ import androidx.compose.ui.sendMouseWheelEvent
54
60
import androidx.compose.ui.unit.Constraints
55
61
import androidx.compose.ui.unit.dp
56
62
import androidx.compose.ui.unit.toSize
@@ -65,8 +71,11 @@ import java.awt.BorderLayout
65
71
import java.awt.Dimension
66
72
import java.awt.GraphicsEnvironment
67
73
import java.awt.event.MouseEvent
74
+ import javax.swing.BoxLayout
68
75
import javax.swing.JFrame
69
76
import javax.swing.JPanel
77
+ import javax.swing.JScrollPane
78
+ import javax.swing.ScrollPaneConstants
70
79
import junit.framework.TestCase.assertTrue
71
80
import kotlin.test.assertEquals
72
81
import kotlin.test.assertFalse
@@ -713,4 +722,119 @@ class ComposePanelTest {
713
722
}
714
723
}
715
724
725
+ @Test
726
+ fun `ComposePanel propagates unconsumed mouse wheel scroll events to parent` () =
727
+ ComposeFeatureFlags .redispatchUnconsumedMouseWheelEvents.withOverride(true ) {
728
+ runApplicationTest {
729
+ val composePanel = ComposePanel ()
730
+ composePanel.preferredSize = Dimension (200 , 200 )
731
+ val scrollState = ScrollState (0 )
732
+ composePanel.setContent {
733
+ Box (Modifier .size(200 .dp).verticalScroll(scrollState).background(Color .Yellow )) {
734
+ Column (Modifier .fillMaxWidth().height(400 .dp)) {
735
+ Text (" Hello World" )
736
+ Text (" Hello World" )
737
+ Text (" Hello World" )
738
+ Text (" Hello World" )
739
+ Text (" Hello World" )
740
+ }
741
+ }
742
+ }
743
+
744
+ val window = JFrame ()
745
+ try {
746
+ window.size = Dimension (200 , 200 )
747
+ val scrollPane = JScrollPane (
748
+ JPanel ().apply {
749
+ layout = BoxLayout (this , BoxLayout .Y_AXIS )
750
+ add(composePanel)
751
+ add(javax.swing.Box .createVerticalStrut(1000 ), BorderLayout .CENTER )
752
+ }
753
+ )
754
+ scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants .HORIZONTAL_SCROLLBAR_NEVER
755
+ window.contentPane.add(scrollPane, BorderLayout .CENTER )
756
+ window.isVisible = true
757
+
758
+ awaitIdle()
759
+
760
+ // Scroll a little and check that compose content was scrolled
761
+ composePanel.sendMouseWheelEvent(wheelRotation = 1.0 )
762
+ awaitIdle()
763
+ assertThat(scrollState.value).isGreaterThan(0 )
764
+
765
+ // Scroll a lot and check that the Swing JScrollPane was scrolled
766
+ // Note that we need two scroll events for now because Compose can't partially consume
767
+ // scroll events. So one event is needed to scroll Compose content to the end, and
768
+ // another one to scroll JScrollPane.
769
+ window.sendMouseWheelEvent(wheelRotation = 1000.0 )
770
+ awaitIdle()
771
+ window.sendMouseWheelEvent(wheelRotation = 1000.0 )
772
+ assertThat(scrollPane.viewport.viewPosition.y).isGreaterThan(0 )
773
+ } finally {
774
+ window.dispose()
775
+ }
776
+ }
777
+ }
778
+
779
+ @Test
780
+ fun `ComposePanel propagates unconsumed mouse wheel scroll events to sibling` () =
781
+ ComposeFeatureFlags .redispatchUnconsumedMouseWheelEvents.withOverride(true ) {
782
+ runApplicationTest {
783
+ val composePanel = ComposePanel ()
784
+ val scrollState = ScrollState (0 )
785
+ composePanel.setContent {
786
+ Box (Modifier .size(200 .dp).verticalScroll(scrollState).background(Color .Green )) {
787
+ Column (Modifier .fillMaxWidth().height(400 .dp)) {
788
+ Text (" Hello World" )
789
+ Text (" Hello World" )
790
+ Text (" Hello World" )
791
+ Text (" Hello World" )
792
+ Text (" Hello World" )
793
+ }
794
+ }
795
+ }
796
+
797
+ val container = JPanel (null )
798
+ container.size = Dimension (200 , 200 )
799
+
800
+ val scrollPane = JScrollPane (
801
+ JPanel ().apply {
802
+ layout = BoxLayout (this , BoxLayout .Y_AXIS )
803
+ add(javax.swing.Box .createVerticalStrut(1000 ), BorderLayout .CENTER )
804
+ }
805
+ )
806
+ scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants .HORIZONTAL_SCROLLBAR_NEVER
807
+
808
+ composePanel.size = Dimension (200 , 200 )
809
+ scrollPane.size = Dimension (200 , 400 )
810
+
811
+ val window = JFrame ()
812
+ try {
813
+ window.size = Dimension (200 , 400 )
814
+ container.add(composePanel)
815
+ container.add(scrollPane)
816
+
817
+ window.contentPane.add(container, BorderLayout .CENTER )
818
+ window.isVisible = true
819
+
820
+ awaitIdle()
821
+
822
+ // Scroll a little and check that compose content was scrolled
823
+ composePanel.sendMouseWheelEvent(wheelRotation = 1.0 )
824
+ awaitIdle()
825
+ assertThat(scrollState.value).isGreaterThan(0 )
826
+
827
+ // Scroll a lot and check that the Swing JScrollPane was scrolled
828
+ // Note that we need two scroll events for now because Compose can't partially consume
829
+ // scroll events. So one event is needed to scroll Compose content to the end, and
830
+ // another one to scroll JScrollPane.
831
+ composePanel.sendMouseWheelEvent(wheelRotation = 1000.0 )
832
+ awaitIdle()
833
+ window.sendMouseWheelEvent(wheelRotation = 1000.0 )
834
+ assertThat(scrollPane.viewport.viewPosition.y).isGreaterThan(0 )
835
+ } finally {
836
+ window.dispose()
837
+ }
838
+ }
839
+ }
716
840
}
0 commit comments