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
@@ -18,6 +18,17 @@ use std::sync::Arc;
|
||||
|
||||
static NEXT_DOCUMENT_TAB_ID: AtomicU64 = AtomicU64::new(1);
|
||||
|
||||
/// Monotonic source for `DocumentTab::document_generation` (API v9): every
|
||||
/// minted value is process-unique, so any freshly installed document — file
|
||||
/// open, template new, system open — differs from every generation a plugin
|
||||
/// has been told about, and the opened-notification guard fires exactly once
|
||||
/// per load.
|
||||
static NEXT_DOCUMENT_GENERATION: AtomicU64 = AtomicU64::new(1);
|
||||
|
||||
fn next_document_generation() -> u64 {
|
||||
NEXT_DOCUMENT_GENERATION.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
// ── Dynamic input ──────────────────────────────────────────────────────────
|
||||
|
||||
/// One quantity shown in the dynamic-input overlay near the cursor.
|
||||
@@ -147,6 +158,13 @@ pub(super) struct DocumentTab {
|
||||
/// Monotonic committed-edit/undo/redo revision. Background save completion
|
||||
/// uses it with scene epochs so an older snapshot never clears newer work.
|
||||
pub(super) edit_revision: u64,
|
||||
/// Monotonic identity of the document currently resident in this tab: a
|
||||
/// fresh value is minted whenever a whole document replaces the tab's
|
||||
/// content (file open, template new, system open), and edits leave it
|
||||
/// alone. The plugin-opened notification guard
|
||||
/// (`OpenCADStudio::notify_plugins_document_opened`, API v9) keys on it
|
||||
/// so `DocumentOpened` dispatches exactly once per load.
|
||||
pub(super) document_generation: u64,
|
||||
pub(super) tab_title: String,
|
||||
pub(super) properties: PropertiesPanel,
|
||||
pub(super) layers: LayerPanel,
|
||||
@@ -623,6 +641,7 @@ impl DocumentTab {
|
||||
dirty: false,
|
||||
recovery_save_as_required: false,
|
||||
edit_revision: 0,
|
||||
document_generation: next_document_generation(),
|
||||
prev_selection: Vec::new(),
|
||||
tab_title: format!("Drawing{}", n),
|
||||
properties: PropertiesPanel::empty(),
|
||||
@@ -692,6 +711,14 @@ impl DocumentTab {
|
||||
t
|
||||
}
|
||||
|
||||
/// A whole document just replaced this tab's content (file open, template
|
||||
/// new, system open): mint a fresh generation so the message-boundary
|
||||
/// dispatch announces `DocumentOpened` exactly once for this load (API
|
||||
/// v9). Call after the document and its derived caches have settled.
|
||||
pub(super) fn note_document_loaded(&mut self) {
|
||||
self.document_generation = next_document_generation();
|
||||
}
|
||||
|
||||
pub(super) fn tab_display_name(&self) -> String {
|
||||
match &self.current_path {
|
||||
Some(p) => p
|
||||
|
||||
Reference in new issue
Block a user