diff --git a/src/app_model/backends/qt/_qmainwindow.py b/src/app_model/backends/qt/_qmainwindow.py index 77fa637..7e7a38b 100644 --- a/src/app_model/backends/qt/_qmainwindow.py +++ b/src/app_model/backends/qt/_qmainwindow.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Collection, Mapping, Optional, Sequence, Union +from typing import TYPE_CHECKING, Collection, Mapping, Optional, Sequence, Union from qtpy.QtCore import Qt from qtpy.QtWidgets import QMainWindow, QWidget @@ -9,6 +9,9 @@ from ._qmenu import QModelMenuBar, QModelToolBar +if TYPE_CHECKING: + from app_model.registries import MenusRegistry + class QModelMainWindow(QMainWindow): """QMainWindow with app-model support.""" @@ -20,14 +23,17 @@ def __init__( self._app = Application.get_or_create(app) if isinstance(app, str) else app def setModelMenuBar( - self, menu_ids: Mapping[str, str] | Sequence[str | tuple[str, str]] + self, + menu_ids: Mapping[str, str] | Sequence[str | tuple[str, str]] | MenusRegistry, ) -> QModelMenuBar: """Set the menu bar to a list of menu ids. Parameters ---------- - menu_ids : Mapping[str, str] | Sequence[str | tuple[str, str]] - A mapping of menu ids to menu titles or a sequence of menu ids. + menu_ids : Mapping[str, str] | Sequence[str | tuple[str, str]] | MenusRegistry + A mapping of `{menu ids -> menu title}` or a sequence of menu ids. If a + `MenuRegistry` instance is passed, all menus in the registry will be added + to the menu bar. """ menu_bar = QModelMenuBar(menu_ids, self._app, self) self.setMenuBar(menu_bar) diff --git a/src/app_model/backends/qt/_qmenu.py b/src/app_model/backends/qt/_qmenu.py index 98db3f9..6c24945 100644 --- a/src/app_model/backends/qt/_qmenu.py +++ b/src/app_model/backends/qt/_qmenu.py @@ -16,6 +16,7 @@ from qtpy.QtWidgets import QMenu, QMenuBar, QToolBar from app_model import Application +from app_model.registries import MenusRegistry from app_model.types import SubmenuItem from ._qaction import QCommandRuleAction, QMenuItemAction @@ -261,13 +262,24 @@ def _on_registry_changed(self, changed_ids: Set[str]) -> None: self.rebuild() +def _menu_sort(menu_name: str | tuple[str, str]) -> tuple: + if isinstance(menu_name, tuple): + menu_name = menu_name[0] + order = ["file", "edit", "format", "view", None, "window", "help"] + menu_name = menu_name.lower() + idx = order.index(menu_name if menu_name in order else None) + return (idx, menu_name) + + class QModelMenuBar(QMenuBar): """QMenuBar that is built from a list of model menu ids. Parameters ---------- menus : Mapping[str, str] | Sequence[str | tuple[str, str]] - A mapping of menu ids to menu titles or a sequence of menu ids. + A mapping of `{menu ids -> menu title}` or a sequence of menu ids. If a + `MenuRegistry` instance is passed, all menus in the registry will be added + to the menu bar. app : Union[str, Application] Application instance or name of application instance. parent : Optional[QWidget] @@ -276,13 +288,23 @@ class QModelMenuBar(QMenuBar): def __init__( self, - menus: Mapping[str, str] | Sequence[str | tuple[str, str]], + menus: Mapping[str, str] | Sequence[str | tuple[str, str]] | MenusRegistry, app: Union[str, Application], parent: Optional[QWidget] = None, ) -> None: super().__init__(parent) - menu_items = menus.items() if isinstance(menus, Mapping) else menus + menu_items: Iterable[str | tuple[str, str]] + if isinstance(menus, MenusRegistry): + menu_items = [m for m, *_ in menus if m != MenusRegistry.COMMAND_PALETTE_ID] + # sort menus so that they are in this order + # File Edit Format View <...> Window Help + menu_items.sort(key=_menu_sort) + elif isinstance(menus, Mapping): + menu_items = menus.items() + else: + menu_items = menus + for item in menu_items: id_, title = item if isinstance(item, tuple) else (item, item.title()) self.addMenu(QModelMenu(id_, app, title, self))