feat(sdk): add IMenuProvider interface and register_command wrapper (#355)

IMenuProvider: declarative menu placement with optional context awareness.
C++ interface with pybind11 bindings + GIL-safe holder. SDKMenuManipulator
(shared WorkbenchManipulator) injects menu items on workbench switch,
filtered by editing context when context_ids() is non-empty.

register_command(): thin Python wrapper around FreeCADGui.addCommand()
that standardizes the calling convention within the SDK contract.

Python wrappers (kindred_sdk.register_menu, kindred_sdk.register_command)
use kcsdk-first routing with FreeCADGui fallback.
This commit is contained in:
forbes
2026-03-01 13:13:44 -06:00
parent 4eb643a26f
commit 747c458e23
11 changed files with 558 additions and 1 deletions

View File

@@ -25,14 +25,17 @@
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <Gui/SDK/IMenuProvider.h>
#include <Gui/SDK/IPanelProvider.h>
#include <Gui/SDK/IToolbarProvider.h>
#include <Gui/SDK/SDKRegistry.h>
#include <Gui/SDK/ThemeEngine.h>
#include <Gui/SDK/Types.h>
#include "PyIMenuProvider.h"
#include "PyIPanelProvider.h"
#include "PyIToolbarProvider.h"
#include "PyMenuHolder.h"
#include "PyProviderHolder.h"
#include "PyToolbarHolder.h"
@@ -284,6 +287,41 @@ PYBIND11_MODULE(kcsdk, m)
},
"Return IDs of all registered toolbar providers.");
// -- Menu provider API --------------------------------------------------
py::class_<IMenuProvider, PyIMenuProvider>(m, "IMenuProvider")
.def(py::init<>())
.def("id", &IMenuProvider::id)
.def("menu_path", &IMenuProvider::menu_path)
.def("items", &IMenuProvider::items)
.def("context_ids", &IMenuProvider::context_ids);
m.def("register_menu",
[](py::object provider) {
auto holder = std::make_unique<PyMenuHolder>(std::move(provider));
SDKRegistry::instance().registerMenu(std::move(holder));
},
py::arg("provider"),
"Register a menu provider for declarative menu placement.\n\n"
"Parameters\n"
"----------\n"
"provider : IMenuProvider\n"
" Menu provider implementing id(), menu_path(), items().\n"
" Optionally override context_ids() to limit to specific contexts.");
m.def("unregister_menu",
[](const std::string& id) {
SDKRegistry::instance().unregisterMenu(id);
},
py::arg("id"),
"Remove a registered menu provider.");
m.def("registered_menus",
[]() {
return SDKRegistry::instance().registeredMenus();
},
"Return IDs of all registered menu providers.");
// -- Theme engine API ---------------------------------------------------
m.def("theme_color",