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
Switch on (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
label in 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
updateMenuItemLabel next to updateMenuItemCheckedState.
- Do not require callers to
key() / dispose NativeTray for a label change.
CheckableItem proving checkmarks can update without a full icon re-render is the model; Item text should be the same.
Tech
Problem
CheckableItem(checked)is now reactive after #432 (liveAtomicBoolean+updateMenuItemCheckedState).Itemlabels 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 wrappingTrayinkey(label)and disposingNativeTray).MenuContentHash/ the composable-DSL fingerprint already includelabel. The native menu still keeps the text captured at lastsetupMenu. Unlike checkmarks, there is noupdateMenuItemText/ 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
Itemlabel. Toggling the same flag from a window (or from the item itself) updates the text on the next open, without the app disposingTray.Minimal reproduction
Painter
Tray(theupdateComposablepath). Flip the switch, then open the tray menu.Steps
Switchon (or click the tray item once).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):labelin the structure that already triggers a native rebuild (menuHash/ composable fingerprint — already there).updateMenuItemLabelnext toupdateMenuItemCheckedState.key()/ disposeNativeTrayfor a label change.CheckableItemproving checkmarks can update without a full icon re-render is the model;Itemtext should be the same.Tech
dev(includes fix: make CheckableItem reactive and serialize tray updates #433 / CheckableItem is not reactive: captured checked + concurrent menu updates #432)dev, Kotlin 2.4.10, Compose MP 1.12, JDK 25