Merge pull request 'feat(sdk): context introspection — available_contexts and context_history (#383)' (#400) from feat/sdk-context-introspection into main
Some checks failed
Build and Test / build (push) Has been cancelled
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #400
This commit was merged in pull request #400.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
from kindred_sdk.command import register_command
|
||||
from kindred_sdk.compat import create_version, freecad_version
|
||||
from kindred_sdk.context import (
|
||||
available_contexts,
|
||||
current_context,
|
||||
inject_commands,
|
||||
refresh_context,
|
||||
@@ -13,7 +14,7 @@ from kindred_sdk.context import (
|
||||
)
|
||||
from kindred_sdk.dock import register_dock_panel
|
||||
from kindred_sdk.events import emit, off, on
|
||||
from kindred_sdk.lifecycle import on_context_enter, on_context_exit
|
||||
from kindred_sdk.lifecycle import context_history, on_context_enter, on_context_exit
|
||||
from kindred_sdk.menu import register_menu
|
||||
from kindred_sdk.origin import (
|
||||
active_origin,
|
||||
@@ -33,6 +34,8 @@ __all__ = [
|
||||
"SDK_VERSION",
|
||||
"active_origin",
|
||||
"addon_version",
|
||||
"available_contexts",
|
||||
"context_history",
|
||||
"create_version",
|
||||
"current_context",
|
||||
"emit",
|
||||
|
||||
@@ -157,6 +157,23 @@ def current_context():
|
||||
return None
|
||||
|
||||
|
||||
def available_contexts():
|
||||
"""Return metadata for all registered editing contexts.
|
||||
|
||||
Returns a list of dicts with keys: ``id``, ``label_template``,
|
||||
``color``, ``priority``. Sorted by descending priority (highest first).
|
||||
Returns an empty list on failure.
|
||||
"""
|
||||
_require_kcsdk()
|
||||
try:
|
||||
return _kcsdk.available_contexts()
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"kindred_sdk: Failed to get available contexts: {e}\n"
|
||||
)
|
||||
return []
|
||||
|
||||
|
||||
def refresh_context():
|
||||
"""Force re-resolution and update of the editing context."""
|
||||
_require_kcsdk()
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#
|
||||
# Also emits "context.enter" / "context.exit" on the SDK event bus.
|
||||
|
||||
import time
|
||||
from collections import deque
|
||||
|
||||
import FreeCAD
|
||||
|
||||
from kindred_sdk.events import emit as _emit_event
|
||||
@@ -15,6 +18,7 @@ _enter_listeners: dict[str, list] = {}
|
||||
_exit_listeners: dict[str, list] = {}
|
||||
_previous_context_id: str = ""
|
||||
_initialized: bool = False
|
||||
_history: deque = deque(maxlen=50)
|
||||
|
||||
|
||||
def on_context_enter(context_id, callback):
|
||||
@@ -55,6 +59,27 @@ def on_context_exit(context_id, callback):
|
||||
subs.append(callback)
|
||||
|
||||
|
||||
def context_history(limit=10):
|
||||
"""Return recent context transitions, newest first.
|
||||
|
||||
Each entry is a dict with keys:
|
||||
|
||||
- ``id`` — the context that was entered
|
||||
- ``timestamp`` — ``time.time()`` when the transition occurred
|
||||
- ``previous_id`` — the context that was exited (empty string if none)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
limit : int, optional
|
||||
Maximum entries to return. Default 10. Pass 0 for all (up to 50).
|
||||
"""
|
||||
entries = list(_history)
|
||||
entries.reverse()
|
||||
if limit:
|
||||
entries = entries[:limit]
|
||||
return entries
|
||||
|
||||
|
||||
def _fire_callbacks(listeners, context_id, ctx_dict):
|
||||
"""Dispatch to listeners for a specific id and the wildcard."""
|
||||
for key in (context_id, "*"):
|
||||
@@ -92,6 +117,15 @@ def _on_context_changed(new_ctx):
|
||||
_fire_callbacks(_enter_listeners, new_id, new_ctx)
|
||||
_emit_event("context.enter", new_ctx)
|
||||
|
||||
# Record transition in history ring buffer.
|
||||
_history.append(
|
||||
{
|
||||
"id": new_id,
|
||||
"timestamp": time.time(),
|
||||
"previous_id": old_id,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _init_lifecycle():
|
||||
"""Wire up the C++ context change signal. Called once from InitGui.py."""
|
||||
|
||||
@@ -599,6 +599,16 @@ EditingContext EditingContextResolver::currentContext() const
|
||||
return d->current;
|
||||
}
|
||||
|
||||
QList<EditingContextResolver::ContextInfo> EditingContextResolver::registeredContexts() const
|
||||
{
|
||||
QList<ContextInfo> result;
|
||||
result.reserve(d->contexts.size());
|
||||
for (const auto& def : d->contexts) {
|
||||
result.append({def.id, def.labelTemplate, def.color, def.priority});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Breadcrumb building
|
||||
|
||||
@@ -103,6 +103,19 @@ public:
|
||||
|
||||
EditingContext currentContext() const;
|
||||
|
||||
/// Static metadata for a registered context definition.
|
||||
struct ContextInfo
|
||||
{
|
||||
QString id;
|
||||
QString labelTemplate;
|
||||
QString color;
|
||||
int priority;
|
||||
};
|
||||
|
||||
/// Return static metadata for all registered context definitions.
|
||||
/// Sorted by descending priority (highest first).
|
||||
QList<ContextInfo> registeredContexts() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void contextChanged(const EditingContext& ctx);
|
||||
|
||||
|
||||
@@ -167,6 +167,22 @@ ContextSnapshot SDKRegistry::currentContext() const
|
||||
return snap;
|
||||
}
|
||||
|
||||
std::vector<SDKRegistry::ContextInfo> SDKRegistry::registeredContexts() const
|
||||
{
|
||||
auto guiContexts =
|
||||
Gui::EditingContextResolver::instance()->registeredContexts();
|
||||
|
||||
std::vector<ContextInfo> result;
|
||||
result.reserve(guiContexts.size());
|
||||
for (const auto& c : guiContexts) {
|
||||
result.push_back({fromQString(c.id),
|
||||
fromQString(c.labelTemplate),
|
||||
fromQString(c.color),
|
||||
c.priority});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SDKRegistry::refresh()
|
||||
{
|
||||
Gui::EditingContextResolver::instance()->refresh();
|
||||
|
||||
@@ -89,6 +89,19 @@ public:
|
||||
/// Return a snapshot of the current editing context.
|
||||
ContextSnapshot currentContext() const;
|
||||
|
||||
/// Static metadata for a registered context definition.
|
||||
struct ContextInfo
|
||||
{
|
||||
std::string id;
|
||||
std::string labelTemplate;
|
||||
std::string color;
|
||||
int priority;
|
||||
};
|
||||
|
||||
/// Return static metadata for all registered editing contexts.
|
||||
/// Sorted by descending priority (highest first).
|
||||
std::vector<ContextInfo> registeredContexts() const;
|
||||
|
||||
/// Force re-resolution of the editing context.
|
||||
void refresh();
|
||||
|
||||
|
||||
@@ -209,6 +209,24 @@ PYBIND11_MODULE(kcsdk, m)
|
||||
},
|
||||
"Force re-resolution of the editing context.");
|
||||
|
||||
m.def("available_contexts",
|
||||
[]() {
|
||||
auto contexts = SDKRegistry::instance().registeredContexts();
|
||||
py::list result;
|
||||
for (const auto& c : contexts) {
|
||||
py::dict d;
|
||||
d["id"] = c.id;
|
||||
d["label_template"] = c.labelTemplate;
|
||||
d["color"] = c.color;
|
||||
d["priority"] = c.priority;
|
||||
result.append(d);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
"Return metadata for all registered editing contexts.\n\n"
|
||||
"Each entry is a dict with keys: id, label_template, color, priority.\n"
|
||||
"Sorted by descending priority (highest first).");
|
||||
|
||||
m.def("on_context_changed",
|
||||
[](py::function callback) {
|
||||
auto held = std::make_shared<py::object>(std::move(callback));
|
||||
|
||||
Reference in New Issue
Block a user