Repository navigation
Item labels are not reactive (Open/Close text stays stale after state change) #434
Description
Problem
CheckableItem(checked) is now reactive after #432 (live AtomicBoolean + updateMenuItemCheckedState). Item labels are not.
Changing Item(label = …) from Compose state does not update the native menu text until something else forces a full tray rebuild (e.g. the app wrapping Tray in key(label) and disposing NativeTray).
MenuContentHash / the composable-DSL fingerprint already include label. The native menu still keeps the text captured at last setupMenu. Unlike checkmarks, there is no updateMenuItemText / rebuild-on-label-change path.
Seen on Windows 11 with ComposeNativeTray dev (post-#433): a tray item that should read "Open Dashboard" / "Close Dashboard" depending on window visibility stayed on "Open Dashboard" after the window opened.
Expected
Opening the tray menu shows the current Item label. Toggling the same flag from a window (or from the item itself) updates the text on the next open, without the app disposing Tray.
Minimal reproduction
Painter Tray (the updateComposable path). Flip the switch, then open the tray menu.
fun main() = nucleusApplication(enableSingleInstance = false) {
var dashboardOpen by remember { mutableStateOf(false) }
Tray(
icon = painterResource(Res.drawable.icon),
tooltip = "Item label repro",
primaryAction = { dashboardOpen = true },
) {
Item(
label = if (dashboardOpen) "Close Dashboard" else "Open Dashboard",
) {
dashboardOpen = !dashboardOpen
}
Item(label = "Quit") { exitApplication() }
}
DecoratedWindow(
onCloseRequest = ::exitApplication,
title = "Item label repro",
state = rememberWindowState(size = DpSize(280.dp, 120.dp)),
) {
Row(Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically) {
Switch(checked = dashboardOpen, onCheckedChange = { dashboardOpen = it })
Text(if (dashboardOpen) "dashboard open" else "dashboard closed")
}
}
}Steps
- Run. Open the tray menu — item is "Open Dashboard".
- Turn the window
Switchon (or click the tray item once). - Open the tray menu again.
Actual
Step 3 still shows "Open Dashboard". Clicking it toggles state again (or no-ops depending on captured onClick), but the text does not follow.
Workaround used in apps: key(dashboardOpen) { Tray(…) }, which disposes and recreates the tray icon.
Suggested fix
Same class of fix as #432, for labels (and ideally isEnabled):
- Include
labelin the structure that already triggers a native rebuild (menuHash/ composable fingerprint — already there). - Make sure that rebuild actually runs and replaces menu item text, or add
updateMenuItemLabelnext toupdateMenuItemCheckedState. - Do not require callers to
key()/ disposeNativeTrayfor a label change.
CheckableItem proving checkmarks can update without a full icon re-render is the model; Item text should be the same.
Tech
- OS: Windows 11
- ComposeNativeTray
dev(includes fix: make CheckableItem reactive and serialize tray updates #433 / CheckableItem is not reactive: captured checked + concurrent menu updates #432) - Nucleus
dev, Kotlin 2.4.10, Compose MP 1.12, JDK 25
Activity
fix: propagate composable menu DSL state changes to the native menu