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) {
darkColors()
} else {
lightColors()
}
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
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.
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
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
onClick = {
isInDarkMode = !isInDarkMode
},
will trigger a callback from my observable, once it has been set. I do it like this:
@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
From Swing to Jetpack Compose Desktop #2
From Swing to Compose Desktop #3
From Swing to Compose Desktop #4
Top comments (0)