Building & installing a plugin
ABI versioning
Easy Writer loads a plugin only when the plugin's abiVersion() equals the app's kPluginAbiVersion. The interface's IID carries the same version as a /N suffix:
#define EW_PLUGIN_IID "games.loomwright.easywriter.Plugin/1"
Both are bumped together on any incompatible change to the Plugin or PluginContext vtable, so a plugin built against an older SDK is rejected rather than mis-called. Always return ew::app::kPluginAbiVersion from abiVersion() and use EW_PLUGIN_IID in your Q_PLUGIN_METADATA — never hard-code a number.
CMake
Build the plugin as a Qt plugin with qt_add_plugin, linking the Easy Writer libraries and Qt Core:
qt_add_plugin(my_plugin CLASS_NAME MyPlugin)
target_sources(my_plugin PRIVATE MyPlugin.cpp)
target_link_libraries(my_plugin
PRIVATE
ew::app
ew::core
Qt6::Core)
CLASS_NAME must match the plugin's C++ class. Build it with the same compiler, C++ standard, and Qt version as the app (MSVC 2022 x64, C++20, Qt 6.8.3) — a plugin is a native library sharing the app's ABI.
Installing
At startup the app scans the plugins/ folder next to the executable (applicationDirPath()/plugins). Copy your built plugin library there and restart. Every file it examines — loaded or skipped, with the skip reason — is listed in Preferences ▸ Plugins; a bad file (not a Qt plugin, wrong IID, incompatible ABI, or a build-key mismatch) is skipped without aborting the scan or crashing the app.
What a plugin can contribute
For the current ABI, a plugin contributes through the PluginContext:
- Themes — an
ew::app::Themepalette, which then appears in Windows ▸ Theme and resolves across restarts. A built-in theme wins a name collision. - AI commands — an
ew::app::PromptCommand(a name plus an instruction), which appears in the AI command list and runs against the user's configured provider.
Both are pure value types, copied into the app's ContributionRegistry — a plugin never holds a live pointer into the app, keeping the boundary clean.