From 23633cfd58cb4d79b3c632a03aaa32d8ce29c2be Mon Sep 17 00:00:00 2001 From: Igor Demin Date: Thu, 10 Mar 2022 13:56:22 +0400 Subject: [PATCH] Don't depend on kotlinx-coroutines-swing Because it overrides Dispatchers.Main Fixes https://github.com/JetBrains/compose-jb/issues/1943 RelNote: Compose don't depend on `kotlinx-coroutines-swing` now. So if you use `Dispatchers.Swing` or `Dispatchers.Main` in your code, add this dependency into `build.gradle.kts`: ``` dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:$coroutinesVersion") } ``` # Conflicts: # compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/ImageComposeSceneTest.kt # compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt # compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/TestComposeWindowTest.kt # compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/TestUtils.kt # compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowTest.kt --- compose/ui/ui/build.gradle | 1 - .../ui/ConfigureSwingGlobalsForCompose.desktop.kt | 2 +- .../androidx/compose/ui/awt/ComposeLayer.desktop.kt | 5 ++--- .../compose/ui/platform/GlobalSnapshotManager.desktop.kt | 7 ++++--- .../androidx/compose/ui/window/Application.desktop.kt | 4 ++-- .../androidx/compose/ui/window/AwtWindow.desktop.kt | 5 ++--- .../kotlin/androidx/compose/ui/ImageComposeSceneTest.kt | 5 ++--- .../kotlin/androidx/compose/ui/awt/ComposePanelTest.kt | 9 ++++----- .../kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt | 9 ++++----- .../androidx/compose/ui/platform/RenderingTestScope.kt | 7 +++---- .../kotlin/androidx/compose/ui/window/TestUtils.kt | 5 ++--- .../androidx/compose/ui/window/window/WindowStateTest.kt | 4 ++-- .../androidx/compose/ui/window/window/WindowTest.kt | 5 ++--- .../compose/ui/platform/GlobalSnapshotManager.skiko.kt | 2 +- gradle/libs.versions.toml | 2 +- 15 files changed, 32 insertions(+), 40 deletions(-) diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle index 50582a6605c4e..6fb12189aa549 100644 --- a/compose/ui/ui/build.gradle +++ b/compose/ui/ui/build.gradle @@ -168,7 +168,6 @@ if(AndroidXComposePlugin.isMultiplatformEnabled(project)) { dependsOn(skikoMain) dependencies { implementation(libs.kotlinStdlibJdk8) - api(libs.kotlinCoroutinesSwing) } } diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ConfigureSwingGlobalsForCompose.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ConfigureSwingGlobalsForCompose.desktop.kt index 540d5dadd3a57..9e7ae841743d6 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ConfigureSwingGlobalsForCompose.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ConfigureSwingGlobalsForCompose.desktop.kt @@ -32,7 +32,7 @@ import org.jetbrains.skia.impl.Library * - sets UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) * * Should be called before using any class from `java.swing.*` - * (even before SwingUtilities.invokeLater or Dispatchers.Swing) + * (even before SwingUtilities.invokeLater or MainUIDispatcher) */ @ExperimentalComposeUiApi fun configureSwingGlobalsForCompose( diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeLayer.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeLayer.desktop.kt index 14eeaa3f1db31..9e7a7221c3fd2 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeLayer.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeLayer.desktop.kt @@ -31,9 +31,8 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.window.WindowExceptionHandler import androidx.compose.ui.window.density import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.swing.Swing import org.jetbrains.skia.Canvas +import org.jetbrains.skiko.MainUIDispatcher import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkikoView import java.awt.Cursor @@ -93,7 +92,7 @@ internal class ComposeLayer { } internal val scene = ComposeScene( - Dispatchers.Swing + coroutineExceptionHandler, + MainUIDispatcher + coroutineExceptionHandler, _component, Density(1f), _component::needRedraw, diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.desktop.kt index 9c87591860223..7928febcf04ec 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.desktop.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.consumeEach import kotlinx.coroutines.launch -import kotlinx.coroutines.swing.Swing +import org.jetbrains.skiko.MainUIDispatcher import java.util.concurrent.atomic.AtomicBoolean /** @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean * * Composition bootstrapping mechanisms for a particular platform/framework should call * [ensureStarted] during setup to initialize periodic global snapshot notifications. - * For desktop, these notifications are always sent on [Dispatchers.Swing]. Other platforms + * For desktop, these notifications are always sent on [MainUIDispatcher]. Other platforms * may establish different policies for these notifications. */ internal actual object GlobalSnapshotManager { @@ -43,7 +43,8 @@ internal actual object GlobalSnapshotManager { actual fun ensureStarted() { if (started.compareAndSet(false, true)) { val channel = Channel(Channel.CONFLATED) - CoroutineScope(Dispatchers.Swing).launch { + Dispatchers.IO + CoroutineScope(MainUIDispatcher).launch { channel.consumeEach { // TODO(https://github.com/JetBrains/compose-jb/issues/1854) get rid of synchronized synchronized(GlobalSnapshotManager) { diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Application.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Application.desktop.kt index 004939d98bcda..bea831ca0c980 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Application.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Application.desktop.kt @@ -41,9 +41,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext import kotlinx.coroutines.yield +import org.jetbrains.skiko.MainUIDispatcher import kotlin.system.exitProcess /** @@ -195,7 +195,7 @@ suspend fun awaitApplication( if (System.getProperty("compose.application.configure.swing.globals") == "true") { configureSwingGlobalsForCompose() } - withContext(Dispatchers.Swing) { + withContext(MainUIDispatcher) { withContext(YieldFrameClock) { GlobalSnapshotManager.ensureStarted() diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/AwtWindow.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/AwtWindow.desktop.kt index d5d32eb61f177..f9e6148ea0f01 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/AwtWindow.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/AwtWindow.desktop.kt @@ -26,11 +26,10 @@ import androidx.compose.ui.node.Ref import androidx.compose.ui.util.UpdateEffect import androidx.compose.ui.util.makeDisplayable import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import kotlinx.coroutines.swing.Swing +import org.jetbrains.skiko.MainUIDispatcher import java.awt.Window /** @@ -117,7 +116,7 @@ fun AwtWindow( // So we will have a wrong active window (window1). showJob.value?.cancel() - showJob.value = GlobalScope.launch(Dispatchers.Swing) { + showJob.value = GlobalScope.launch(MainUIDispatcher) { window().isVisible = currentVisible } } diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/ImageComposeSceneTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/ImageComposeSceneTest.kt index 963c54c5be7d2..313b308070e40 100644 --- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/ImageComposeSceneTest.kt +++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/ImageComposeSceneTest.kt @@ -39,10 +39,9 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp import kotlin.time.Duration.Companion.seconds import kotlin.time.ExperimentalTime -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.swing.Swing +import org.jetbrains.skiko.MainUIDispatcher import org.junit.Ignore import org.junit.Rule import org.junit.Test @@ -118,7 +117,7 @@ class ImageComposeSceneTest { @Test(timeout = 5000) fun `closing ImageComposeScene should not cancel coroutineContext's Job`() { - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { val scene = ImageComposeScene(100, 100, coroutineContext = coroutineContext) scene.close() } diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposePanelTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposePanelTest.kt index 371a4536e2a67..6e6e8a79c8e61 100644 --- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposePanelTest.kt +++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposePanelTest.kt @@ -24,9 +24,8 @@ import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp import androidx.compose.ui.window.density import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.swing.Swing +import org.jetbrains.skiko.MainUIDispatcher import org.junit.Assume import org.junit.Test import java.awt.Dimension @@ -38,7 +37,7 @@ class ComposePanelTest { fun `don't override user preferred size`() { Assume.assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance) - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { val composePanel = ComposePanel() composePanel.preferredSize = Dimension(234, 345) assertThat(composePanel.preferredSize).isEqualTo(Dimension(234, 345)) @@ -63,7 +62,7 @@ class ComposePanelTest { fun `pack to Compose content`() { Assume.assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance) - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { val composePanel = ComposePanel() composePanel.setContent { Box(Modifier.requiredSize(300.dp, 400.dp)) @@ -93,7 +92,7 @@ class ComposePanelTest { val layoutPassConstraints = mutableListOf() - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { val composePanel = ComposePanel() composePanel.setContent { Box(Modifier.fillMaxSize().layout { _, constraints -> diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt index 034a57e2902cb..9a1d93dbf98fc 100644 --- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt +++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/awt/ComposeWindowTest.kt @@ -44,9 +44,8 @@ import java.awt.event.MouseEvent.MOUSE_MOVED import java.awt.event.MouseEvent.MOUSE_PRESSED import java.awt.event.MouseEvent.MOUSE_RELEASED import java.awt.event.WindowEvent -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.swing.Swing +import org.jetbrains.skiko.MainUIDispatcher import org.junit.Assume import org.junit.Test @@ -142,7 +141,7 @@ class ComposeWindowTest { fun `don't override user preferred size`() { Assume.assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance) - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { val window = ComposeWindow() try { window.preferredSize = Dimension(234, 345) @@ -160,7 +159,7 @@ class ComposeWindowTest { fun `pack to Compose content`() { Assume.assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance) - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { val window = ComposeWindow() try { window.setContent { @@ -187,7 +186,7 @@ class ComposeWindowTest { val layoutPassConstraints = mutableListOf() - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { val window = ComposeWindow() try { window.size = Dimension(300, 400) diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt index dbc107b188df1..c90acb317d215 100644 --- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt +++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/RenderingTestScope.kt @@ -24,21 +24,20 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Density import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.yield import org.jetbrains.skia.Canvas import org.jetbrains.skia.Surface import org.jetbrains.skiko.FrameDispatcher +import org.jetbrains.skiko.MainUIDispatcher import kotlin.coroutines.CoroutineContext internal fun renderingTest( width: Int, height: Int, - context: CoroutineContext = Dispatchers.Swing, + context: CoroutineContext = MainUIDispatcher, block: suspend RenderingTestScope.() -> Unit -) = runBlocking(Dispatchers.Swing) { +) = runBlocking(MainUIDispatcher) { val scope = RenderingTestScope(width, height, context) try { scope.block() diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/TestUtils.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/TestUtils.kt index 6ce74e06f2d48..8c0a25e80e65d 100644 --- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/TestUtils.kt +++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/TestUtils.kt @@ -24,16 +24,15 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.Snapshot import java.awt.GraphicsEnvironment import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withTimeout import kotlinx.coroutines.yield +import org.jetbrains.skiko.MainUIDispatcher import org.junit.Assume.assumeFalse @OptIn(ExperimentalCoroutinesApi::class) @@ -52,7 +51,7 @@ internal fun runApplicationTest( ) { assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance) - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { withTimeout(30000) { val exceptionHandler = TestExceptionHandler() withExceptionHandler(exceptionHandler) { diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowStateTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowStateTest.kt index c3d25e63b3c6a..b82a967880539 100644 --- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowStateTest.kt +++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowStateTest.kt @@ -394,7 +394,7 @@ class WindowStateTest { @Test fun `restore size and position after maximize`() = runApplicationTest { // Swing/macOs can't re-change isMaximized in a deterministic way: -// fun main() = runBlocking(Dispatchers.Swing) { +// fun main() = runBlocking(MainUIDispatcher) { // val window = ComposeWindow() // window.size = Dimension(200, 200) // window.isVisible = true @@ -498,7 +498,7 @@ class WindowStateTest { @Test fun `minimize window before show`() = runApplicationTest { // Linux/macos doesn't support this: -// fun main() = runBlocking(Dispatchers.Swing) { +// fun main() = runBlocking(MainUIDispatcher) { // val window = ComposeWindow() // window.size = Dimension(200, 200) // window.isMinimized = true diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowTest.kt index 98d1c896516b7..7853ce1ad1461 100644 --- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowTest.kt +++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/window/WindowTest.kt @@ -50,11 +50,10 @@ import java.awt.Dimension import java.awt.GraphicsEnvironment import java.awt.event.WindowAdapter import java.awt.event.WindowEvent -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.swing.Swing +import org.jetbrains.skiko.MainUIDispatcher import org.junit.Assume.assumeFalse import org.junit.Test import kotlinx.coroutines.cancelAndJoin @@ -416,7 +415,7 @@ class WindowTest { val oldRecomposers = Recomposer.runningRecomposers.value - runBlocking(Dispatchers.Swing) { + runBlocking(MainUIDispatcher) { repeat(10) { val window = ComposeWindow() window.size = Dimension(200, 200) diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.skiko.kt index d011e093f24e6..cbdfccbfca889 100644 --- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.skiko.kt +++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.skiko.kt @@ -24,7 +24,7 @@ package androidx.compose.ui.platform * * Composition bootstrapping mechanisms for a particular platform/framework should call * [ensureStarted] during setup to initialize periodic global snapshot notifications. - * For desktop, these notifications are always sent on [Dispatchers.Swing]. Other platforms + * For desktop, these notifications are always sent on [MainUIDispatcher]. Other platforms * may establish different policies for these notifications. */ internal expect object GlobalSnapshotManager { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1a7dfc9418ebc..29c9110be23cc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,7 +38,7 @@ ksp = "1.6.20-1.0.4" ktlint = "0.43.0" leakcanary = "2.7" mockito = "2.25.0" -skiko = "0.7.14" +skiko = "0.7.15" sqldelight = "1.3.0" wire = "3.6.0"