Escribir un plugin nativo¶
Un plugin nativo mínimo es una clase, una macro de fábrica, un archivo JSON y
un CMakeLists.txt corto. Puedes crearlo por completo fuera del árbol del
proyecto, contra el SDK de plugins, sin necesidad de descargar el código fuente
de MTE.
¿Nativo o Python?
Esta página cubre los plugins nativos de C++ (compilados a un
.dll/.so/.dylib y cargados por el cargador de Qt). Si prefieres
distribuir una carpeta de archivos .py que el editor lanza como
subproceso, consulta Plugins de Python: misma
superficie de PluginApi, backend distinto, sin necesidad de compilador.
Borrador
Este recorrido está basado en PLUGIN_ARCHITECTURE.md; verifica el nombre
exacto del paquete del SDK y los argumentos de mte_add_plugin() contra el
SDK distribuido antes de confiar en ellos.
1. Implementa 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. Exporta la fábrica (backend nativo de Qt)¶
#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. Descríbelo en 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 }
]
}
El apiVersion de aquí y el que devuelve info() deben ser ambos iguales a
MTE_PLUGIN_API_VERSION, o el anfitrión se negará a cargar el plugin.
Lo acompañan dos claves opcionales:
apiMinorVersion(0de forma predeterminada): la versión menor mínima de la API del anfitrión que necesita tu plugin. Un anfitrión cuyaMTE_PLUGIN_API_MINOR_VERSIONsea inferior muestra el plugin con «Necesita una versión más reciente de OctoEdit» y no lo carga. El anfitrión comprueba este valor del manifiesto antes de cargar nada, y después vuelve a comprobarPluginInfo::apiMinorVersiondeinfo(); ese valor es por defecto la versión menor de las cabeceras con las que compilaste, así que no lo toques a menos que sepas que no usas nada más reciente. Consulta Versionado.requires([]de forma predeterminada): los identificadores de los plugins sin los que tu plugin no puede funcionar, por ejemplo["org.mte.lsp"]. Solo identificadores: no hay rango de versiones ni forma opcional; un plugin con el que el tuyo simplemente funciona mejor no va aquí: comunícate con él en tiempo de ejecución mediante eventos y registros de proveedores. El editor inicializa tus dependencias antes que tu plugin, diga lo que digaorder, y las apaga después. Si alguna falta, está desactivada, necesita una licencia que no está activa o no se cargó, tu plugin no se carga en absoluto, y Preferencias ▸ Complementos indica qué plugin necesita y por qué («Necesita el complemento LSP Provider, que está desactivado»). Un ciclo bloquea todos los plugins que forman parte de él. La misma clave significa lo mismo en elplugin.jsonde un plugin Python.
Atribuir las bibliotecas incluidas — thirdParty¶
Si tu plugin incluye o enlaza un componente de terceros, decláralo en una matriz
opcional thirdParty para que el editor pueda cumplir sus obligaciones de
atribución:
"thirdParty": [
{ "name": "Qt WebEngine", "license": "LGPL-3.0",
"url": "https://doc.qt.io/qt-6/qtwebengine-index.html",
"file": "QtWebEngine-LGPLv3.txt" }
]
Cada entrada necesita un name (las entradas sin él se descartan); license,
url y file son opcionales. Las licencias son responsabilidad de tu
plugin: distribúyelas junto a él, bajo
Plugins/Licenses/<your-plugin-id>/, donde file es el nombre base. El diálogo
Acerca de lista los componentes thirdParty de todos los plugins instalados
y remite a los usuarios a esa carpeta; el núcleo nunca transporta por un plugin
los textos de terceros de este. (El HtmlPreviewPlugin incluido, que integra
QtWebEngine + Chromium, es el ejemplo de referencia.)
4. Compílalo¶
Usa el ayudante de CMake del SDK:
find_package(MTEPluginSDK REQUIRED)
mte_add_plugin(NAME HelloWorldPlugin
SOURCES Src/HelloWorldPlugin.cpp
# TRANSLATIONS Translations/HelloWorld_en.ts ...
)
Requisitos de compilación¶
Un plugin nativo se carga dentro del proceso del editor, así que tiene que compilarse con una cadena de herramientas binariamente compatible con la compilación del editor a la que va dirigido. Estos son los requisitos de la versión actual (compilada con Qt 6.11.2):
| Windows | macOS | |
|---|---|---|
| Compilador | Visual Studio 2022, conjunto de herramientas MSVC 14.44 o uno 14.x anterior (VS 2015–2022 comparten una ABI de C++); clang-cl también funciona. Las compilaciones con MinGW / GCC no se pueden cargar. |
Apple clang de Xcode (Xcode 27, Apple clang 21.0.0); solo arm64 |
| Configuración de compilación | Release, tiempo de ejecución dinámico (/MD, lo predeterminado de CMake). El cargador rechaza un plugin en Debug; un plugin con tiempo de ejecución estático (/MT) falla cuando la memoria cruza el límite del plugin |
Release; destino de despliegue 14.4 (el mismo que el editor) |
| Qt | Qt 6.11, kit msvc2022_64. Un plugin compilado contra una versión menor 6.x anterior se carga; uno compilado contra una menor más nueva puede que no |
Qt 6.11, la misma arquitectura |
| Estándar de C++ | C++20 (las cabeceras de la API usan std::span y conceptos) |
C++20 |
| CMake | 3.16+ | 3.16+ |
| Plugin API | el MTE_PLUGIN_API_VERSION del SDK que se distribuyó con la versión del editor a la que apuntas |
igual |
Dos consecuencias que conviene conocer en Windows:
- El Visual C++ Redistributable instalado en la máquina del usuario tiene que ser al menos tan nuevo como el conjunto de herramientas MSVC más nuevo que use el editor o cualquier plugin cargado. Compilar tu plugin con un conjunto de herramientas más nuevo que el del editor eleva ese requisito para tus usuarios; compilar con el del editor o con uno anterior, no.
- La versión del SDK de Windows contra la que compilas no afecta a la compatibilidad con el editor; solo decide a qué API de Windows puede llamar tu plugin, y el editor admite Windows 10 1809 y posteriores.
Y en macOS:
- El editor se distribuye solo para arm64; un plugin solo para
x86_64no se puede cargar (un binario universal sí vale). Mantén el destino de despliegue en 14.4: un plugin compilado para un macOS mínimo más nuevo lo rechaza el cargador del sistema en Mac más antiguos, y el tiempo de ejecución es la propia libc++ del sistema operativo, así que no se plantea la cuestión del redistribuible. - Referencia los frameworks de Qt a través de
@rpathpara que el plugin tome las copias incluidas dentro deOctoEdit.app. Los kits de Qt cuyos frameworks llevan nombres de instalación absolutos (los de Homebrew, por ejemplo) necesitaninstall_name_toolsobre el plugin compilado antes de distribuirlo; si no, el plugin buscará Qt en la ruta de la máquina de compilación.
Nada de esto se aplica a los plugins de Python: se ejecutan en un proceso aparte y no comparten ninguna interfaz binaria con el editor.
5. Instálalo y ejecútalo¶
Puedes comprimir la biblioteca compilada y el plugin.json como
<name>.mteplugin e instalarlo mediante Preferencias ▸ Complementos ▸
Instalar un complemento... (el paquete se valida —manifiesto, seguridad del archivo comprimido y
metadatos incrustados en el binario— antes de tocar nada), o dejar los archivos a
mano en un directorio de plugins detectado (consulta la
visión general de la arquitectura). Reinicia el
editor; actívalo en Preferencias ▸ Complementos si hace falta.
Siguiente¶
- Visión general de la Plugin API — ciclo de vida, testigos y la tabla completa de servicios
- Comandos y menús
- Paneles, pestañas y barra de estado
- Páginas de ajustes
- Eventos
- Traducciones