DEV Community

Cover image for From Swing to Compose Desktop #5

From Swing to Compose Desktop #5

Thomas Künneth
Developer. Speaker. Listener. Loves writing. GDE Android. Confessing mobile computing addict ;-)
Updated on ・3 min read

Welcome to the 5. post about my journey of transforming a Java Swing app to Compose for Desktop. Today I will focus on menubars and light and dark colors. Menubars are a vital ui metaphor in Desktop operating systems so it is very good to have support for them in Compose Desktop, too. As you will see shortly it may be a little limited at the moment. Keep in mind, though, that currently Compose for Desktop is in preview.

To wet your appetite please take a look at this clip:

Let us take a look at how to choose light or dark colors.

private fun colors(): Colors = if (isInDarkMode) {
} else {
Enter fullscreen mode Exit fullscreen mode

Both darkColors() and lightColors() belong to androidx.compose.material.Colors.kt. isInDarkMode is a variable I defined like this:

private var isInDarkMode: Boolean by observable(true /* isSystemInDarkTheme() */) { _, oldValue, newValue ->
    onIsInDarkModeChanged?.let { it(oldValue, newValue) }
private var onIsInDarkModeChanged: ((Boolean, Boolean) -> Unit)? = null
Enter fullscreen mode Exit fullscreen mode

Jetpack Compose on Android allows us to check if the system is currently in dark mode with isSystemInDarkTheme() but currently this is not supported in Compose Desktop. There is a feature request regarding this. Please consider voting for it.

As you can see, isInDarkMode is an observable. When its value changes I invoke onIsInDarkModeChanged if it is not null. Yes, this looks strange. Why did I not remember it inside a composable? Here is how the menubar is set up. Notice that this is done before the AppWindow comes into play.

    MenuBar(Menu("Appearance", MenuItem(
        name = if (isInDarkMode) "Light Mode" else "Dark Mode",
        onClick = {
          isInDarkMode = !isInDarkMode
        shortcut = KeyStroke(Key.L)
AppWindow(title = "TKDupeFinder",
    size = IntSize(600, 400)).show {
Enter fullscreen mode Exit fullscreen mode

Both Menu and MenuBar currently are classes, not composables. To get a global menubar, you need to set it up using AppManager.setMenu(). When a new AppWindow is created, it inherits the global menubar. So, if you change the menubar afterwards, the AppWindow (that is, its menubar) will not be affected. Consequently, my code

name = if (isInDarkMode) "Light Mode" else "Dark Mode",
Enter fullscreen mode Exit fullscreen mode

makes no sense, as it is executed just once. It is like this because I wrote it before I learned what I just told you. 😂

I think to be able to change menus they would need to be composables. And changes to the global menubar would need to be propagated to menubars inside windows. I may not be able to change the menu, but obviously switching colors does work. How is that? Remember that

onClick = {
  isInDarkMode = !isInDarkMode
Enter fullscreen mode Exit fullscreen mode

will trigger a callback from my observable, once it has been set. I do it like this:

fun TKDupeFinderContent() {
    var colors by remember { mutableStateOf(colors()) }
    onIsInDarkModeChanged = { _, _ ->
        colors = colors()
Enter fullscreen mode Exit fullscreen mode

This way a composable can react to changes of variables that are defined outside. To conclude, let's recap the takeaways:

  • Currently menus and menubars appear to be no composables
  • Altering menubars may be difficult at the moment

Did I miss something? Please share your findings, ideas and suggestions in the comments.

From Swing to Jetpack Compose Desktop #1
From Swing to Jetpack Compose Desktop #2
From Swing to Compose Desktop #3
From Swing to Compose Desktop #4

Discussion (0)