You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a cherry-pick (with some adaptations) of
#2425
Redispatch mouse wheel events which the Compose scene did not consume.
### Difference with Swing behavior
This fix introduces a slight difference with how Swing handles nested
mouse-wheel scrolling. In Swing, there is essentially no nested
scrolling. In a scenario with a `JScrollPane` inside another
`JScrollPane`, only the inner one can be scrolled when the mouse is over
it. With this PR, a `ComposePanel` with a scrollable element within a
`JScrollPane` will scroll until it reaches the start/end and then the
outer `JScrollPane` will scroll.
This behavior seems to be an improvement, so we thought it was ok to
have this difference.
### Danger, Will Robinson
Note that this includes a potentially dangerous hack of whose
consequences I'm not yet sure. If it proves too problematic, we will
need to take a different approach (documented in the source).
The hack is that in order to allow AWT to find the correct target for
the redispatched event, we temporarily unregister
`ComposeSceneMediator`'s mouse listeners.
### Feature flag
I've put the behavior behind
`ComposeFeatureFlags.redispatchUnconsumedMouseWheelEvents`. The flag
will be:
- `false` by default in 1.9.1
- `true` by default in 1.10
- Removed in 1.11, if no major problems are discovered.
In this PR, it's `true`, as it merges into `jb-main`. In the cherry-pick
to 1.9.1, we should change it to `false`. When it's approved I will add
a ticket to remove it for 1.11
### Other mouse events
Note that this PR only deals with mouse wheel events, but at least in
the case of a lightweight skia layer (i.e. `SkiaSwingLayer`), we should
probably do the same for other mouse events.
Fixes https://youtrack.jetbrains.com/issue/CMP-4601
## Testing
Added unit tests, and also tested manually with
```
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.*
import androidx.compose.ui.awt.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.unit.*
import java.awt.*
import javax.swing.*
fun main() = SwingUtilities.invokeLater {
val frame = JFrame("CfD repro")
frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
frame.minimumSize = Dimension(500, 400)
System.setProperty("compose.swing.render.on.graphics", "true")
val mainPanel =
JPanel(null).apply {
val container =
JPanel(null).apply {
layout = BoxLayout(this, BoxLayout.Y_AXIS)
addChildren()
}
val scrollPane = JScrollPane(container)
scrollPane.setBounds(0, 0, 500, 500)
add(scrollPane)
}
frame.contentPane.add(mainPanel, BorderLayout.CENTER)
frame.isVisible = true
}
private fun JPanel.addChildren() {
repeat(10) {
repeat(5) {
add(JLabel("<html>" + "Very long text here ".repeat(10) + "</html>"))
add(Box.createVerticalStrut(10))
}
add(
ComposePanel().apply {
setContent {
ComposeBox()
}
}
)
add(Box.createVerticalStrut(10))
}
}
@composable
fun ComposeBox() {
Box(
modifier = Modifier
.height(100.dp)
.background(color = Color(180, 180, 180))
.padding(10.dp)
) {
val stateVertical = rememberScrollState(0)
Box(
modifier = Modifier
.verticalScroll(stateVertical)
.padding(end = 12.dp, bottom = 12.dp)
) {
Column {
for (item in 0..10) {
TextBox("Item #$item")
}
}
}
}
}
@composable
fun TextBox(text: String = "Item") {
Box(
modifier = Modifier.height(32.dp)
.width(400.dp)
.background(color = Color(0, 0, 0, 20))
.padding(start = 10.dp),
contentAlignment = Alignment.CenterStart
) {
Text(text = text)
}
}
```
This should be tested by QA
## Release Notes
### Fixes - Desktop
- ComposePanel can now re-dispatch unconsumed mouse wheel events,
allowing scrollable components beneath to be scrolled. To enable this
behavior, set the system property
`"compose.swing.redispatchMouseWheelEvents"` to `"true"`.
0 commit comments