Move BuiltinPlugin into ocs_plugin_api (host feature) so out-of-tree crates can implement it, and add export_plugin! to emit the two C symbols a cdylib exposes: ocs_plugin_api_version (checked first, so an ABI-incompatible build never runs) and ocs_plugin_register -> boxed BuiltinPlugin. The host loads every compatible package from the plugins folder at startup via libloading (desktop only), keeps the library resident for the session, and merges its ribbon tab + command dispatch into the same paths as built-ins (honouring enable/disable). The Plugin Manager shows external packages with a Loaded / incompatible status. Approach B: the plugin hands back a boxed trait object, assuming a matching toolchain + ocs_plugin_api version (the version symbol enforces the latter). crates/ocs_example_plugin is the reference cdylib. Part of the #100 extensibility epic (phase 2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
68 lines
2.1 KiB
Rust
68 lines
2.1 KiB
Rust
//! Reference external add-on, built as a `cdylib` the host loads at runtime.
|
|
//!
|
|
//! It depends only on `ocs_plugin_api` (with the `host` feature) — never on the
|
|
//! `OpenCADStudio` binary — so it demonstrates the stable contract an
|
|
//! out-of-tree plugin targets: a `PluginManifest`, a `CadModule` ribbon tab, a
|
|
//! `BuiltinPlugin` entry point, and the `export_plugin!` C-ABI export.
|
|
|
|
use ocs_plugin_api::host::{BuiltinPlugin, HostApi};
|
|
use ocs_plugin_api::manifest::{ApiVersion, PluginManifest};
|
|
use ocs_plugin_api::ribbon::{CadModule, IconKind, ModuleEvent, RibbonGroup, RibbonItem, ToolDef};
|
|
|
|
static MANIFEST: PluginManifest = PluginManifest {
|
|
id: "opencad.example",
|
|
name: "Example Plugin",
|
|
version: "0.1.0",
|
|
description: "Reference dynamically-loaded add-on",
|
|
api_version: ApiVersion::CURRENT,
|
|
ribbon_order: 50,
|
|
xdata_apps: &[],
|
|
command_prefixes: &["EX_"],
|
|
};
|
|
|
|
/// Ribbon tab for the example plugin.
|
|
struct ExampleModule;
|
|
|
|
impl CadModule for ExampleModule {
|
|
fn id(&self) -> &'static str {
|
|
"example"
|
|
}
|
|
fn title(&self) -> &'static str {
|
|
"Example"
|
|
}
|
|
fn ribbon_groups(&self) -> Vec<RibbonGroup> {
|
|
vec![RibbonGroup {
|
|
title: "Demo",
|
|
tools: vec![RibbonItem::LargeTool(ToolDef {
|
|
id: "EX_HELLO",
|
|
label: "Hello",
|
|
icon: IconKind::Glyph("◆"),
|
|
event: ModuleEvent::Command("EX_HELLO".to_string()),
|
|
})],
|
|
}]
|
|
}
|
|
}
|
|
|
|
/// The plugin entry point handed to the host.
|
|
struct ExamplePlugin;
|
|
|
|
impl BuiltinPlugin for ExamplePlugin {
|
|
fn manifest(&self) -> &'static PluginManifest {
|
|
&MANIFEST
|
|
}
|
|
fn ribbon(&self) -> Box<dyn CadModule> {
|
|
Box::new(ExampleModule)
|
|
}
|
|
fn dispatch(&self, host: &mut dyn HostApi, cmd: &str) -> bool {
|
|
match cmd {
|
|
"EX_HELLO" => {
|
|
host.push_info("Hello from the external example plugin (cdylib loaded).");
|
|
true
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit the C-ABI symbols the host loader looks for.
|
|
ocs_plugin_api::export_plugin!(ExamplePlugin);
|