Skip to content

Release stale native menu command items on rebuild - #4587

Open
lntutor wants to merge 5 commits into
beeware:mainfrom
lntutor:fix/native-menu-item-lifecycle
Open

Release stale native menu command items on rebuild#4587
lntutor wants to merge 5 commits into
beeware:mainfrom
lntutor:fix/native-menu-item-lifecycle

Conversation

@lntutor

@lntutor lntutor commented Jul 26, 2026

Copy link
Copy Markdown

Fixes #4580

Rebuilding menus, toolbars, and status-icon menus now deregisters the obsolete native command items on GTK, Qt, and WinForms. GTK also removes replaced application actions.

A backend-agnostic test verifies that a command no longer retains its prior native menu instances after an application-menu rebuild.

PR Checklist:

  • I will abide by the BeeWare Code of Conduct
  • I have read and have followed the CONTRIBUTING.md file
  • This PR was generated or assisted using an AI tool
    Assisted-by: OpenAI Codex

@lntutor
lntutor force-pushed the fix/native-menu-item-lifecycle branch from fbd301e to dc00219 Compare July 26, 2026 12:00
@lntutor

lntutor commented Jul 26, 2026

Copy link
Copy Markdown
Author

CI note: the linux-wayland-qt testbed job failed only in the existing layout-sensitive tests/widgets/test_selection.py::test_flex_horizontal_widget_size (expected width at least 350; observed 73). The new command_rebuild_replaces_native_items test passed in that job. All other completed backend testbeds are green; the remaining jobs are still running.

@lntutor
lntutor force-pushed the fix/native-menu-item-lifecycle branch from dc00219 to 2af2346 Compare July 26, 2026 15:01

@freakboy3742 freakboy3742 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted inline, the additional test isn't actually verifying anything; but the core of the implementation is on the right track. While the mechanics are correct, it's missing the "spirit" of the suggested change.

Cocoa is highlighted as a backend that doesn't have the problem - it has a matching pair of create_menu_item and remove_menu_item methods, and remove_menu_item does the full removal process. This means there's symmetry in the interface, and the abstracted create/remove method does the whole job.

However, in your PR, you've added remove_native methods... which are nothing more than a proxy to calling native.remove() (or the equivalent). We either need to work out how to abstract a full create_menu_item()/remove_menu_item() pair; or simplify the implementation to call the remove method directly.

Comment thread testbed/tests/app/test_app.py
@Oliver-Leigh

Copy link
Copy Markdown
Contributor

@lntutor It's great that you've made some progress on the issue. There is also the issue of deleting the menu items when the MainWindow is removed. Here's an example to highlight the problem:

import gc
import toga

class MinimalApp(toga.App):
    def __init__(self):
        super().__init__(
            formal_name="MinimalApp", app_id="com.example.minimal.app"
        )

    def startup(self):
        def on_running(app=self):
            all_commands = [cmd for cmd in app.commands._commands.values()]
            cmd = all_commands[0]

            for i in range(10):
                temp_window = toga.MainWindow(title="Temp Window")
                temp_window.show()
                temp_window.close()
                gc.collect()

                print(f"The number of Toga windows is {len(self.windows)}.")
                print(f"Native instances of `cmd`: {len(cmd._impl.native)}")
        
        self.on_running = on_running

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.show()

def main():
    return MinimalApp()

if __name__ == "__main__":
    app = main()
    app.main_loop()

@lntutor

lntutor commented Jul 27, 2026

Copy link
Copy Markdown
Author

Follow-up to the review: the commits after 2af2346 now take the direct-removal option. Qt and WinForms filter each window-owned item out of the associated command native list during window cleanup; no remove_native proxy interface was retained. The accompanying test selects only the items owned by the command being checked.

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.

Native menu items on WinForms are never deleted

3 participants