Ein natives Plugin schreiben¶
Ein minimales natives Plugin besteht aus einer Klasse, einem Factory-Makro,
einer JSON-Datei und einer kurzen CMakeLists.txt. Sie können es vollständig
außerhalb des Quellbaums gegen das Plugin-SDK entwickeln — ein Checkout der
MTE-Quellen ist nicht erforderlich.
Nativ oder Python?
Diese Seite behandelt native C++-Plugins (kompiliert zu einer
.dll/.so/.dylib und vom Qt-Loader geladen). Wenn Sie lieber einen
Ordner mit .py-Dateien ausliefern, den der Editor als Subprozess startet,
lesen Sie Python-Plugins — dieselbe
PluginApi-Oberfläche, anderes Backend, kein Compiler nötig.
Entwurf
Diese Anleitung basiert auf PLUGIN_ARCHITECTURE.md; prüfen Sie den genauen
SDK-Paketnamen und die Argumente von mte_add_plugin() gegen das
ausgelieferte SDK, bevor Sie sich darauf verlassen.
1. IPlugin implementieren¶
#include <PluginApi/IPlugin.h>
#include <PluginApi/IPluginContext.h>
class HelloWorldPlugin : public MTE::plugin::IPlugin {
public:
MTE::plugin::PluginInfo info() const override {
return { "org.mte.helloworld", "Hello World", "1.0.0",
"Example Vendor", "Says hi.", MTE_PLUGIN_API_VERSION };
}
bool initialize(MTE::plugin::IPluginContext& ctx) override {
m_cmd = ctx.commands().registerCommand(
{ "helloworld.sayHi", "Say Hi", "Ctrl+Alt+H", "Hello" },
[&ctx] { ctx.log().log(MTE::plugin::LogLevel::Info, "Hi!"); });
m_menu = ctx.menus().addItem("Plugins/Say Hi", "helloworld.sayHi");
return true;
}
void shutdown() noexcept override {} // tokens tear down automatically
private:
MTE::plugin::CommandToken m_cmd;
MTE::plugin::MenuToken m_menu;
};
2. Die Factory exportieren (natives Qt-Backend)¶
#include <PluginApi/Qt/PluginQtGlue.h>
class HelloWorldFactory : public QObject, public MTE::plugin::IPluginFactory {
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.mte.IPluginFactory/1" FILE "plugin.json")
Q_INTERFACES(MTE::plugin::IPluginFactory)
public:
std::unique_ptr<MTE::plugin::IPlugin> create() override {
return std::make_unique<HelloWorldPlugin>();
}
};
3. In plugin.json beschreiben¶
{
"id": "org.mte.helloworld",
"name": "Hello World",
"version": "1.0.0",
"apiVersion": 1,
"entry": "native",
"requires": [],
"order": 600,
"menus": [
{ "title": "Plugins", "barPriority": 570, "itemPriority": 100 }
]
}
Die apiVersion hier und die von info() zurückgegebene müssen beide
gleich MTE_PLUGIN_API_VERSION sein, andernfalls verweigert der Host das Laden
des Plugins.
Dazu gehören zwei optionale Schlüssel:
apiMinorVersion(Standard0) — die niedrigste Minor-Version der Host-API, die Ihr Plugin braucht. Ein Host, dessenMTE_PLUGIN_API_MINOR_VERSIONniedriger ist, führt das Plugin mit „Erfordert eine neuere Version von OctoEdit“ und lädt es nicht. Der Host prüft diesen Manifestwert, bevor er irgendetwas lädt, und danach noch einmalPluginInfo::apiMinorVersionausinfo()— dieser Wert ist standardmäßig die Minor-Version der Header, gegen die Sie kompiliert haben; lassen Sie ihn also in Ruhe, es sei denn, Sie wissen, dass Sie nichts Neueres verwenden. Siehe Versionierung.requires(Standard[]) — die IDs der Plugins, ohne die Ihr Plugin nicht funktioniert, zum Beispiel["org.mte.lsp"]. Nur IDs: Es gibt keinen Versionsbereich und keine optionale Form — ein Plugin, mit dem Ihres lediglich besser funktioniert, gehört nicht hierher; sprechen Sie zur Laufzeit über Events und Provider-Registries mit ihm. Der Editor initialisiert Ihre Abhängigkeiten vor Ihrem Plugin, egal wasordersagt, und fährt sie nach ihm herunter. Fehlt eine davon, ist sie deaktiviert, braucht sie eine Lizenz, die nicht aktiv ist, oder wurde sie nicht geladen, dann wird Ihr Plugin überhaupt nicht geladen, und Einstellungen ▸ Erweiterungen zeigt, welches Plugin es braucht und warum („Benötigt das Plug-in LSP Provider, das deaktiviert ist“). Ein Zyklus blockiert jedes Plugin darin. Derselbe Schlüssel bedeutet dasselbe in derplugin.jsoneines Python-Plugins.
Mitgelieferte Bibliotheken attributieren — thirdParty¶
Wenn Ihr Plugin eine Drittanbieter-Komponente bündelt oder dagegen linkt,
deklarieren Sie sie in einem optionalen thirdParty-Array, damit der Editor
seine Attributionspflichten erfüllen kann:
"thirdParty": [
{ "name": "Qt WebEngine", "license": "LGPL-3.0",
"url": "https://doc.qt.io/qt-6/qtwebengine-index.html",
"file": "QtWebEngine-LGPLv3.txt" }
]
Jeder Eintrag benötigt einen name (Einträge ohne Namen werden verworfen);
license, url und file sind optional. Ihr Plugin ist Eigentümer der
Lizenztexte — liefern Sie sie neben dem Plugin unter
Plugins/Licenses/<your-plugin-id>/ aus, wobei file der Basisname ist. Der
Über-Dialog listet die thirdParty-Komponenten jedes installierten
Plugins auf und verweist die Benutzer auf diesen Ordner; der Kern trägt die
Drittanbieter-Texte eines Plugins niemals selbst. (Das mitgelieferte
HtmlPreviewPlugin, das QtWebEngine + Chromium einbettet, ist das
Referenzbeispiel.)
4. Bauen¶
Verwenden Sie den CMake-Helfer des SDK:
find_package(MTEPluginSDK REQUIRED)
mte_add_plugin(NAME HelloWorldPlugin
SOURCES Src/HelloWorldPlugin.cpp
# TRANSLATIONS Translations/HelloWorld_en.ts ...
)
Build-Voraussetzungen¶
Ein natives Plugin wird in den Prozess des Editors geladen und muss deshalb mit einer Toolchain gebaut werden, die binärkompatibel zu dem Editor-Build ist, für den es bestimmt ist. Die Anforderungen für die aktuelle Version (gebaut mit Qt 6.11.2):
| Windows | macOS | |
|---|---|---|
| Compiler | Visual Studio 2022, MSVC-Toolset 14.44 oder ein älteres 14.x (VS 2015–2022 teilen sich eine C++-ABI); clang-cl funktioniert ebenfalls. MinGW-/GCC-Builds lassen sich nicht laden. |
Apple clang aus Xcode (Xcode 27, Apple clang 21.0.0); nur arm64 |
| Build-Konfiguration | Release, dynamische Laufzeitbibliothek (/MD, CMake-Standard). Ein Debug-Plugin weist der Loader ab; ein Plugin mit statischer Laufzeitbibliothek (/MT) stürzt ab, sobald Speicher die Plugin-Grenze überschreitet |
Release; Deployment-Target 14.4 (wie der Editor) |
| Qt | Qt 6.11, Kit msvc2022_64. Ein gegen ein älteres 6.x gebautes Plugin lädt; eines gegen eine neuere Minor-Version möglicherweise nicht |
Qt 6.11, gleiche Architektur |
| C++-Standard | C++20 (die API-Header verwenden std::span und Concepts) |
C++20 |
| CMake | 3.16+ | 3.16+ |
| Plugin-API | die MTE_PLUGIN_API_VERSION des SDK, das mit der Ziel-Editorversion ausgeliefert wurde |
ebenso |
Zwei Folgen unter Windows, die man kennen sollte:
- Das Visual C++ Redistributable auf dem Rechner des Anwenders muss mindestens so neu sein wie das neueste MSVC-Toolset, das der Editor oder ein geladenes Plugin verwendet. Ein Plugin mit einem neueren Toolset als dem des Editors zu bauen erhöht diese Anforderung für Ihre Anwender; das Toolset des Editors oder ein älteres tut das nicht.
- Die Version des Windows SDK, gegen das Sie kompilieren, beeinflusst die Kompatibilität mit dem Editor nicht; sie bestimmt nur, welche Windows-APIs Ihr Plugin aufrufen kann — der Editor unterstützt Windows 10 1809 und neuer.
Und unter macOS:
- Der Editor wird nur als arm64 ausgeliefert; ein reines
x86_64-Plugin lässt sich nicht laden (ein Universal-Binary schon). Belassen Sie das Deployment-Target bei 14.4: ein Plugin, das für ein neueres Mindest-macOS gebaut wurde, weist der System-Loader auf älteren Macs ab, und die libc++ des Systems ist die Laufzeitbibliothek — eine Redistributable-Frage stellt sich nicht. - Referenzieren Sie die Qt-Frameworks über
@rpath, damit das Plugin die inOctoEdit.appgebündelten Kopien verwendet. Qt-Kits, deren Frameworks absolute Install-Namen tragen (etwa Homebrews), brauchen vor der Auslieferunginstall_name_toolauf dem gebauten Plugin; sonst sucht das Plugin Qt unter dem Pfad des Build-Rechners.
Nichts davon gilt für Python-Plugins: Sie laufen in einem eigenen Prozess und teilen keine Binärschnittstelle mit dem Editor.
5. Installieren & ausführen¶
Packen Sie entweder die gebaute Bibliothek + plugin.json als
<name>.mteplugin in ein Zip und installieren Sie es über
Einstellungen ▸ Erweiterungen ▸ Plugin installieren... (das Paket wird validiert — Manifest,
Archivsicherheit und die eingebetteten Metadaten der Binärdatei —, bevor
irgendetwas angefasst wird), oder legen Sie die Dateien von Hand in ein
erkanntes Plugin-Verzeichnis (siehe
Architekturüberblick). Starten Sie den Editor
neu; aktivieren Sie das Plugin bei Bedarf unter Einstellungen ▸ Erweiterungen.
Weiter¶
- Plugin-API-Überblick — Lebenszyklus, Tokens und die vollständige Diensttabelle
- Commands & Menüs
- Docks, Tabs & Statusleiste
- Einstellungsseiten
- Events
- Übersetzungen