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
16.01k stars 1.17k forks source link

How to open Window/Dialog on the same screen as launcher/trigger, when working with multiple screen #1845

Closed haikalpribadi closed 2 years ago

haikalpribadi commented 2 years ago

Say you have 2 screens -- Screen 1 and Screen 2, and the "main/default" screen is Screen 1. When you launch the application from Screen 2 (via terminal, or application icon, or IDE), the application Window will always start in Screen 1. Even after you drag the application window to Screen 2, when you click any button that opens a Dialog, that dialog will always open in Screen 1, and you have to drag it manually to Screen 2 again.

Effectively, how can we get Compose to recognise that you're working on multiple screens, and that it should focus on working on just the one screen that the application sits in the moment?

haikalpribadi commented 2 years ago

Hey guys, @igordmn and @akurasov any updates on this? Seems like it's removed from you milestone 4 days ago now? We actually start having customers complaining to us about this issue. Is there anything we can do to help from our side?

igordmn commented 2 years ago

Hey!

Our plans regarding 1.1.2/1.2 are changed, and scope of it will be revised.

Currently, if you need this feature right now, you can copy-paste Window / Dialog, and use another constructor of ComposeWindow:

import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.awt.ComposeWindow
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import java.awt.Dimension
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.swing.JFrame

private var isSecondWindowOpened by mutableStateOf(false)

fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        Button({
            isSecondWindowOpened = !isSecondWindowOpened
        }) {
            Text("Second window")
        }
    }

    if (isSecondWindowOpened) {
        val graphicsConfiguration = remember {
            java.awt.Window.getWindows().find { it.isActive }?.graphicsConfiguration
        }
        Window(
            create = {
                ComposeWindow(graphicsConfiguration).apply {
                    defaultCloseOperation = JFrame.DO_NOTHING_ON_CLOSE
                    size = Dimension(200, 200)
                    addWindowListener(object : WindowAdapter() {
                        override fun windowClosing(e: WindowEvent) {
                            isSecondWindowOpened = false
                        }
                    })
                }
            },
            dispose = ComposeWindow::dispose
        ) {
        }
    }
}

(I don't have multiple monitors right now, so can't verify if code is correct)

haikalpribadi commented 2 years ago

Can you tell me which what's the key requirement in the code above, @igordmn ?

haikalpribadi commented 2 years ago

Okay, I think I understand the essence of what you're doing there: you're passing GraphicsConfiguration from java.aw.Window.getWindows() that isActive into ComposeWindow(). Am I right?

However, right now we construct our windows using Window() from androidx.compose.ui.window.Window.desktop.kt, which seems to manage a lot of things such as: title, resizing, icon, focus, alwaysOnTop, closeRequest, minimization, decoration, placement, position, etc. If we go with your suggestion above, does that mean we will loose all these functionalities? @igordmn

igordmn commented 2 years ago

I think I understand the essence of what you're doing there: you're passing GraphicsConfiguration from

Yes, GraphicsConfiguration defines on which display the window should be shown.

However, right now we construct our windows using Window()

Yes, it is unfortunate, but you can copy-paste this file (temporarily), and pass graphicsConfiguration here

We will add this feature into the official release, but before that we should be sure that this code won't break other cases (aligning in the center of the screen, positioning, etc).

haikalpribadi commented 2 years ago

I did think of doing that, but as I expected, copying the file over is not possible as it depends on internal APIs:

Error:(33, 33) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(34, 33) cannot access 'makeDisplayable': it is internal in 'androidx.compose.ui.util'
Error:(35, 33) cannot access 'setIcon': it is internal in 'androidx.compose.ui.util'
Error:(36, 33) cannot access 'setPositionSafely': it is internal in 'androidx.compose.ui.util'
Error:(37, 33) cannot access 'setSizeSafely': it is internal in 'androidx.compose.ui.util'
Error:(38, 33) cannot access 'setUndecoratedSafely': it is internal in 'androidx.compose.ui.util'
Error:(144, 19) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(195, 21) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(196, 17) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(197, 17) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(197, 17) cannot access 'setIcon': it is internal in 'androidx.compose.ui.util'
Error:(198, 17) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(198, 17) cannot access 'setUndecoratedSafely': it is internal in 'androidx.compose.ui.util'
Error:(199, 17) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(200, 17) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(201, 17) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(202, 17) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(203, 17) cannot access 'ComponentUpdater': it is internal in 'androidx.compose.ui.util'
Error:(206, 24) cannot access 'setSizeSafely': it is internal in 'androidx.compose.ui.util'
Error:(210, 24) cannot access 'setPositionSafely': it is internal in 'androidx.compose.ui.util'
Error:(384, 20) cannot access 'makeDisplayable': it is internal in 'androidx.compose.ui.util'

Did you anticipate this? Or is there a workaround, @igordmn ?

Thomas-Vos commented 2 years ago

You can add the following to the top of your file, but use with caution:

@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
haikalpribadi commented 2 years ago

Thanks @Thomas-Vos ! That worked like magic! Crazy dangerous 😅 -- but magic.

@igordmn I gave it a try as you suggested: https://github.com/haikalpribadi/typedb-studio/commit/ad1be4cfefe627e297dea1f03598fa8b6b797115

However, the application still opens only in the "main screen", and most critically: after I drag the application into the second screen, all dialogs (androidx.compose.ui.window.Dialog) that I open still opens only in the main (first) screen.

I tried to look into whether I can pass GraphicsConfiguration into ComposeDialog (androidx.compose.ui.awt.ComposeDialog) and I don't seem to find any such input parameter.

Do you have an idea where I should go from here, @igordmn ?

haikalpribadi commented 2 years ago

Also, @igordmn, when you said "We will add this feature into the official release" do you mean 1.2.0? Or not yet known?

igordmn commented 2 years ago

Probably it will be in 1.2.0, if there won't be major issues with the code above.

haikalpribadi commented 2 years ago

I implemented it the way you suggested, above, but it didn't work. Any idea why, @igordmn ?

haikalpribadi commented 2 years ago

Any updates on this @igordmn ?

igordmn commented 2 years ago

No updates yet. I don't have the second monitor right now to test it, but probably will receive it this month.

okushnikov commented 1 month ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.