Dieses Tutorial zeigt, wie ein Add-in ein Panel in der Seitenleiste der Shell bereitstellt. Sidebar-Views eignen sich für navigierende Inhalte: Baumstrukturen, Listen, Filteransichten. Der Host bettet den Frame in die Seitenleiste ein und steuert das Accordion-Verhalten.
Zurück zur Übersicht.
Ein Add-in namens „Asset Navigator“, das:
{ "id": "demo.asset-navigator", "name": "asset-navigator", "displayName": "Asset Navigator", "version": "1.0.0", "publisher": "demo", "kind": "native", "main": "bin/AssetNavigator.dll", "engineVersion": "^1.0.0", "activationEvents": ["onStartup"], "contributes": { "commands": [ { "command": "assets.nav.refresh", "title": "Aktualisieren", "category": "Assets", "icon": "media/refresh.svg" }, { "command": "assets.nav.add", "title": "Anlage hinzufügen", "category": "Assets", "icon": "media/add.svg" }, { "command": "assets.nav.edit", "title": "Bearbeiten", "category": "Assets" }, { "command": "assets.nav.delete", "title": "Löschen", "category": "Assets" } ], "views": { "sidebar": [ { "id": "assets.navigator", "name": "Anlagen", "icon": "media/navigator.svg", "containerId": "assets-explorer" }, { "id": "assets.categories", "name": "Kategorien", "icon": "media/categories.svg", "containerId": "assets-explorer" } ] }, "menus": { "view/title": [ { "command": "assets.nav.refresh", "when": "view == assets.navigator", "group": "navigation" }, { "command": "assets.nav.add", "when": "view == assets.navigator", "group": "navigation" } ], "view/item/context": [ { "command": "assets.nav.edit", "when": "view == assets.navigator && viewItem == asset", "group": "modification", "order": 1 }, { "command": "assets.nav.delete", "when": "view == assets.navigator && viewItem == asset", "group": "modification", "order": 2 } ] }, "viewsWelcome": [ { "viewId": "assets.navigator", "content": "Noch keine Anlagen vorhanden.\n[Erste Anlage erstellen](command:assets.nav.add)", "when": "!hasAssets", "group": "main", "order": 1 } ] } }
Einige Dinge fallen hier auf:
assets.navigator und assets.categories) haben dieselbe containerId (assets-explorer). Der Host gruppiert sie zu einem Accordion in der Seitenleiste.view/title mit when: view == assets.navigator erscheinen als Buttons in der Titelleiste des Navigator-Panels.view/item/context mit viewItem == asset erscheinen nur beim Rechtsklick auf einen Baumknoten vom Typ „asset“.!hasAssets), zeigt das Panel einen Begrüßungstext mit einem Link, der den Hinzufügen-Command auslöst.onStartup der übliche Weg.unit AssetNavigatorFrame; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Controls, ComCtrls, WvdS.Document.Host.Api; type TAssetNavigatorFrame = class(TWvdSDocumentFrame) private FHost: IHost; FTree: TTreeView; procedure HandleTreeSelectionChanged(Sender: TObject); public constructor Create(AOwner: TComponent); override; procedure Initialize(const AHost: IHost); procedure RefreshData; end; implementation constructor TAssetNavigatorFrame.Create(AOwner: TComponent); begin inherited; FTree := TTreeView.Create(Self); FTree.Parent := Self; FTree.Align := alClient; FTree.OnSelectionChanged := @HandleTreeSelectionChanged; end; procedure TAssetNavigatorFrame.Initialize(const AHost: IHost); begin FHost := AHost; RefreshData; end; procedure TAssetNavigatorFrame.RefreshData; var RootNode, ChildNode: TTreeNode; begin FTree.Items.Clear; RootNode := FTree.Items.Add(nil, 'Fahrzeuge'); FTree.Items.AddChild(RootNode, 'PKW-001'); FTree.Items.AddChild(RootNode, 'LKW-002'); RootNode := FTree.Items.Add(nil, 'Gebäude'); FTree.Items.AddChild(RootNode, 'Hauptgebäude'); FTree.Items.AddChild(RootNode, 'Lager West'); if FTree.Items.Count > 0 then FHost.Context.SetContext('hasAssets', 'true') else FHost.Context.SetContext('hasAssets', ''); end; procedure TAssetNavigatorFrame.HandleTreeSelectionChanged(Sender: TObject); begin if Assigned(FTree.Selected) and (FTree.Selected.Level > 0) then // Blattknoten = eine Anlage → Kontextmenü "asset" aktivieren FHost.Context.SetContext('viewItem', 'asset') else // Gruppenknoten → kein Kontextmenü FHost.Context.SetContext('viewItem', ''); end; end.
Der Frame setzt Context Keys, die das Kontextmenü steuern. Wenn ein Blattknoten ausgewählt ist, wird viewItem auf asset gesetzt, und die Menüeinträge mit viewItem == asset werden sichtbar. Bei Gruppenknoten verschwindet das Kontextmenü.
unit AssetNavigatorPlugin; {$mode objfpc}{$H+} interface uses WvdS.Document.Host.Api; type TAssetNavigatorPlugin = class(TInterfacedObject, IPlugin) private FHost: IHost; FNavigatorFrame: TObject; // Referenz für Commands public constructor Create(const AHost: IHost); procedure Activate(const AContext: IExtensionContext); procedure Deactivate; end; implementation uses AssetNavigatorFrame; type TRefreshHandler = class(TInterfacedObject, ICommandHandler) private FFrame: TAssetNavigatorFrame; public constructor Create(AFrame: TAssetNavigatorFrame); procedure Execute; end; { TAssetNavigatorPlugin } constructor TAssetNavigatorPlugin.Create(const AHost: IHost); begin inherited Create; FHost := AHost; end; procedure TAssetNavigatorPlugin.Activate(const AContext: IExtensionContext); begin // Sidebar-Panel registrieren AContext.Subscribe( FHost.SideBar.RegisterPanel('assets.navigator', 'Anlagen', 'media/navigator.svg', TAssetNavigatorFrame) ); // Command-Handler registrieren AContext.Subscribe( FHost.Commands.RegisterCommand('assets.nav.refresh', 'Aktualisieren', TRefreshHandler.Create(nil)) // Frame-Referenz wird später gesetzt ); AContext.Subscribe( FHost.Commands.RegisterCommand('assets.nav.add', 'Anlage hinzufügen', TAddHandler.Create(FHost)) ); AContext.Subscribe( FHost.Commands.RegisterCommand('assets.nav.edit', 'Bearbeiten', TEditHandler.Create(FHost)) ); AContext.Subscribe( FHost.Commands.RegisterCommand('assets.nav.delete', 'Löschen', TDeleteHandler.Create(FHost)) ); end; procedure TAssetNavigatorPlugin.Deactivate; begin end; { TRefreshHandler } constructor TRefreshHandler.Create(AFrame: TAssetNavigatorFrame); begin inherited Create; FFrame := AFrame; end; procedure TRefreshHandler.Execute; begin if Assigned(FFrame) then FFrame.RefreshData; end; end.
onStartup wird die DLL sofort geladen und Activate aufgerufen.viewItem, und das Kontextmenü wird kontextabhängig aufgebaut.viewItem == asset zeigt der Host „Bearbeiten“ und „Löschen“.Weiter zum Menü/Toolbar-Merge Tutorial oder zurück zur Übersicht.