Skip to content

Commit dc1dac0

Browse files
committed
Add a flag to control the redispatching behavior
1 parent 9b636d9 commit dc1dac0

File tree

3 files changed

+115
-97
lines changed

3 files changed

+115
-97
lines changed

compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ComposeFeatureFlags.desktop.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ internal object ComposeFeatureFlags {
8888
val useInteropBlending = FeatureFlag {
8989
System.getProperty("compose.interop.blending").toBoolean()
9090
}
91+
92+
/**
93+
* Whether to propagate unconsumed mouse wheel events to the parent Swing component.
94+
*
95+
* This flag will be removed in the future, and the default behavior will correspond to a value
96+
* of `true`.
97+
*/
98+
val propagateUnconsumedMouseWheelEvents = FeatureFlag {
99+
System.getProperty("compose.swing.propagateMouseWheelEvents").toBoolean()
100+
}
91101
}
92102

93103

compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.desktop.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,9 @@ internal class ComposeSceneMediator(
472472
processMouseEvent {
473473
val processingResult = scene.onMouseWheelEvent(event.position, event)
474474
if (!processingResult.anyChangeConsumed) {
475-
redispatchUnconsumedMouseEvent(event)
475+
if (ComposeFeatureFlags.propagateUnconsumedMouseWheelEvents.value) {
476+
redispatchUnconsumedMouseEvent(event)
477+
}
476478
}
477479
}
478480
}

compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposePanelTest.kt

Lines changed: 102 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -723,112 +723,118 @@ class ComposePanelTest {
723723
}
724724

725725
@Test
726-
fun `ComposePanel propagates unconsumed mouse wheel scroll events to parent`() = runApplicationTest {
727-
val composePanel = ComposePanel()
728-
composePanel.preferredSize = Dimension(200, 200)
729-
val scrollState = ScrollState(0)
730-
composePanel.setContent {
731-
Box(Modifier.size(200.dp).verticalScroll(scrollState).background(Color.Yellow)) {
732-
Column(Modifier.fillMaxWidth().height(400.dp)) {
733-
Text("Hello World")
734-
Text("Hello World")
735-
Text("Hello World")
736-
Text("Hello World")
737-
Text("Hello World")
726+
fun `ComposePanel propagates unconsumed mouse wheel scroll events to parent`() =
727+
ComposeFeatureFlags.propagateUnconsumedMouseWheelEvents.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+
}
738742
}
739-
}
740-
}
741743

742-
val window = JFrame()
743-
try {
744-
window.size = Dimension(200, 200)
745-
val scrollPane = JScrollPane(
746-
JPanel().apply {
747-
layout = BoxLayout(this, BoxLayout.Y_AXIS)
748-
add(composePanel)
749-
add(javax.swing.Box.createVerticalStrut(1000), BorderLayout.CENTER)
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()
750775
}
751-
)
752-
scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
753-
window.contentPane.add(scrollPane, BorderLayout.CENTER)
754-
window.isVisible = true
755-
756-
awaitIdle()
757-
758-
// Scroll a little and check that compose content was scrolled
759-
composePanel.sendMouseWheelEvent(wheelRotation = 1.0)
760-
awaitIdle()
761-
assertThat(scrollState.value).isGreaterThan(0)
762-
763-
// Scroll a lot and check that the Swing JScrollPane was scrolled
764-
// Note that we need two scroll events for now because Compose can't partially consume
765-
// scroll events. So one event is needed to scroll Compose content to the end, and
766-
// another one to scroll JScrollPane.
767-
window.sendMouseWheelEvent(wheelRotation = 1000.0)
768-
awaitIdle()
769-
window.sendMouseWheelEvent(wheelRotation = 1000.0)
770-
assertThat(scrollPane.viewport.viewPosition.y).isGreaterThan(0)
771-
} finally {
772-
window.dispose()
776+
}
773777
}
774-
}
775778

776779
@Test
777-
fun `ComposePanel propagates unconsumed mouse wheel scroll events to sibling`() = runApplicationTest {
778-
val composePanel = ComposePanel()
779-
val scrollState = ScrollState(0)
780-
composePanel.setContent {
781-
Box(Modifier.size(200.dp).verticalScroll(scrollState).background(Color.Green)) {
782-
Column(Modifier.fillMaxWidth().height(400.dp)) {
783-
Text("Hello World")
784-
Text("Hello World")
785-
Text("Hello World")
786-
Text("Hello World")
787-
Text("Hello World")
780+
fun `ComposePanel propagates unconsumed mouse wheel scroll events to sibling`() =
781+
ComposeFeatureFlags.propagateUnconsumedMouseWheelEvents.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+
}
788795
}
789-
}
790-
}
791-
792-
val container = JPanel(null)
793-
container.size = Dimension(200, 200)
794-
795-
val scrollPane = JScrollPane(
796-
JPanel().apply {
797-
layout = BoxLayout(this, BoxLayout.Y_AXIS)
798-
add(javax.swing.Box.createVerticalStrut(1000), BorderLayout.CENTER)
799-
}
800-
)
801-
scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
802-
803-
composePanel.size = Dimension(200, 200)
804-
scrollPane.size = Dimension(200, 400)
805796

806-
val window = JFrame()
807-
try {
808-
window.size = Dimension(200, 400)
809-
container.add(composePanel)
810-
container.add(scrollPane)
797+
val container = JPanel(null)
798+
container.size = Dimension(200, 200)
811799

812-
window.contentPane.add(container, BorderLayout.CENTER)
813-
window.isVisible = true
814-
815-
awaitIdle()
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
816807

817-
// Scroll a little and check that compose content was scrolled
818-
composePanel.sendMouseWheelEvent(wheelRotation = 1.0)
819-
awaitIdle()
820-
assertThat(scrollState.value).isGreaterThan(0)
808+
composePanel.size = Dimension(200, 200)
809+
scrollPane.size = Dimension(200, 400)
821810

822-
// Scroll a lot and check that the Swing JScrollPane was scrolled
823-
// Note that we need two scroll events for now because Compose can't partially consume
824-
// scroll events. So one event is needed to scroll Compose content to the end, and
825-
// another one to scroll JScrollPane.
826-
composePanel.sendMouseWheelEvent(wheelRotation = 1000.0)
827-
awaitIdle()
828-
window.sendMouseWheelEvent(wheelRotation = 1000.0)
829-
assertThat(scrollPane.viewport.viewPosition.y).isGreaterThan(0)
830-
} finally {
831-
window.dispose()
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+
}
832839
}
833-
}
834840
}

0 commit comments

Comments
 (0)