====== Menü-Merge ====== Menüs sind der sichtbarste Ort, an dem Add-ins ihre Funktionalität in die Shell einbringen. Der Host besitzt die Menüleiste und steuert deren Aufbau. Add-ins deklarieren Menüpunkte an definierten Stellen, und der Host fügt sie in die bestehende Struktur ein. Dieses Zusammenführen — das Merging — folgt einem festen Regelwerk, das sicherstellt, dass die Menüstruktur konsistent bleibt, egal wie viele Add-ins installiert sind. Zurück zur [[start|Contributions-Übersicht]] oder zur [[..:start|Hauptübersicht]]. ===== Orte (Locations) ===== Jeder Menüpunkt wird an einem bestimmten Ort in der Shell platziert. Der Ort wird als Schlüssel im ''menus''-Objekt des [[..:manifest|Manifests]] angegeben. ==== menuBar ==== Der Ort ''menuBar'' platziert Einträge in die Hauptmenüleiste. Hier werden typischerweise Top-Level-Menüs über Submenü-Referenzen erzeugt: "submenus": [ { "id": "assets", "label": "Assets", "icon": "media/assets.svg" } ], "menus": { "menuBar": [ { "submenu": "assets", "group": "modules", "order": 200 } ] } Der Host erzeugt ein neues Top-Level-Menü mit dem Label "Assets" an der durch ''group'' und ''order'' bestimmten Position. Das ''icon'' erscheint neben dem Menünamen, sofern der aktive [[..:skin-contract|Skin]] Icons in der Menüleiste unterstützt. ==== Submenu-Inhalt ==== Der Inhalt eines Submenüs wird über dessen Id als Location-Schlüssel befüllt: "menus": { "assets": [ { "command": "assets.open", "group": "navigation", "order": 1 }, { "command": "assets.list", "group": "navigation", "order": 2 }, { "command": "assets.settings", "group": "configuration", "order": 1 } ] } In diesem Beispiel hat das Assets-Menü zwei Gruppen: ''navigation'' und ''configuration''. Der Host sortiert die Gruppen alphabetisch und fügt zwischen ihnen einen Trennstrich ein. Innerhalb jeder Gruppe bestimmt ''order'' die Reihenfolge. ==== commandPalette ==== Der Ort ''commandPalette'' steuert, ob ein Command in der CommandPalette erscheint: "menus": { "commandPalette": [ { "command": "assets.open" }, { "command": "assets.internal.refresh", "when": "false" } ] } Ein Eintrag mit ''when'': ''"false"'' blendet den Command aus der Palette aus. Commands, die nicht in ''commandPalette'' aufgeführt sind, erscheinen standardmäßig. ==== view/title ==== Der Ort ''view/title'' platziert Aktionsbuttons in die Titelleiste einer Sidebar-View: "menus": { "view/title": [ { "command": "assets.refresh", "when": "view == assets.navigator", "group": "navigation" } ] } Der ''when''-Ausdruck stellt sicher, dass der Button nur in der Titelleiste der passenden View erscheint. Ohne diesen Ausdruck würde der Button in jeder View-Titelleiste auftauchen. ==== view/item/context ==== Der Ort ''view/item/context'' definiert Kontextmenüeinträge für Elemente in einer TreeView: "menus": { "view/item/context": [ { "command": "assets.edit", "when": "view == assets.navigator && viewItem == asset", "group": "inline" }, { "command": "assets.delete", "when": "view == assets.navigator && viewItem == asset", "group": "modification" } ] } Der ''viewItem''-Kontextwert wird vom TreeDataProvider des Add-ins gesetzt und erlaubt unterschiedliche Kontextmenüs für verschiedene Elementtypen. ===== Das Merge-Verfahren ===== Der Host führt das Menü-Merge in zwei Durchgängen durch. ==== Durchgang 1: Top-Level-Menüs erzeugen ==== Der Host sammelt alle ''menuBar''-Einträge mit ''submenu''-Referenz aus allen installierten Add-ins. Für jede referenzierte Submenu-Id sucht er die passende Submenu-Definition (mit ''label'' und ''icon'') und erzeugt einen Top-Level-Menüpunkt. Die Reihenfolge der Top-Level-Menüs ergibt sich aus ''group'' und ''order''. Alle Add-ins, die in dieselbe Gruppe einfügen, werden nebeneinander sortiert. Dadurch kann ein Add-in sein Menü gezielt neben einem bestimmten Host-Menü platzieren. ==== Durchgang 2: Inhalte einfügen ==== Der Host sammelt dann alle Einträge für jede Location (z.B. alle Einträge für Location ''assets'') und sortiert sie: - Sortierung nach ''group'' (alphabetisch) - Innerhalb der Gruppe nach ''order'' (numerisch aufsteigend) - Trennstrich zwischen verschiedenen Gruppen Das bedeutet: Wenn zwei Add-ins Einträge in dasselbe Menü und in dieselbe Gruppe einfügen, stehen sie nebeneinander, sortiert nach ''order''. Wenn sie verschiedene Gruppen verwenden, werden sie durch einen Trennstrich getrennt. ===== Beispiel: Mehrere Add-ins mergen ===== Angenommen, drei Add-ins fügen Einträge in das Tools-Menü ein: **Add-in A (Berichte):** "menus": { "tools": [ { "command": "reports.monthly", "group": "reports", "order": 1 }, { "command": "reports.yearly", "group": "reports", "order": 2 } ] } **Add-in B (Export):** "menus": { "tools": [ { "command": "export.csv", "group": "export", "order": 1 }, { "command": "export.pdf", "group": "export", "order": 2 } ] } **Add-in C (Diagnose):** "menus": { "tools": [ { "command": "diag.log", "group": "diagnostics", "order": 1 } ] } Das resultierende Tools-Menü sieht so aus: Tools ├── Diagnose-Log (diagnostics / 1) ├── ───────────────────── (Trennstrich) ├── CSV exportieren (export / 1) ├── PDF exportieren (export / 2) ├── ───────────────────── (Trennstrich) ├── Monatsbericht (reports / 1) └── Jahresbericht (reports / 2) Die Gruppen werden alphabetisch sortiert (diagnostics < export < reports), die Einträge innerhalb jeder Gruppe nach ''order''. ===== When-Ausdrücke ===== Jeder Menüeintrag kann einen ''when''-Ausdruck enthalten, der bestimmt, unter welchen Bedingungen der Eintrag sichtbar oder aktiviert ist. { "command": "assets.save", "when": "activeDocument == assets.editor && isDirty" } Unterstützte Operatoren: | **Ausdruck** | **Bedeutung** | | ''key'' | Wahr, wenn der Kontextwert gesetzt und nicht leer ist | | ''!key'' | Wahr, wenn der Kontextwert nicht gesetzt oder leer ist | | ''key == value'' | Wahr, wenn der Kontextwert exakt ''value'' entspricht | | ''key != value'' | Wahr, wenn der Kontextwert nicht ''value'' entspricht | | ''a && b'' | Wahr, wenn beide Ausdrücke wahr sind | | ''a || b'' | Wahr, wenn mindestens ein Ausdruck wahr ist | Der Host wertet ''when''-Ausdrücke dynamisch aus. Ändert sich ein Kontextwert (weil der Benutzer den Tab wechselt, ein Dokument schließt oder ein Add-in einen Wert setzt), aktualisiert der Host die Sichtbarkeit aller betroffenen Menüpunkte sofort. ===== Dynamische Registrierung ===== Für Szenarien, in denen Menüpunkte von Laufzeitdaten abhängen, bietet ''IMenuService'' die programmatische Registrierung: AContext.Subscribe( FHost.Menus.AddMenuItem('menuBar/tools', 'assets.custom-report', 'hasReportData') ); Dynamisch registrierte Einträge werden genauso gemergt wie statische. Der Host kennzeichnet sie intern als nicht-statisch, behandelt sie in der Oberfläche aber identisch. Weiter zu [[toolbar|Toolbar]] oder zurück zur [[start|Contributions-Übersicht]].