From Swing to Compose Desktop #5
Tags: java, kotlin, ux
Published: 2020-12-06 13:45:00

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:

{% youtube -pduuIlXTls %}

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

```kotlin
private fun colors(): Colors = if (isInDarkMode) {
    darkColors()
} else {
    lightColors()
}
```

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

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

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](https://github.com/JetBrains/compose-jb/issues/169) 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.

```kotlin
AppManager.setMenu(
    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 {
  TKDupeFinderContent()
}
```

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

```kotlin
name = if (isInDarkMode) "Light Mode" else "Dark 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

```kotlin
onClick = {
  isInDarkMode = !isInDarkMode
},
```

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

```kotlin
@Composable
fun TKDupeFinderContent() {
    var colors by remember { mutableStateOf(colors()) }
    onIsInDarkModeChanged = { _, _ ->
        colors = colors()
    }
```

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](https://dev.to/tkuenneth/from-swing-to-jetpack-compose-desktop-1-12kf)
[From Swing to Jetpack Compose Desktop #2](https://dev.to/tkuenneth/from-swing-to-jetpack-compose-desktop-2-4a4h)
[From Swing to Compose Desktop #3](https://dev.to/tkuenneth/from-swing-to-compose-desktop-3-23ck)
[From Swing to Compose Desktop #4](https://dev.to/tkuenneth/from-swing-to-compose-desktop-4-35c1)