Skip to content

Commit

Permalink
Don't depend on kotlinx-coroutines-swing
Browse files Browse the repository at this point in the history
Because it overrides Dispatchers.Main

Fixes JetBrains/compose-multiplatform#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")
}
```
  • Loading branch information
igordmn committed Mar 10, 2022
1 parent ff10e59 commit b19e55d
Show file tree
Hide file tree
Showing 16 changed files with 34 additions and 43 deletions.
1 change: 0 additions & 1 deletion compose/ui/ui/build.gradle
Expand Up @@ -163,7 +163,6 @@ if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
dependsOn(skikoMain)
dependencies {
implementation(libs.kotlinStdlibJdk8)
api(libs.kotlinCoroutinesSwing)
}
dependsOn(jvmMain)
}
Expand Down
Expand Up @@ -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(
Expand Down
Expand Up @@ -34,9 +34,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.Component
Expand Down Expand Up @@ -93,7 +92,7 @@ internal class ComposeLayer {
}

internal val scene = ComposeScene(
Dispatchers.Swing + coroutineExceptionHandler,
MainUIDispatcher + coroutineExceptionHandler,
_component,
Density(1f),
_component::needRedraw,
Expand Down
Expand Up @@ -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

/**
Expand All @@ -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 {
Expand All @@ -43,7 +43,8 @@ internal actual object GlobalSnapshotManager {
actual fun ensureStarted() {
if (started.compareAndSet(false, true)) {
val channel = Channel<Unit>(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) {
Expand Down
Expand Up @@ -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

/**
Expand Down Expand Up @@ -196,7 +196,7 @@ suspend fun awaitApplication(
if (System.getProperty("compose.application.configure.swing.globals") == "true") {
configureSwingGlobalsForCompose()
}
withContext(Dispatchers.Swing) {
withContext(MainUIDispatcher) {
withContext(YieldFrameClock) {
GlobalSnapshotManager.ensureStarted()

Expand Down
Expand Up @@ -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

/**
Expand Down Expand Up @@ -117,7 +116,7 @@ fun <T : Window> 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
}
}
Expand Down
Expand Up @@ -38,10 +38,9 @@ import androidx.compose.ui.test.InternalTestApi
import androidx.compose.ui.test.junit4.DesktopScreenshotTestRule
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
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
Expand Down Expand Up @@ -153,7 +152,7 @@ class ImageComposeSceneTest {

@Test(timeout = 5000)
fun `closing ImageComposeScene should not cancel coroutineContext's Job`() {
runBlocking(Dispatchers.Swing) {
runBlocking(MainUIDispatcher) {
val window = ImageComposeScene(100, 100, coroutineContext = coroutineContext)
window.close()
}
Expand Down
Expand Up @@ -9,9 +9,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
Expand All @@ -23,7 +22,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))
Expand All @@ -48,7 +47,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))
Expand Down Expand Up @@ -78,7 +77,7 @@ class ComposePanelTest {

val layoutPassConstraints = mutableListOf<Constraints>()

runBlocking(Dispatchers.Swing) {
runBlocking(MainUIDispatcher) {
val composePanel = ComposePanel()
composePanel.setContent {
Box(Modifier.fillMaxSize().layout { _, constraints ->
Expand Down
Expand Up @@ -14,9 +14,8 @@ import androidx.compose.ui.sendMouseEvent
import androidx.compose.ui.window.density
import androidx.compose.ui.window.runApplicationTest
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.GraphicsEnvironment
Expand All @@ -28,7 +27,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)
Expand All @@ -46,7 +45,7 @@ class ComposeWindowTest {
fun `pack to Compose content`() {
Assume.assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance)

runBlocking(Dispatchers.Swing) {
runBlocking(MainUIDispatcher) {
val window = ComposeWindow()
try {
window.setContent {
Expand All @@ -73,7 +72,7 @@ class ComposeWindowTest {

val layoutPassConstraints = mutableListOf<Constraints>()

runBlocking(Dispatchers.Swing) {
runBlocking(MainUIDispatcher) {
val window = ComposeWindow()
try {
window.size = Dimension(300, 400)
Expand Down
Expand Up @@ -24,19 +24,18 @@ 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(context) {
val scope = RenderingTestScope(width, height, context)
Expand Down
Expand Up @@ -23,9 +23,8 @@ import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountBox
import androidx.compose.ui.Modifier
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.MainUIDispatcher
import org.junit.Test

@Suppress("DEPRECATION")
Expand All @@ -51,7 +50,7 @@ class TestComposeWindowTest {

@Test(timeout = 5000)
fun `disposing TestComposeWindow should not cancel coroutineContext's Job`() {
runBlocking(Dispatchers.Swing) {
runBlocking(MainUIDispatcher) {
val window = TestComposeWindow(100, 100, coroutineContext = coroutineContext)
window.dispose()
}
Expand Down
Expand Up @@ -23,16 +23,15 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.Snapshot
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
import java.awt.GraphicsEnvironment

Expand All @@ -52,7 +51,7 @@ internal fun runApplicationTest(
) {
assumeFalse(GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance)

runBlocking(Dispatchers.Swing) {
runBlocking(MainUIDispatcher) {
withTimeout(30000) {
val exceptionHandler = TestExceptionHandler()
withExceptionHandler(exceptionHandler) {
Expand Down Expand Up @@ -104,7 +103,7 @@ import java.awt.event.ComponentEvent
import javax.swing.JFrame
fun main() {
runBlocking(Dispatchers.Swing) {
runBlocking(MainUIDispatcher) {
repeat(10) {
val actions = mutableListOf<String>()
val frame = JFrame()
Expand Down
Expand Up @@ -390,7 +390,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
Expand Down Expand Up @@ -494,7 +494,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
Expand Down
Expand Up @@ -46,11 +46,10 @@ import androidx.compose.ui.window.launchApplication
import androidx.compose.ui.window.rememberWindowState
import androidx.compose.ui.window.runApplicationTest
import com.google.common.truth.Truth.assertThat
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 java.awt.Dimension
Expand Down Expand Up @@ -415,7 +414,7 @@ class WindowTest {

val oldRecomposers = Recomposer.runningRecomposers.value

runBlocking(Dispatchers.Swing) {
runBlocking(MainUIDispatcher) {
repeat(10) {
val window = ComposeWindow()
window.size = Dimension(200, 200)
Expand Down
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Expand Up @@ -35,7 +35,7 @@ ksp = "1.6.10-1.0.2"
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"

Expand Down

0 comments on commit b19e55d

Please sign in to comment.