Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?)

like image 847
Frank Bruch Avatar asked Nov 27 '25 02:11

Frank Bruch


2 Answers

Found a better solution (without the flickering): using alwaysOnTop, inspired by this answer... (does not work without the state-thingy)

var windowVisible = mutableStateOf(true)

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

fun main() = application() {
    val windowAlwaysOnTop = remember { mutableStateOf(false) }
    val state = rememberWindowState(width = 320.dp, height = 200.dp)
    Window(
        onCloseRequest = ::exitApplication, state = state, visible = windowVisible.value,
        alwaysOnTop = windowAlwaysOnTop.value, focusable = true,
    ) {
        val provider = Provider.getCurrentProvider(false)
        provider.register(
            KeyStroke.getKeyStroke("alt S")
        ) {
            println("shortcut to show received")
            windowVisible.value = true
            windowAlwaysOnTop.value = true
            windowAlwaysOnTop.value = false
        }
        App()
    }
}
like image 152
Frank Bruch Avatar answered Nov 30 '25 00:11

Frank Bruch


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.

like image 30
Sascha Avatar answered Nov 30 '25 00:11

Sascha



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!