forked from 1b-agent/opencadstudio-upstream
feat(app): dispatch HostNotifications to native plugins (API v9)
Close the no-load-callback gap: on_notification was a dead dispatch sink —
a plugin had no way to learn that the active document changed, the selection
changed, or that a document opened, so runtime designations could not be
re-seeded after a load.
Dispatch the existing plain variants at the fingerprint-guarded boundaries
the v4 bridge already uses: DocumentChanged (the active tab's settled
geometry epoch) and SelectionChanged (the selection set) leave through
BuiltinPlugin::on_notification at the post-message boundary — after the
mutation settles and pending history scopes closed, never mid-undo or
mid-pick — and at the automation post-op boundary. Plugin-initiated writes
publish immediately after settling, like the drawing-changed event; the
boundary fingerprint guard deduplicates.
Add the additive HostNotification::DocumentOpened { tab_id } variant
(discriminant 10, the counterpart of DocumentTabClosed): fired exactly once
per whole-document install — file open, template new, system open — keyed on
a new per-tab document generation, so an edit can never repeat it and a
reload fires it once. The API version stays v9 (the bump is unreleased).
Write semantics for handlers, tested and documented: a handler may call
HostApi write methods through the same session (set_reference_only at
minimum); its write bumps the epoch, the next boundary notifies once more,
and because re-marking already-designated geometry is a documented no-op —
no epoch bump, no undo step, no publish — the loop terminates. History
(undo/redo) replay itself emits nothing; the boundary is the only channel.
No Python mapping, like the rest of this increment.
This commit is contained in:
1 parent
50db12b169
commit
2cdc1df021
11 files changed
+536
-11
No files matched your search
@@ -2244,6 +2244,10 @@ impl OpenCADStudio {
|
||||
.set(crate::app::OPEN_PHASE_FINALIZING, 10000, 1, 1);
|
||||
}
|
||||
self.opening.take();
|
||||
// The document and its derived caches have settled: mark the tab's
|
||||
// generation so the next message boundary announces `DocumentOpened`
|
||||
// to native plugins exactly once for this load (API v9).
|
||||
self.tabs[i].note_document_loaded();
|
||||
// The tab list just changed (and the active tab with it): remember
|
||||
// it for the next boot's restore flow (web session memory).
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
||||
@@ -308,6 +308,11 @@ impl OpenCADStudio {
|
||||
self.last_plugin_selection = Some(key);
|
||||
let handles = self.tabs[i].scene.selected_handles_in_order();
|
||||
crate::plugin::v4_support::publish_selection_changed_v4(tab_id, handles.clone());
|
||||
// Same guarded boundary, second surface (API v9): the plain
|
||||
// `SelectionChanged` reaches every loaded native plugin through
|
||||
// `BuiltinPlugin::on_notification`, without a shared-view
|
||||
// subscription.
|
||||
crate::plugin::v4_support::publish_selection_changed(handles.clone());
|
||||
self.control_record_selection_event(&handles);
|
||||
}
|
||||
|
||||
@@ -327,6 +332,35 @@ impl OpenCADStudio {
|
||||
self.last_plugin_document = Some(key);
|
||||
crate::plugin::v4_support::publish_drawing_changed(tab.id, tab.scene.geometry_epoch);
|
||||
crate::plugin::v4_support::publish_document_view_v4(tab.id, &tab.scene.document);
|
||||
// Same guarded boundary, second surface (API v9): the plain
|
||||
// `DocumentChanged` carries the settled geometry epoch to every
|
||||
// loaded native plugin through `BuiltinPlugin::on_notification`.
|
||||
crate::plugin::v4_support::publish_document_changed(tab.scene.geometry_epoch);
|
||||
}
|
||||
|
||||
/// Announce that a whole document became resident in the active tab —
|
||||
/// opened from file, created from a template, or loaded by a system open
|
||||
/// (API v9). Keyed on `(tab id, document generation)`, which changes only
|
||||
/// when a tab's content is wholesale replaced, so an edit can never fire
|
||||
/// it twice and a reload fires it exactly once. Part of the same
|
||||
/// post-message boundary as the selection and document dispatches: runs
|
||||
/// after the mutation settles and pending history scopes closed, never
|
||||
/// inside history replay or interactive picking.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(super) fn notify_plugins_document_opened(&mut self) {
|
||||
if self.active_tab >= self.tabs.len() {
|
||||
return;
|
||||
}
|
||||
let i = self.active_tab;
|
||||
if self.tabs[i].is_start {
|
||||
return;
|
||||
}
|
||||
let key = (self.tabs[i].id, self.tabs[i].document_generation);
|
||||
if self.last_plugin_document_opened == Some(key) {
|
||||
return;
|
||||
}
|
||||
self.last_plugin_document_opened = Some(key);
|
||||
crate::plugin::v4_support::publish_document_opened(self.tabs[i].id);
|
||||
}
|
||||
|
||||
pub fn update(&mut self, msg: Message) -> Task<Message> {
|
||||
@@ -540,6 +574,10 @@ impl OpenCADStudio {
|
||||
self.notify_plugins_selection_changed();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
self.notify_plugins_document_changed();
|
||||
// A message may also have installed a whole document into a tab (file
|
||||
// open, template new); announce it on the same boundary (API v9).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
self.notify_plugins_document_opened();
|
||||
// OTRACK acquires tracking points only while a command or grip drag is
|
||||
// running; drop them once neither is active so the temporary tracking
|
||||
// points / vectors disappear when the command ends (issue #64). A grip
|
||||
|
||||
Reference in new issue
Block a user