JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.73k stars 1.14k forks source link

Showing a child window for the second time fails #477

Open Inego opened 3 years ago

Inego commented 3 years ago
fun main() {

    val checkBoxState = mutableStateOf(false)

    Window {
        val childWindow = remember { AppWindow("Child") }
        val compositionContext = rememberCompositionContext()

        Checkbox(checkBoxState.value, {

            checkBoxState.value = it

            if (it) {
                childWindow.show(compositionContext) {
                    Text("Content")
                }
            } else {
                childWindow.close()
            }
        })
    }
}
  1. Clicking the checkbox displays the child window.
  2. Clicking the checkbox again closes the child window.
  3. Clicking the checkbox again does not show the child window again, instead, an exception is thrown:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Check failed.
    at androidx.compose.desktop.ComposeLayer.setContent$ui(ComposeLayer.desktop.kt:244)
    at androidx.compose.desktop.ComposeWindow.setContent(ComposeWindow.desktop.kt:72)
    at androidx.compose.desktop.AppWindow.onCreate(AppWindow.desktop.kt:415)
    at androidx.compose.desktop.AppWindow.show(AppWindow.desktop.kt:441)
    at TempKt$main$1$1.invoke(Temp.kt:22)

Compose Desktop 0.3.2.

Inego commented 3 years ago

Judging by the code, it seems an AppWindow cannot be shown after it was closed.

    internal fun setContent(
        parentComposition: CompositionContext? = null,
        content: @Composable () -> Unit
    ) {
        check(!isDisposed)
        check(this.content == null) { "Cannot set content twice" }
        ...
igordmn commented 3 years ago

Thanks!

We plan to gradually replace AppWindow API by Composable Window API (prototype).

So the current example probably will be:

fun main() = Application {
    val checkBoxState = remember { mutableStateOf(false) }

    Window {
        if (checkBoxState) {
            Window("Child") {
                Text("Content")
            }
        }

        Checkbox(checkBoxState.value, {
            checkBoxState.value = it
        })
    }
}

(it is not a final API, don't know yet how we will support child non-modal windows).

Regarding the current API - don't know if it is good or bad to provide ability to reshow AppWindow 🤔 (we can make AppWindow nullable and just create a new AppWindow). We would intevestigate use-cases, if we aren't implementing a new API.

Inego commented 3 years ago

We plan to gradually replace AppWindow API by Composable Window API (prototype).

So the current example probably will be: ...

This API looks great! I wonder how fine-tuning of such windows will work (like, to specify window keyboard shortcuts).

igordmn commented 3 years ago

how fine-tuning of such windows will work

Probably something like this:

val state = rememberWindowState(width = 100.dp)
Window(title = "Title", state = state) {
  Button(onClick = { state.resize(height= 200.dp) })
}

We will also provide the ability to manually tune underlying JFrame:

Window(title = "Title", configure = { jframe : JFrame ->
  // do something with jframe 
})

specify window keyboard shortcuts

We don't know yet about window shortcuts, but we will try to keep all the current functionality in the new API. If we remove some functionality we will provide an alternative.

olonho commented 3 years ago

Maybe something like https://github.com/JetBrains/compose-jb/tree/master/tutorials/Swing_Integration#adding-a-swing-component-to-cfd-composition-using-swingpanel which returns JPanel would make sense.