Skip to content

Commit 78c001e

Browse files
authored
Fix ComposePanel background (#2670)
1 parent 7306b40 commit 78c001e

File tree

4 files changed

+58
-11
lines changed

4 files changed

+58
-11
lines changed

compose/ui/ui/api/desktop/ui.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ public final class androidx/compose/ui/awt/ComposePanel : javax/swing/JLayeredPa
504504
public fun requestFocus (Z)Z
505505
public fun requestFocusInWindow ()Z
506506
public fun requestFocusInWindow (Ljava/awt/event/FocusEvent$Cause;)Z
507+
public fun setBackground (Ljava/awt/Color;)V
507508
public fun setBounds (IIII)V
508509
public fun setComponentOrientation (Ljava/awt/ComponentOrientation;)V
509510
public final fun setContent (Lkotlin/jvm/functions/Function2;)V

compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposePanel.desktop.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ class ComposePanel @ExperimentalComposeUiApi constructor(
9090
" (use SwingUtilities.invokeLater).\n" +
9191
"Creating from another thread isn't supported."
9292
}
93-
background = Color.white
9493
layout = null
9594
focusTraversalPolicy = object : FocusTraversalPolicy() {
9695
override fun getComponentAfter(
@@ -212,6 +211,12 @@ class ComposePanel @ExperimentalComposeUiApi constructor(
212211
_composeContainer?.preferredSize ?: Dimension(0, 0)
213212
}
214213

214+
override fun setBackground(bg: Color?) {
215+
// Note that unless `setOpaque(true)` is called, JLayeredPane will not paint the background
216+
super.setBackground(bg)
217+
_composeContainer?.contentComponent?.background = bg
218+
}
219+
215220
/**
216221
* Sets Compose content of the ComposePanel.
217222
*
@@ -300,6 +305,7 @@ class ComposePanel @ExperimentalComposeUiApi constructor(
300305
setBounds(0, 0, width, height)
301306
contentComponent.isFocusable = isFocusable
302307
contentComponent.isRequestFocusEnabled = isRequestFocusEnabled
308+
contentComponent.background = background
303309
exceptionHandler = this@ComposePanel.exceptionHandler
304310

305311
_focusListeners.forEach(contentComponent::addFocusListener)

compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/skia/WindowSkiaLayerComponent.desktop.kt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -140,16 +140,15 @@ internal class WindowSkiaLayerComponent(
140140
get() = contentComponent.transparency
141141
set(value) {
142142
contentComponent.transparency = value
143-
if (value && !windowContext.isWindowTransparent && renderApi == GraphicsApi.METAL) {
144-
/*
145-
* SkiaLayer sets background inside transparency setter, that is required for
146-
* cases like software rendering.
147-
* In case of transparent Metal canvas on opaque window, background values with
148-
* alpha == 0 will make the result color black after clearing the canvas.
149-
*
150-
* Reset it to null to keep the color default.
151-
*/
152-
contentComponent.background = null
143+
contentComponent.background = when {
144+
!value -> null // This will use the parent's background
145+
// In case of transparent Metal canvas on an opaque window, background values with
146+
// alpha == 0 will make the result color black after clearing the canvas.
147+
// The case of `transparency = true` together with `!windowContext.isWindowTransparent`
148+
// is needed for "Experimental interop on a regular (non-transparent) window"
149+
// (per @MatkovIvan)
150+
!windowContext.isWindowTransparent && (renderApi == GraphicsApi.METAL) -> null
151+
else -> java.awt.Color(0, 0, 0, 0)
153152
}
154153
}
155154
override var fullscreen by contentComponent::fullscreen

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,4 +897,45 @@ class ComposePanelTest {
897897
window.dispose()
898898
}
899899
}
900+
901+
@Test
902+
fun `ComposePanel draws background correctly`() = runApplicationTest {
903+
// Show a canvas and a `ComposePanel` with the same background and compare the two colors.
904+
// Simply comparing to the set color doesn't work because Robot returns the color after
905+
// the OS transforms it to the screen color space (which doesn't seem to be accessible from
906+
// the JVM).
907+
908+
val bgColor = java.awt.Color.RED
909+
910+
val canvas = java.awt.Canvas().apply {
911+
size = Dimension(300, 300)
912+
background = bgColor
913+
}
914+
915+
val composePanel = ComposePanel().apply {
916+
size = Dimension(300, 300)
917+
background = bgColor
918+
}
919+
composePanel.setContent { }
920+
921+
922+
val frame = JFrame().apply {
923+
contentPane.layout = BoxLayout(contentPane, BoxLayout.Y_AXIS)
924+
contentPane.add(canvas)
925+
contentPane.add(composePanel)
926+
size = Dimension(300, 600)
927+
}
928+
929+
try {
930+
frame.isVisible = true
931+
awaitIdle()
932+
val robot = java.awt.Robot()
933+
val frameBounds = frame.bounds
934+
val canvasPixel = robot.getPixelColor(frameBounds.centerX.toInt(), (0.25 * frameBounds.centerY).toInt())
935+
val composePixel = robot.getPixelColor(frameBounds.centerX.toInt(), (0.75 * frameBounds.centerY).toInt())
936+
assertThat(composePixel).isEqualTo(canvasPixel)
937+
} finally {
938+
frame.dispose()
939+
}
940+
}
900941
}

0 commit comments

Comments
 (0)