Docks, Tabs, Statusleiste & Theme¶
Die UI-Flächen, zu denen ein Plugin über Menüs hinaus beitragen kann. Alle folgen demselben Muster: Beschreiben Sie Ihr Anliegen in Backend-neutralen Daten, übergeben Sie dem Host eine Factory und behalten Sie das zurückgegebene RAII-Token.
Dock-Panels¶
ctx.docks() fügt ein andockbares Seitenpanel hinzu. Die öffentliche
Oberfläche übergibt das Widget bewusst als opakes void* — der Qt-gestützte
Host castet es zu QWidget*; ein Python-Plugin baut dasselbe Panel
stattdessen deklarativ (siehe
die Python-Referenz zu ctx.docks).
class IDockPanel {
public:
void* nativeWidget(); // your QWidget*, host takes ownership
std::string title() const;
};
class IDockPanelFactory {
public:
std::unique_ptr<IDockPanel> create();
};
struct DockPanelDescriptor {
std::string id; // "myplugin.outline"
std::string defaultTitle;
DockArea preferredArea = DockArea::Right; // Left/Right/Top/Bottom
bool initiallyVisible = true; // false = created hidden
};
m_dock = ctx.docks().addPanel(descriptor, std::move(factory));
Der Host instanziiert das Panel lazy (bei der ersten Anzeige) und kann es
nach einem Layout-Reset erneut instanziieren — halten Sie den Zustand pro
Panel außerhalb des Widgets oder bauen Sie ihn in create() neu auf. Das
DockToken entfernt das Panel bei seiner Zerstörung.
Vollwertige Tab-Typen¶
Wenn ein Feature eine eigene Art von Tab verdient (Diff-Ergebnisse, eine
Log-Ansicht, ein Hex-Viewer) statt eines Textpuffers oder eines Seitenpanels,
registrieren Sie einen Tab-Typ mit ctx.documents():
struct DocumentTabTypeDescriptor {
std::string typeId; // "log-view" -- lowercase-with-hyphens
std::string displayName; // localized type name
DefaultPlacement defaultPlacement; // ActiveGroup / NewGroupRight / NewGroupBottom
};
m_type = ctx.documents().registerType(descriptor, std::move(factory));
ctx.documents().openTab("log-view", {/* params */});
Der Host platziert Ihren IDocumentTab in der Tab-Leiste neben den
Editor-Tabs und leitet Speichern/Schließen an ihn weiter. Tabs, die ein
nicht-leeres sessionParams() zurückgeben, werden von der
Sitzungspersistenz erfasst und beim Wiederherstellen über openTab()
wieder abgespielt; fehlt das besitzende Plugin zum Zeitpunkt der
Wiederherstellung, fasst der Host die verwaisten Einträge in einer einzigen
Meldung zusammen, statt sie zu verwerfen. First-Party-Beispiele: Diff
Compare und der Log Analyzer.
Die Vergleichsansicht¶
ctx.compare() öffnet das zweigeteilte Vergleichsfenster des Editors für ein
beliebiges Quellenpaar — Dateipfade, offene Dokumente oder Text im Speicher:
CompareSource left{...}, right{...};
bool shown = ctx.compare().openCompare(left, right, CompareOptions{});
Der Aufruf ist immer sicher: Ein Host ohne Vergleichsansicht liefert ein
No-op, das false zurückgibt.
Statusleiste¶
Zwei unabhängige Kanäle — flüchtige Benachrichtigungen und persistente, Plugin-eigene Segmente:
// Transient: replaces/queues by severity; do NOT use for live state.
ctx.statusBar().showMessage("Wrapped", 2000, StatusLevel::Info);
// Persistent segment: an always-on indicator you own and refresh.
m_segment = ctx.statusBar().addSegment("wordcount.total");
m_segment->setText("2,431 words");
m_segment->setVisible(false); // hide without destroying
// dropping the unique_ptr removes the segment
Eine Benachrichtigung höherer Dringlichkeit ersetzt die angezeigte sofort;
eine mit niedrigerer Dringlichkeit kann kurz zurückgehalten werden, damit ein
Error nicht sofort von routinemäßigem Info überschrieben wird. Das
Aktualisieren eines Segments überschreibt niemals eine Benachrichtigung und
umgekehrt.
Editor-Kontextmenü¶
Anders als IMenuRegistry — statische Menüleisten-Einträge, die an Commands
gebunden sind — sind Kontextmenü-Einträge dynamisch: Bei jedem
Rechtsklick in den Textbereich fragt der Host jeden registrierten Provider,
was an der angeklickten Position zutrifft, und hängt die Antworten unter die
eingebauten Bearbeitungsaktionen (Undo/Redo, Ausschneiden/Kopieren/Einfügen,
Alles auswählen):
class MyProvider final : public plugin::IContextMenuProvider
{
std::vector<plugin::ContextMenuItem>
contextMenuItems(const plugin::ContextMenuRequest& req) override
{
// req.doc + req.position (byte offset under the click; the caret
// for keyboard-triggered menus). Empty vector = decline.
if (!appliesAt(req.doc, req.position)) { return {}; }
return {
{ "Do the thing", [this, req] { doTheThing(req); } },
{ "", {} }, // empty title = separator
{ "Another action", [this] { another(); } },
};
}
};
m_token = ctx.contextMenu().registerProvider(m_provider); // RAII token
Provider laufen auf dem UI-Thread, während der Benutzer auf das Menü wartet — antworten Sie aus dem Zustand im Speicher, blockieren Sie niemals. Die Eintragstitel sind dynamische Strings, die das Plugin selbst übersetzt. Die Rechtschreibprüfung nutzt dies für ihre Vorschläge / Add to Dictionary / Ignore for This Session-Einträge.
Theme-Palette¶
Plugins, die eigene Farben zeichnen, registrieren benannte Kategorien mit Hell/Dunkel-Standardwerten und lösen sie zur Zeichenzeit zu RGB auf — Farben niemals fest codieren:
// ThemeCategory: {id, displayLabel, defaultLight, defaultDark}.
// The id is namespaced "<plugin.id>/<role>" by convention.
m_cat = ctx.themePalette().registerCategory(
{"com.example.myplugin/match", "Match highlight",
/*light*/ 0xFFF3B0, /*dark*/ 0x8A6D00});
std::uint32_t rgb = ctx.themePalette().resolveColor("com.example.myplugin/match");
bool themed = ctx.themePalette().hasOverride("com.example.myplugin/match");
std::string variant = ctx.themePalette().activeVariant(); // "light" / "dark" / ""
resolveColor liefert die Überschreibung des Themes, falls vorhanden, sonst
den registrierten Standard der aktiven Variante, und 0x000000 für eine
nicht registrierte Id (behandeln Sie das als „eigenen Fallback verwenden“).
Jede registrierte Kategorie erscheint automatisch im Theme-Editor, und
Benutzer-Überschreibungen werden in der Theme-Datei unter pluginColors
verlustfrei gespeichert. Zeichnen Sie bei events::ThemeChanged neu.