cherry-pick #32: MDI pre-document tab for Silo new item (70118201b0)
This commit is contained in:
@@ -67,6 +67,7 @@
|
||||
#include "WorkbenchManager.h"
|
||||
#include "WorkbenchManipulatorPython.h"
|
||||
#include "FileOriginPython.h"
|
||||
#include "EditingContext.h"
|
||||
#include "OriginManager.h"
|
||||
#include "Inventor/MarkerBitmaps.h"
|
||||
#include "Language/Translator.h"
|
||||
@@ -596,6 +597,68 @@ PyMethodDef ApplicationPy::Methods[] = {
|
||||
"Set the active origin by ID.\n"
|
||||
"\n"
|
||||
"id : str\n The origin ID to activate."},
|
||||
{"registerEditingContext",
|
||||
(PyCFunction)ApplicationPy::sRegisterEditingContext,
|
||||
METH_VARARGS,
|
||||
"registerEditingContext(id, label, color, toolbars, match, priority=50) -> None\n"
|
||||
"\n"
|
||||
"Register an editing context for dynamic toolbar management.\n"
|
||||
"\n"
|
||||
"id : str\n Unique context identifier (e.g. 'fem.analysis').\n"
|
||||
"label : str\n Display label template. Use {name} for object name.\n"
|
||||
"color : str\n Hex color for breadcrumb (e.g. '#f38ba8').\n"
|
||||
"toolbars : list of str\n Toolbar names to show when active.\n"
|
||||
"match : callable\n Function returning True if context is active.\n"
|
||||
"priority : int\n Higher values checked first (default 50)."},
|
||||
{"unregisterEditingContext",
|
||||
(PyCFunction)ApplicationPy::sUnregisterEditingContext,
|
||||
METH_VARARGS,
|
||||
"unregisterEditingContext(id) -> None\n"
|
||||
"\n"
|
||||
"Unregister an editing context.\n"
|
||||
"\n"
|
||||
"id : str\n Context identifier to remove."},
|
||||
{"registerEditingOverlay",
|
||||
(PyCFunction)ApplicationPy::sRegisterEditingOverlay,
|
||||
METH_VARARGS,
|
||||
"registerEditingOverlay(id, toolbars, match) -> None\n"
|
||||
"\n"
|
||||
"Register an overlay context that adds toolbars to any active context.\n"
|
||||
"\n"
|
||||
"id : str\n Unique overlay identifier.\n"
|
||||
"toolbars : list of str\n Toolbar names to add.\n"
|
||||
"match : callable\n Function returning True if overlay is active."},
|
||||
{"unregisterEditingOverlay",
|
||||
(PyCFunction)ApplicationPy::sUnregisterEditingOverlay,
|
||||
METH_VARARGS,
|
||||
"unregisterEditingOverlay(id) -> None\n"
|
||||
"\n"
|
||||
"Unregister an editing overlay.\n"
|
||||
"\n"
|
||||
"id : str\n Overlay identifier to remove."},
|
||||
{"injectEditingCommands",
|
||||
(PyCFunction)ApplicationPy::sInjectEditingCommands,
|
||||
METH_VARARGS,
|
||||
"injectEditingCommands(contextId, toolbarName, commands) -> None\n"
|
||||
"\n"
|
||||
"Inject additional commands into a context's toolbar.\n"
|
||||
"\n"
|
||||
"contextId : str\n Context to inject into.\n"
|
||||
"toolbarName : str\n Toolbar within that context.\n"
|
||||
"commands : list of str\n Command names to add."},
|
||||
{"currentEditingContext",
|
||||
(PyCFunction)ApplicationPy::sCurrentEditingContext,
|
||||
METH_VARARGS,
|
||||
"currentEditingContext() -> dict\n"
|
||||
"\n"
|
||||
"Get the current editing context as a dict with keys:\n"
|
||||
"id, label, color, toolbars, breadcrumb, breadcrumbColors."},
|
||||
{"refreshEditingContext",
|
||||
(PyCFunction)ApplicationPy::sRefreshEditingContext,
|
||||
METH_VARARGS,
|
||||
"refreshEditingContext() -> None\n"
|
||||
"\n"
|
||||
"Force re-resolution of the editing context."},
|
||||
{nullptr, nullptr, 0, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
@@ -2139,3 +2202,185 @@ PyObject* ApplicationPy::sSetActiveOrigin(PyObject* /*self*/, PyObject* args)
|
||||
}
|
||||
PY_CATCH;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Editing Context Python API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static QStringList pyListToQStringList(PyObject* listObj)
|
||||
{
|
||||
QStringList result;
|
||||
if (!listObj || !PyList_Check(listObj)) {
|
||||
return result;
|
||||
}
|
||||
Py_ssize_t size = PyList_Size(listObj);
|
||||
for (Py_ssize_t i = 0; i < size; ++i) {
|
||||
PyObject* item = PyList_GetItem(listObj, i);
|
||||
if (PyUnicode_Check(item)) {
|
||||
result << QString::fromUtf8(PyUnicode_AsUTF8(item));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject* qStringListToPyList(const QStringList& list)
|
||||
{
|
||||
Py::List pyList;
|
||||
for (const auto& s : list) {
|
||||
pyList.append(Py::String(s.toUtf8().constData()));
|
||||
}
|
||||
return Py::new_reference_to(pyList);
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sRegisterEditingContext(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
const char* id = nullptr;
|
||||
const char* label = nullptr;
|
||||
const char* color = nullptr;
|
||||
PyObject* toolbarsList = nullptr;
|
||||
PyObject* matchCallable = nullptr;
|
||||
int priority = 50;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sssOO|i", &id, &label, &color, &toolbarsList, &matchCallable, &priority)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!PyCallable_Check(matchCallable)) {
|
||||
PyErr_SetString(PyExc_TypeError, "match must be callable");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QStringList toolbars = pyListToQStringList(toolbarsList);
|
||||
|
||||
// Hold a reference to the Python callable
|
||||
Py::Callable pyMatch(matchCallable);
|
||||
pyMatch.increment_reference_count();
|
||||
|
||||
ContextDefinition def;
|
||||
def.id = QString::fromUtf8(id);
|
||||
def.labelTemplate = QString::fromUtf8(label);
|
||||
def.color = QString::fromUtf8(color);
|
||||
def.toolbars = toolbars;
|
||||
def.priority = priority;
|
||||
def.match = [pyMatch]() -> bool {
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Callable fn(pyMatch);
|
||||
Py::Object result = fn.apply(Py::TupleN());
|
||||
return result.isTrue();
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e;
|
||||
e.ReportException();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
EditingContextResolver::instance()->registerContext(def);
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sUnregisterEditingContext(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
const char* id = nullptr;
|
||||
if (!PyArg_ParseTuple(args, "s", &id)) {
|
||||
return nullptr;
|
||||
}
|
||||
EditingContextResolver::instance()->unregisterContext(QString::fromUtf8(id));
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sRegisterEditingOverlay(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
const char* id = nullptr;
|
||||
PyObject* toolbarsList = nullptr;
|
||||
PyObject* matchCallable = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sOO", &id, &toolbarsList, &matchCallable)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!PyCallable_Check(matchCallable)) {
|
||||
PyErr_SetString(PyExc_TypeError, "match must be callable");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QStringList toolbars = pyListToQStringList(toolbarsList);
|
||||
|
||||
Py::Callable pyMatch(matchCallable);
|
||||
pyMatch.increment_reference_count();
|
||||
|
||||
OverlayDefinition def;
|
||||
def.id = QString::fromUtf8(id);
|
||||
def.toolbars = toolbars;
|
||||
def.match = [pyMatch]() -> bool {
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Callable fn(pyMatch);
|
||||
Py::Object result = fn.apply(Py::TupleN());
|
||||
return result.isTrue();
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e;
|
||||
e.ReportException();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
EditingContextResolver::instance()->registerOverlay(def);
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sUnregisterEditingOverlay(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
const char* id = nullptr;
|
||||
if (!PyArg_ParseTuple(args, "s", &id)) {
|
||||
return nullptr;
|
||||
}
|
||||
EditingContextResolver::instance()->unregisterOverlay(QString::fromUtf8(id));
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sInjectEditingCommands(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
const char* contextId = nullptr;
|
||||
const char* toolbarName = nullptr;
|
||||
PyObject* commandsList = nullptr;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ssO", &contextId, &toolbarName, &commandsList)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QStringList commands = pyListToQStringList(commandsList);
|
||||
EditingContextResolver::instance()
|
||||
->injectCommands(QString::fromUtf8(contextId), QString::fromUtf8(toolbarName), commands);
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sCurrentEditingContext(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EditingContext ctx = EditingContextResolver::instance()->currentContext();
|
||||
|
||||
Py::Dict result;
|
||||
result.setItem("id", Py::String(ctx.id.toUtf8().constData()));
|
||||
result.setItem("label", Py::String(ctx.label.toUtf8().constData()));
|
||||
result.setItem("color", Py::String(ctx.color.toUtf8().constData()));
|
||||
result.setItem("toolbars", Py::Object(qStringListToPyList(ctx.toolbars), true));
|
||||
result.setItem("breadcrumb", Py::Object(qStringListToPyList(ctx.breadcrumb), true));
|
||||
result.setItem("breadcrumbColors", Py::Object(qStringListToPyList(ctx.breadcrumbColors), true));
|
||||
|
||||
return Py::new_reference_to(result);
|
||||
}
|
||||
|
||||
PyObject* ApplicationPy::sRefreshEditingContext(PyObject* /*self*/, PyObject* args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return nullptr;
|
||||
}
|
||||
EditingContextResolver::instance()->refresh();
|
||||
Py_Return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user