Skip to content

macOS: Keep the main menu updating after a MenuBarModel change - #1745

Closed
emezeske wants to merge 1 commit into
juce-framework:developfrom
emezeske:pr/mac-main-menu-delegate
Closed

emezeske wants to merge 1 commit into
juce-framework:developfrom
emezeske:pr/mac-main-menu-delegate

Conversation

@emezeske

Copy link
Copy Markdown
Contributor

On macOS, items in the native main menu bar stop reflecting their enabled state after the first MenuBarModel::menuItemsChanged() call: opening a menu shows whatever state the commands had at that moment, and later changes are not picked up until the next call. Any app that uses setMacMainMenu() together with setApplicationCommandManagerToWatch() hits this as soon as keyboard focus moves, because ApplicationCommandManager calls commandStatusChanged() on every focus change and that ends in menuItemsChanged().

To reproduce, register a command whose getCommandInfo() enabled state depends on something that can change without moving keyboard focus, such as a selection made with a keyboard shortcut, and put it in a menu shown through setMacMainMenu() with setApplicationCommandManagerToWatch() in effect. Move focus once so the bar is rebuilt, then change that state and open the menu. The item still shows the old state, and keeps showing it until focus moves again.

The menus built when the bar is first populated get the JuceMenuCallbackClass delegate, whose menuNeedsUpdate: rebuilds the menu from the model each time it opens. The replacements that menuBarItemsChanged() creates through updateTopLevelMenu() were allocated without that delegate, so they were never refreshed on open. This change creates them through createMenu() with the delegate attached, as the initial menus are.

Tested on macOS 26 with a standalone app: reading the menu's items through Accessibility before and after a keyboard-driven selection change now shows the enabled states following the model, where before the change they stayed at the values from the last focus change.

Items in the native main menu bar stopped reflecting their current
enabled state: after a MenuBarModel::menuItemsChanged() call, opening a
menu showed whatever state the items had at that moment, and later
changes to the command targets were never picked up.

Any app that uses MenuBarModel::setMacMainMenu() together with
setApplicationCommandManagerToWatch() hits this as soon as keyboard focus
moves, because ApplicationCommandManager calls commandStatusChanged() on
every focus change and that ends in menuItemsChanged(). A command whose
enabled state depends on something that changes without moving focus,
such as a selection made with the keyboard, then shows the stale state
until the next focus change.

The menus created when the bar is first populated get the
JuceMenuCallbackClass delegate, whose menuNeedsUpdate: rebuilds the menu
from the model every time it opens. The replacement menus that
menuBarItemsChanged() builds via updateTopLevelMenu() were created
without that delegate, so they were never refreshed on open.

Create the replacement through createMenu() with the delegate attached,
as the initial menu is.
@emezeske
emezeske marked this pull request as ready for review September 11, 2026 21:39
@juce-push-bot

Copy link
Copy Markdown
Collaborator

This pull request has been mentioned on The JUCE Forum. There might be relevant details there:

https://forum.juce.com/t/pr-macos-bugfix-keep-the-main-menu-updating-after-a-menubarmodel-change/69488/1

@szarvas

szarvas commented Sep 29, 2026

Copy link
Copy Markdown
Member

Fixed in 9336be2

@szarvas szarvas closed this Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants