Add context/overlay registration, injection, query, and refresh to the KCSDK C++ library and kcsdk pybind11 module. New files: - src/Gui/SDK/Types.h — ContextDef, OverlayDef, ContextSnapshot structs (plain C++, no Qt in public API) Modified: - src/Gui/SDK/SDKRegistry.h/.cpp — register_context/overlay, unregister, inject_commands, current_context, refresh (delegates to EditingContextResolver with std↔Qt conversion) - src/Gui/SDK/CMakeLists.txt — add Types.h, link FreeCADGui - src/Gui/SDK/bindings/kcsdk_py.cpp — bind all context functions with GIL-safe match callable wrapping and dict-based snapshot return - mods/sdk/kindred_sdk/context.py — try kcsdk first, fall back to FreeCADGui for backwards compatibility
182 lines
5.8 KiB
Python
182 lines
5.8 KiB
Python
"""Editing context and overlay registration wrappers.
|
|
|
|
Routes through the ``kcsdk`` C++ module when available, falling back to
|
|
the legacy ``FreeCADGui`` Python bindings for backwards compatibility.
|
|
"""
|
|
|
|
import FreeCAD
|
|
|
|
# Try to import the C++ SDK module; None if not yet built/installed.
|
|
try:
|
|
import kcsdk as _kcsdk
|
|
except ImportError:
|
|
_kcsdk = None
|
|
|
|
|
|
def _gui():
|
|
"""Lazy import of FreeCADGui (not available in console mode)."""
|
|
import FreeCADGui
|
|
|
|
return FreeCADGui
|
|
|
|
|
|
def register_context(context_id, label, color, toolbars, match, priority=50):
|
|
"""Register an editing context.
|
|
|
|
Parameters
|
|
----------
|
|
context_id : str
|
|
Unique identifier (e.g. ``"myaddon.edit"``).
|
|
label : str
|
|
Display label template. Supports ``{name}`` placeholder.
|
|
color : str
|
|
Hex color for the breadcrumb (e.g. ``"#f38ba8"``).
|
|
toolbars : list[str]
|
|
Toolbar names to show when this context is active.
|
|
match : callable
|
|
Zero-argument callable returning *True* when this context is active.
|
|
priority : int, optional
|
|
Higher values are checked first. Default 50.
|
|
"""
|
|
if not isinstance(context_id, str):
|
|
raise TypeError(f"context_id must be str, got {type(context_id).__name__}")
|
|
if not isinstance(toolbars, list):
|
|
raise TypeError(f"toolbars must be list, got {type(toolbars).__name__}")
|
|
if not callable(match):
|
|
raise TypeError("match must be callable")
|
|
|
|
try:
|
|
if _kcsdk is not None:
|
|
_kcsdk.register_context(context_id, label, color, toolbars, match, priority)
|
|
else:
|
|
_gui().registerEditingContext(
|
|
context_id, label, color, toolbars, match, priority
|
|
)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(
|
|
f"kindred_sdk: Failed to register context '{context_id}': {e}\n"
|
|
)
|
|
|
|
|
|
def unregister_context(context_id):
|
|
"""Remove a previously registered editing context."""
|
|
if not isinstance(context_id, str):
|
|
raise TypeError(f"context_id must be str, got {type(context_id).__name__}")
|
|
|
|
try:
|
|
if _kcsdk is not None:
|
|
_kcsdk.unregister_context(context_id)
|
|
else:
|
|
_gui().unregisterEditingContext(context_id)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(
|
|
f"kindred_sdk: Failed to unregister context '{context_id}': {e}\n"
|
|
)
|
|
|
|
|
|
def register_overlay(overlay_id, toolbars, match):
|
|
"""Register an editing overlay.
|
|
|
|
Overlays add toolbars to whatever context is currently active when
|
|
*match* returns True.
|
|
|
|
Parameters
|
|
----------
|
|
overlay_id : str
|
|
Unique overlay identifier.
|
|
toolbars : list[str]
|
|
Toolbar names to append.
|
|
match : callable
|
|
Zero-argument callable returning *True* when the overlay applies.
|
|
"""
|
|
if not isinstance(overlay_id, str):
|
|
raise TypeError(f"overlay_id must be str, got {type(overlay_id).__name__}")
|
|
if not isinstance(toolbars, list):
|
|
raise TypeError(f"toolbars must be list, got {type(toolbars).__name__}")
|
|
if not callable(match):
|
|
raise TypeError("match must be callable")
|
|
|
|
try:
|
|
if _kcsdk is not None:
|
|
_kcsdk.register_overlay(overlay_id, toolbars, match)
|
|
else:
|
|
_gui().registerEditingOverlay(overlay_id, toolbars, match)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(
|
|
f"kindred_sdk: Failed to register overlay '{overlay_id}': {e}\n"
|
|
)
|
|
|
|
|
|
def unregister_overlay(overlay_id):
|
|
"""Remove a previously registered editing overlay."""
|
|
if not isinstance(overlay_id, str):
|
|
raise TypeError(f"overlay_id must be str, got {type(overlay_id).__name__}")
|
|
|
|
try:
|
|
if _kcsdk is not None:
|
|
_kcsdk.unregister_overlay(overlay_id)
|
|
else:
|
|
_gui().unregisterEditingOverlay(overlay_id)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(
|
|
f"kindred_sdk: Failed to unregister overlay '{overlay_id}': {e}\n"
|
|
)
|
|
|
|
|
|
def inject_commands(context_id, toolbar_name, commands):
|
|
"""Inject commands into a context's toolbar.
|
|
|
|
Parameters
|
|
----------
|
|
context_id : str
|
|
Target context identifier.
|
|
toolbar_name : str
|
|
Toolbar within that context.
|
|
commands : list[str]
|
|
Command names to add.
|
|
"""
|
|
if not isinstance(context_id, str):
|
|
raise TypeError(f"context_id must be str, got {type(context_id).__name__}")
|
|
if not isinstance(toolbar_name, str):
|
|
raise TypeError(f"toolbar_name must be str, got {type(toolbar_name).__name__}")
|
|
if not isinstance(commands, list):
|
|
raise TypeError(f"commands must be list, got {type(commands).__name__}")
|
|
|
|
try:
|
|
if _kcsdk is not None:
|
|
_kcsdk.inject_commands(context_id, toolbar_name, commands)
|
|
else:
|
|
_gui().injectEditingCommands(context_id, toolbar_name, commands)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(
|
|
f"kindred_sdk: Failed to inject commands into '{context_id}': {e}\n"
|
|
)
|
|
|
|
|
|
def current_context():
|
|
"""Return the current editing context as a dict.
|
|
|
|
Keys: ``id``, ``label``, ``color``, ``toolbars``, ``breadcrumb``,
|
|
``breadcrumbColors``. Returns ``None`` if no context is active.
|
|
"""
|
|
try:
|
|
if _kcsdk is not None:
|
|
return _kcsdk.current_context()
|
|
return _gui().currentEditingContext()
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(
|
|
f"kindred_sdk: Failed to get current context: {e}\n"
|
|
)
|
|
return None
|
|
|
|
|
|
def refresh_context():
|
|
"""Force re-resolution and update of the editing context."""
|
|
try:
|
|
if _kcsdk is not None:
|
|
_kcsdk.refresh()
|
|
else:
|
|
_gui().refreshEditingContext()
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(f"kindred_sdk: Failed to refresh context: {e}\n")
|