feat(sdk): add IToolbarProvider interface to kcsdk (#354)
All checks were successful
Build and Test / build (pull_request) Successful in 30m28s
All checks were successful
Build and Test / build (pull_request) Successful in 30m28s
This commit is contained in:
@@ -4,6 +4,7 @@ set(KCSDK_SRCS
|
||||
KCSDKGlobal.h
|
||||
Types.h
|
||||
IPanelProvider.h
|
||||
IToolbarProvider.h
|
||||
WidgetBridge.h
|
||||
WidgetBridge.cpp
|
||||
ThemeEngine.h
|
||||
|
||||
60
src/Gui/SDK/IToolbarProvider.h
Normal file
60
src/Gui/SDK/IToolbarProvider.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Kindred Systems <development@kindred-systems.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef KCSDK_ITOOLBARPROVIDER_H
|
||||
#define KCSDK_ITOOLBARPROVIDER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "KCSDKGlobal.h"
|
||||
|
||||
namespace KCSDK
|
||||
{
|
||||
|
||||
/// Abstract interface for addon-provided toolbar declarations.
|
||||
///
|
||||
/// Addons implement this interface to declaratively register toolbar
|
||||
/// configurations. On registration the SDK auto-injects the declared
|
||||
/// commands into the specified editing contexts.
|
||||
class KCSDKExport IToolbarProvider
|
||||
{
|
||||
public:
|
||||
virtual ~IToolbarProvider() = default;
|
||||
|
||||
/// Unique provider identifier (e.g. "ztools.partdesign", "gears.gear").
|
||||
virtual std::string id() const = 0;
|
||||
|
||||
/// Toolbar name shown in the UI.
|
||||
virtual std::string toolbar_name() const = 0;
|
||||
|
||||
/// Editing context IDs this toolbar applies to.
|
||||
virtual std::vector<std::string> context_ids() const = 0;
|
||||
|
||||
/// Command names to inject into the toolbar.
|
||||
virtual std::vector<std::string> commands() const = 0;
|
||||
};
|
||||
|
||||
} // namespace KCSDK
|
||||
|
||||
#endif // KCSDK_ITOOLBARPROVIDER_H
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "SDKRegistry.h"
|
||||
#include "IPanelProvider.h"
|
||||
#include "IToolbarProvider.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@@ -91,10 +92,13 @@ std::vector<std::string> SDKRegistry::available() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> result;
|
||||
result.reserve(panels_.size());
|
||||
result.reserve(panels_.size() + toolbars_.size());
|
||||
for (const auto& [id, _] : panels_) {
|
||||
result.push_back(id);
|
||||
}
|
||||
for (const auto& [id, _] : toolbars_) {
|
||||
result.push_back(id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -252,4 +256,53 @@ std::vector<std::string> SDKRegistry::registeredPanels() const
|
||||
return result;
|
||||
}
|
||||
|
||||
// -- Toolbar provider API ---------------------------------------------------
|
||||
|
||||
void SDKRegistry::registerToolbar(std::unique_ptr<IToolbarProvider> provider)
|
||||
{
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
std::string id = provider->id();
|
||||
std::string toolbarName = provider->toolbar_name();
|
||||
std::vector<std::string> contextIds = provider->context_ids();
|
||||
std::vector<std::string> cmds = provider->commands();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
toolbars_[id] = std::move(provider);
|
||||
}
|
||||
|
||||
// Auto-inject commands into each target context.
|
||||
for (const auto& ctxId : contextIds) {
|
||||
injectCommands(ctxId, toolbarName, cmds);
|
||||
}
|
||||
|
||||
Base::Console().log("KCSDK: registered toolbar provider '%s'\n", id.c_str());
|
||||
}
|
||||
|
||||
void SDKRegistry::unregisterToolbar(const std::string& id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto it = toolbars_.find(id);
|
||||
if (it == toolbars_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
toolbars_.erase(it);
|
||||
Base::Console().log("KCSDK: unregistered toolbar provider '%s'\n", id.c_str());
|
||||
}
|
||||
|
||||
std::vector<std::string> SDKRegistry::registeredToolbars() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> result;
|
||||
result.reserve(toolbars_.size());
|
||||
for (const auto& [id, _] : toolbars_) {
|
||||
result.push_back(id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace KCSDK
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace KCSDK
|
||||
{
|
||||
|
||||
class IPanelProvider;
|
||||
class IToolbarProvider;
|
||||
|
||||
/// Current KCSDK API major version. Addons should check this at load time.
|
||||
constexpr int API_VERSION_MAJOR = 1;
|
||||
@@ -102,6 +103,18 @@ public:
|
||||
/// Return IDs of all registered panel providers.
|
||||
std::vector<std::string> registeredPanels() const;
|
||||
|
||||
// -- Toolbar provider API ----------------------------------------------
|
||||
|
||||
/// Register a toolbar provider. Ownership transfers to the registry.
|
||||
/// Auto-injects the declared commands into the target editing contexts.
|
||||
void registerToolbar(std::unique_ptr<IToolbarProvider> provider);
|
||||
|
||||
/// Remove a registered toolbar provider.
|
||||
void unregisterToolbar(const std::string& id);
|
||||
|
||||
/// Return IDs of all registered toolbar providers.
|
||||
std::vector<std::string> registeredToolbars() const;
|
||||
|
||||
private:
|
||||
SDKRegistry();
|
||||
|
||||
@@ -112,6 +125,7 @@ private:
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, std::unique_ptr<IPanelProvider>> panels_;
|
||||
std::unordered_map<std::string, std::unique_ptr<IToolbarProvider>> toolbars_;
|
||||
};
|
||||
|
||||
} // namespace KCSDK
|
||||
|
||||
@@ -4,6 +4,8 @@ set(KCSDKPy_SRCS
|
||||
kcsdk_py.cpp
|
||||
PyIPanelProvider.h
|
||||
PyProviderHolder.h
|
||||
PyIToolbarProvider.h
|
||||
PyToolbarHolder.h
|
||||
)
|
||||
|
||||
add_library(kcsdk_py SHARED ${KCSDKPy_SRCS})
|
||||
|
||||
65
src/Gui/SDK/bindings/PyIToolbarProvider.h
Normal file
65
src/Gui/SDK/bindings/PyIToolbarProvider.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Kindred Systems <development@kindred-systems.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef KCSDK_PYITOOLBARPROVIDER_H
|
||||
#define KCSDK_PYITOOLBARPROVIDER_H
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include <Gui/SDK/IToolbarProvider.h>
|
||||
|
||||
namespace KCSDK
|
||||
{
|
||||
|
||||
/// pybind11 trampoline class for IToolbarProvider.
|
||||
/// Enables Python subclasses that override virtual methods.
|
||||
class PyIToolbarProvider : public IToolbarProvider
|
||||
{
|
||||
public:
|
||||
using IToolbarProvider::IToolbarProvider;
|
||||
|
||||
std::string id() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(std::string, IToolbarProvider, id);
|
||||
}
|
||||
|
||||
std::string toolbar_name() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(std::string, IToolbarProvider, toolbar_name);
|
||||
}
|
||||
|
||||
std::vector<std::string> context_ids() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(std::vector<std::string>, IToolbarProvider, context_ids);
|
||||
}
|
||||
|
||||
std::vector<std::string> commands() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(std::vector<std::string>, IToolbarProvider, commands);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace KCSDK
|
||||
|
||||
#endif // KCSDK_PYITOOLBARPROVIDER_H
|
||||
81
src/Gui/SDK/bindings/PyToolbarHolder.h
Normal file
81
src/Gui/SDK/bindings/PyToolbarHolder.h
Normal file
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Kindred Systems <development@kindred-systems.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef KCSDK_PYTOOLBARHOLDER_H
|
||||
#define KCSDK_PYTOOLBARHOLDER_H
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include <Gui/SDK/IToolbarProvider.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace KCSDK
|
||||
{
|
||||
|
||||
/// GIL-safe forwarding wrapper that holds a Python IToolbarProvider instance.
|
||||
///
|
||||
/// Stores the py::object to prevent garbage collection. Acquires the GIL
|
||||
/// before every call into Python. All methods return std types so no
|
||||
/// Qt/PySide bridging is needed.
|
||||
class PyToolbarHolder : public IToolbarProvider
|
||||
{
|
||||
public:
|
||||
explicit PyToolbarHolder(py::object obj)
|
||||
: obj_(std::move(obj))
|
||||
, provider_(obj_.cast<IToolbarProvider*>())
|
||||
{}
|
||||
|
||||
std::string id() const override
|
||||
{
|
||||
py::gil_scoped_acquire gil;
|
||||
return provider_->id();
|
||||
}
|
||||
|
||||
std::string toolbar_name() const override
|
||||
{
|
||||
py::gil_scoped_acquire gil;
|
||||
return provider_->toolbar_name();
|
||||
}
|
||||
|
||||
std::vector<std::string> context_ids() const override
|
||||
{
|
||||
py::gil_scoped_acquire gil;
|
||||
return provider_->context_ids();
|
||||
}
|
||||
|
||||
std::vector<std::string> commands() const override
|
||||
{
|
||||
py::gil_scoped_acquire gil;
|
||||
return provider_->commands();
|
||||
}
|
||||
|
||||
private:
|
||||
py::object obj_; ///< Prevents Python GC — keeps reference alive.
|
||||
IToolbarProvider* provider_; ///< Raw pointer into trampoline inside obj_.
|
||||
};
|
||||
|
||||
} // namespace KCSDK
|
||||
|
||||
#endif // KCSDK_PYTOOLBARHOLDER_H
|
||||
@@ -26,12 +26,15 @@
|
||||
#include <pybind11/functional.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 "PyIPanelProvider.h"
|
||||
#include "PyIToolbarProvider.h"
|
||||
#include "PyProviderHolder.h"
|
||||
#include "PyToolbarHolder.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace KCSDK;
|
||||
@@ -247,6 +250,40 @@ PYBIND11_MODULE(kcsdk, m)
|
||||
},
|
||||
"Return IDs of all registered panel providers.");
|
||||
|
||||
// -- Toolbar provider API -----------------------------------------------
|
||||
|
||||
py::class_<IToolbarProvider, PyIToolbarProvider>(m, "IToolbarProvider")
|
||||
.def(py::init<>())
|
||||
.def("id", &IToolbarProvider::id)
|
||||
.def("toolbar_name", &IToolbarProvider::toolbar_name)
|
||||
.def("context_ids", &IToolbarProvider::context_ids)
|
||||
.def("commands", &IToolbarProvider::commands);
|
||||
|
||||
m.def("register_toolbar",
|
||||
[](py::object provider) {
|
||||
auto holder = std::make_unique<PyToolbarHolder>(std::move(provider));
|
||||
SDKRegistry::instance().registerToolbar(std::move(holder));
|
||||
},
|
||||
py::arg("provider"),
|
||||
"Register a toolbar provider for automatic context injection.\n\n"
|
||||
"Parameters\n"
|
||||
"----------\n"
|
||||
"provider : IToolbarProvider\n"
|
||||
" Toolbar provider implementing id(), toolbar_name(), context_ids(), commands().");
|
||||
|
||||
m.def("unregister_toolbar",
|
||||
[](const std::string& id) {
|
||||
SDKRegistry::instance().unregisterToolbar(id);
|
||||
},
|
||||
py::arg("id"),
|
||||
"Remove a registered toolbar provider.");
|
||||
|
||||
m.def("registered_toolbars",
|
||||
[]() {
|
||||
return SDKRegistry::instance().registeredToolbars();
|
||||
},
|
||||
"Return IDs of all registered toolbar providers.");
|
||||
|
||||
// -- Theme engine API ---------------------------------------------------
|
||||
|
||||
m.def("theme_color",
|
||||
|
||||
Reference in New Issue
Block a user