Writing a native plugin¶
A minimal native plugin is one class, one factory macro, one JSON file, and a
short CMakeLists.txt. You can author it entirely out-of-tree against the
Plugin SDK — no MTE source checkout required.
Native or Python?
This page covers native C++ plugins (compiled to a .dll/.so/.dylib
and loaded by the Qt loader). If you'd rather ship a folder of .py files
the editor spawns as a subprocess, see
Python plugins — same PluginApi surface,
different backend, no compiler required.
Draft
This walkthrough is grounded in PLUGIN_ARCHITECTURE.md; verify the exact
SDK package name and mte_add_plugin() arguments against the shipped SDK
before relying on them.
1. Implement IPlugin¶
#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. Export the factory (native 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. Describe it in plugin.json¶
{
"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 }
]
}
The apiVersion here and the one returned by info() must both equal
MTE_PLUGIN_API_VERSION, or the host refuses to load the plugin.
Attributing bundled libraries — thirdParty¶
If your plugin bundles or links a third-party component, declare it in an
optional thirdParty array so the editor can honour its attribution
obligations:
"thirdParty": [
{ "name": "Qt WebEngine", "license": "LGPL-3.0",
"url": "https://doc.qt.io/qt-6/qtwebengine-index.html",
"file": "QtWebEngine-LGPLv3.txt" }
]
Each entry needs a name (entries without one are dropped); license, url,
and file are optional. Your plugin owns the licence texts — ship them next
to the plugin under Plugins/Licenses/<your-plugin-id>/, where file is the
basename. The About dialog lists every installed plugin's thirdParty
components and points users at that folder; the core never carries a plugin's
third-party texts for it. (The bundled HtmlPreviewPlugin, which embeds
QtWebEngine + Chromium, is the reference example.)
4. Build it¶
Use the SDK's CMake helper:
find_package(MTEPluginSDK REQUIRED)
mte_add_plugin(NAME HelloWorldPlugin
SOURCES Src/HelloWorldPlugin.cpp
# TRANSLATIONS Translations/HelloWorld_en.ts ...
)
5. Install & run¶
Either zip the built library + plugin.json as <name>.mteplugin and
install it via Preferences ▸ Plugins ▸ Install… (the package is
validated — manifest, archive safety, and the binary's embedded metadata —
before anything is touched), or drop the files into a discovered plugin
directory by hand (see
Architecture overview). Restart the editor;
enable it in Preferences ▸ Plugins if needed.
Next¶
- Plugin API overview — lifecycle, tokens, and the full service table
- Commands & menus
- Docks, tabs & status bar
- Settings pages
- Events
- Translations