'Unable to bring window to foreground with compose desktop

With the following code the application window can be hidden using the button and restored using a global shortcut ALT+S. Now I would like to also use the shortcut to bring the window to the foreground (if it wasn't hidden).

Find below my failed attempt to do so. (I am relatively new to the matter of jetpack compose.)

var windowVisible = mutableStateOf(true)

@Composable
fun App(windowFocusRequester: FocusRequester) {
    MaterialTheme() {
        Button(modifier = Modifier.focusRequester(windowFocusRequester), onClick = {
            println("click to hide received")
            windowVisible.value = false
        }) {
            Text("Hide window (ALT+S to show)")
        }
    }
}

fun main() = application() {
    Window(onCloseRequest = ::exitApplication, visible = windowVisible.value, focusable = true,
    ) {
        val windowFocusRequester = remember { FocusRequester() }
        val provider = Provider.getCurrentProvider(false)
        provider.register(
            KeyStroke.getKeyStroke("alt S")
        ) {
            println("shortcut to show received")
            windowVisible.value = true
            windowFocusRequester.requestFocus()
        }
        App(windowFocusRequester)
    }
}

Probably you would need to add the FocusRequester as a modifier to the Window but this does not seem to be possible.

To be able to run the code this lib is needed

implementation("com.github.tulskiy:jkeymaster:1.3")

Thanks for any ideas to try, advance or even workaround! (maybe accessing awt window?)



Solution 1:[1]

I came across the same issue and ended up with this solution:

import androidx.compose.runtime.*
import androidx.compose.ui.window.FrameWindowScope
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowState
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

@Composable
fun FrameWindowScope.WindowFocusRequester(state: WindowState): () -> Unit {
    var requestFocus by remember { mutableStateOf(false) }
    val scope = rememberCoroutineScope()
    var showFakeWindow by remember { mutableStateOf(false) }

    if (requestFocus) {
        requestFocus = false
        showFakeWindow = true

        scope.launch {
            delay(1)
            showFakeWindow = false
        }
    }

    if (showFakeWindow) {
        Window({}) {}
        window.toFront()
        state.isMinimized = false
    }

    return { requestFocus = true }
}

Use the code like this:

fun main() = application {
    val state = rememberWindowState()
    Window({ exitApplication() }, state) {
        val requestWindowFocus = WindowFocusRequester(state)

        // Inside some listener just invoke the returned lambda
        requestWindowFocus()
    }
}

It is a hack. So maybe it will not work on every OS. I tested it with Ubuntu 20 and Compose 1.1.1.

If this is not working for you, try to increase the delay duration or exchange the toFront() call with something mentioned here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1