Ga naar inhoud

Een inheemse plug-in schrijven

Een minimale inheemse plug-in is één klasse, één fabrieksmacro, één JSON-bestand en een korte CMakeLists.txt. U kunt haar volledig buiten de broncodeboom schrijven, tegen de plug-in-SDK aan — er is geen uitcheck van de MTE-broncode voor nodig.

Inheems of Python?

Deze pagina behandelt inheemse C++-plug-ins (gecompileerd tot een .dll/.so/.dylib en door de Qt-lader geladen). Levert u liever een map met .py-bestanden die de editor als subproces start, zie dan Python-plug-ins — hetzelfde vlak van PluginApi, een andere backend, geen compiler nodig.

Concept

Deze doorloop is gebaseerd op PLUGIN_ARCHITECTURE.md; controleer de precieze pakketnaam van de SDK en de argumenten van mte_add_plugin() tegen de geleverde SDK voordat u erop vertrouwt.

1. Implementeer 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. Voer de fabriek uit (inheemse 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. Beschrijf haar 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 }
  ]
}

De apiVersion hier en die welke info() oplevert moeten beide gelijk zijn aan MTE_PLUGIN_API_VERSION, anders weigert de host de plug-in te laden.

Er horen twee optionele sleutels bij:

  • apiMinorVersion (standaard 0) — de laagste minorversie van de host-API die je plug-in nodig heeft. Een host waarvan MTE_PLUGIN_API_MINOR_VERSION lager is, toont de plug-in met «Vereist een nieuwere versie van OctoEdit» en laadt hem niet. De host controleert deze manifestwaarde voordat hij iets laadt, en daarna nogmaals PluginInfo::apiMinorVersion uit info() — die is standaard de minor van de headers waartegen je hebt gecompileerd, dus laat hem met rust tenzij je weet dat je niets nieuwers gebruikt. Zie Versiebeheer.
  • requires (standaard []) — de id's van plug-ins zonder welke je plug-in niet kan werken, bijvoorbeeld ["org.mte.lsp"]. Alleen id's: er is geen versiebereik en geen optionele vorm — een plug-in waarmee de jouwe alleen beter werkt, hoort hier niet; praat er tijdens de uitvoering mee via gebeurtenissen en leveranciersregisters. De editor initialiseert je afhankelijkheden vóór jou, wat order ook zegt, en sluit ze na jou af. Ontbreekt er een, is die uitgeschakeld, heeft die een licentie nodig die niet actief is, of is die niet geladen, dan wordt je plug-in helemaal niet geladen, en zegt Voorkeuren ▸ Plug-ins welke plug-in hij nodig heeft en waarom («Vereist de plug-in LSP Provider, die is uitgeschakeld»). Een cyclus blokkeert elke plug-in die erin zit. Dezelfde sleutel betekent hetzelfde in de plugin.json van een Python-plug-in.

Meegeleverde bibliotheken vermelden — thirdParty

Levert uw plug-in een onderdeel van derden mee of koppelt zij daaraan, verklaar het dan in een optionele array thirdParty, zodat de editor de vermeldingsverplichtingen ervan kan nakomen:

"thirdParty": [
  { "name": "Qt WebEngine", "license": "LGPL-3.0",
    "url": "https://doc.qt.io/qt-6/qtwebengine-index.html",
    "file": "QtWebEngine-LGPLv3.txt" }
]

Elk item heeft een name nodig (items zonder naam vallen af); license, url en file zijn optioneel. De licentieteksten zijn van uw plug-in: lever ze naast de plug-in mee onder Plugins/Licenses/<your-plugin-id>/, waar file de kale bestandsnaam is. Het venster Over somt de thirdParty-onderdelen van elke geïnstalleerde plug-in op en wijst gebruikers naar die map; de kern draagt de teksten van derden van een plug-in nooit voor haar. (De meegeleverde HtmlPreviewPlugin, die QtWebEngine + Chromium inbedt, is het referentievoorbeeld.)

4. Compileer haar

Gebruik het CMake-hulpmiddel van de SDK:

find_package(MTEPluginSDK REQUIRED)
mte_add_plugin(NAME HelloWorldPlugin
    SOURCES Src/HelloWorldPlugin.cpp
    # TRANSLATIONS Translations/HelloWorld_en.ts ...
)

Bouwvereisten

Een inheemse plug-in wordt in het proces van de editor geladen, dus zij moet gebouwd worden met een toolchain die binair compatibel is met de editorbuild waarvoor zij bedoeld is. Dit zijn de vereisten voor de huidige uitgave (gebouwd met Qt 6.11.2):

Windows macOS
Compiler Visual Studio 2022, MSVC-toolset 14.44 of een ouder 14.x (VS 2015–2022 delen één C++-ABI); clang-cl werkt ook. Builds met MinGW / GCC kunnen niet geladen worden. Apple clang uit Xcode (Xcode 27, Apple clang 21.0.0); alleen arm64
Bouwconfiguratie Release, dynamische runtime (/MD, de standaard van CMake). Een Debug-plug-in wordt door de lader geweigerd; een plug-in met statische runtime (/MT) crasht wanneer geheugen de plug-ingrens oversteekt Release; deployment target 14.4 (hetzelfde als de editor)
Qt Qt 6.11, kit msvc2022_64. Een plug-in die tegen een oudere 6.x-minor gebouwd is laadt; een die tegen een nieuwere minor gebouwd is misschien niet Qt 6.11, dezelfde architectuur
C++-standaard C++20 (de API-headers gebruiken std::span en concepts) C++20
CMake 3.16+ 3.16+
Plug-in-API de MTE_PLUGIN_API_VERSION van de SDK die met de editorversie meekwam waarop u mikt idem

Twee gevolgen die de moeite van het weten waard zijn op Windows:

  • De Visual C++ Redistributable die op de machine van de gebruiker geïnstalleerd is moet minstens zo nieuw zijn als de nieuwste MSVC-toolset die door de editor of een geladen plug-in gebruikt wordt. Uw plug-in met een nieuwere toolset bouwen dan die van de editor verhoogt die eis voor uw gebruikers; bouwen met de toolset van de editor of met een oudere niet.
  • De versie van de Windows SDK waartegen u compileert beïnvloedt de compatibiliteit met de editor niet; die bepaalt alleen welke Windows-API's uw plug-in kan aanroepen, en de editor ondersteunt Windows 10 1809 en later.

En op macOS:

  • De editor wordt alleen voor arm64 geleverd; een plug-in die alleen x86_64 is kan niet geladen worden (een universeel binair bestand is prima). Houd het deployment target op 14.4: een plug-in die voor een nieuwere minimale macOS gebouwd is wordt op oudere Macs door de lader van het systeem geweigerd, en omdat de libc++ van het besturingssysteem zelf de runtime is, speelt er geen vraag rond een redistributable.
  • Verwijs naar de Qt-frameworks via @rpath, zodat de plug-in de kopieën oppikt die binnen OctoEdit.app meegeleverd zijn. Qt-kits waarvan de frameworks absolute installatienamen dragen (die van Homebrew bijvoorbeeld) hebben vóór de levering install_name_tool op de gebouwde plug-in nodig; anders zoekt de plug-in Qt op het pad van de bouwmachine.

Niets hiervan geldt voor Python-plug-ins: die draaien in een apart proces en delen geen binaire interface met de editor.

5. Installeren en uitvoeren

U kunt de gebouwde bibliotheek + plugin.json als <name>.mteplugin comprimeren en haar via Voorkeuren ▸ Plug-ins ▸ Plug-in installeren... installeren (het pakket wordt gevalideerd — manifest, veiligheid van het archief en de metagegevens die in het binaire bestand ingebed zijn — voordat er iets aangeraakt wordt), of de bestanden met de hand in een gevonden plug-inmap neerzetten (zie Overzicht van de architectuur). Herstart de editor en schakel haar zo nodig in bij Voorkeuren ▸ Plug-ins.

Volgende stappen