Inhaltsverzeichnis
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 Contributions-Übersicht oder zur 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 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 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 oder zurück zur Contributions-Übersicht.