Closed kirill-grouchnikov closed 1 year ago
Maybe related to https://github.com/JetBrains/compose-jb/issues/1794 that was fixed some time ago
For Composable Window we use this code to draw the first frame in the background:
swingFrame.preferredSize = swingFrame.size
swingFrame.pack()
swingFrame.contentPane.paint(swingFrame.graphics)
Full code in your case:
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposePanel
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import java.awt.BorderLayout
import java.awt.Dimension
import javax.swing.JFrame
fun main() = application {
Window(
onCloseRequest = ::exitApplication,
state = rememberWindowState(
width = 400.dp,
height = 200.dp,
position = WindowPosition.Aligned(Alignment.Center)
),
title = "ComposePanel Demo",
resizable = false
) {
Box(
modifier = Modifier.fillMaxSize(1.0f).background(Color.Yellow)
.clickable {
val swingFrame = JFrame()
swingFrame.isUndecorated = true;
swingFrame.size = Dimension(500, 200)
swingFrame.setLocation(0, 0)
swingFrame.contentPane.layout = BorderLayout()
swingFrame.contentPane.background = java.awt.Color.BLACK
val composePanel = ComposePanel()
composePanel.setContent {
Box(Modifier.fillMaxSize().background(Color.Blue))
}
swingFrame.contentPane.add(composePanel)
swingFrame.preferredSize = swingFrame.size
swingFrame.pack()
swingFrame.contentPane.paint(swingFrame.graphics)
swingFrame.isVisible = true
})
}
}
Will it work for you?
Thanks, will check this snippet tomorrow
Doesn't look like this is going to work for the scenario where I'm using ComposePanel
in Aurora.
ComposePanel
JPopupMenu
)Due to how Swing's JPopupMenu.show
works, I don't see how I can access the underlying popup window to set its size, pack it and then explicitly paint its content before making it visible.
The reasons I can't use JWindow
directly, and have to go through JPopupMenu
for displaying popup content are over at https://github.com/kirill-grouchnikov/aurora/issues/22 - due to internal APIs to grab / ungrab windows during user interaction that are not exposed as Swing / AWT APIs.
I didn't figure out how to fix it for JPopupMenu
to the 1.2.0. Will try to figure to the 1.2.1 release
P.S.
This issue also affects the case when we embed ComposePanel
into IDEA.
I noticed, that even without ComposePanel
it sometimes flashes with the gray color. And we can get rid of these flashes changing swingFrame.background
instead of swingFrame.contentPane.background
. But it doesn't work for JPopupMenu
:
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import java.awt.Dimension
import java.awt.Graphics
import javax.swing.JButton
import javax.swing.JPopupMenu
fun main() = application {
Window(
onCloseRequest = ::exitApplication,
state = rememberWindowState(
width = 400.dp,
height = 200.dp,
position = WindowPosition.Aligned(Alignment.Center)
),
title = "ComposePanel Demo",
resizable = false
) {
Box(
modifier = Modifier.fillMaxSize(1.0f).background(Color.Yellow)
.clickable {
val swingFrame = JPopupMenu()
swingFrame.size = Dimension(500, 200)
swingFrame.background = java.awt.Color.RED
swingFrame.setLocation(0, 0)
// val composePanel = ComposePanel()
// composePanel.size = Dimension(200, 200)
// composePanel.preferredSize = Dimension(200, 200)
// composePanel.setContent {
// Box(Modifier.fillMaxSize().background(Color.Green))
// Canvas(Modifier.fillMaxSize()) {
// Thread.sleep(500)
// }
// }
// composePanel.background = java.awt.Color.RED
swingFrame.add(object : JButton() {
override fun paint(g: Graphics?) {
Thread.sleep(1000)
super.paint(g)
}
})
swingFrame.isVisible = true
})
}
}
There is another issue besides that - composePanel.background
isn't applied to ComposePanel
. But we should never see this color if we set content for ComposePanel
, because the content of ComposePanel
is painted synchronously (I verified this adding sleep into setContent and in the draw method). The only color that can be useful is a transparent color (there is an issue about ComposePanel
transparency).
I am not sure, but is the main feature of JPopupMenu is an internal window pool? In this case workaround could be using JFrame with your own pool.
Another workaround can be finding similar to "Panel.background" property, that will affect JPopupMenu
background.
In any case, it seems it is related to Swing, not to Compose, if I investigated correctly.
Can't use JFrame
or JWindow
as those don't have built-in access to the internal APIs to track window grab and ungrab events that are needed to dismiss open popup menus when the user interacts with the windows. The two scenarios in Aurora and Radiance are:
There's probably more, and they are all down to the same private APIs to grab / ungrab top-level windows that are only wired in JPopupMenu
Can you check, if the issue is with flashes remains, if you replace ComposePanel
by this?
object : JButton() {
override fun paint(g: Graphics?) {
Thread.sleep(1000)
super.paint(g)
}
}
grab / ungrab
Can we close popups, when they just lose focus?
That's going to break cascading popups
That's going to break cascading popups
Maybe we can close all popups, when the parent window receives focus, not when an individual popup loses focus.
But if we definitely need to react on window grab, can we just call this?
Toolkit.getDefaultToolkit().addAWTEventListener(..., 0x80000000); // sun.awt.SunToolkit.GRAB_EVENT_MASK
and call grab/ungrab via reflection? I am not sure that we can access Sun API via reflection though. And It usually isn't safe, but the Swing API in a stable state for many years.
The alternative will be to use native API's (Compose exposes windowHandle
, so it is possible)
Closing this issue, as from the current point of view it isn't obvious what can't be done on Compose side. This can help to further understand, if it is truly unrelated to Compose.
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
Record the screen and then replay it frame by frame. When the
JFrame
is shown with the internalComposePanel
, it flashes the default light grey for just a frame or two before switching to the intended red.Also, there should be a way to set the initial color of a
ComposePanel
without having to resort to theUIManager.put("Panel.background")
call