Merge pull request #1747 from schoeller/reference_only_entities

Reference-only entities (plugin API v9): a scene-scoped, in-memory
designation for plugin-owned display geometry, plus HostNotification
dispatch to native plugins so they can re-mark on DocumentOpened.

Resolves the new DeltaSnapshot site in block_editor.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-14 23:46:42 +03:00
commit 68330ce39e
40 files changed
+1977 -85

No files matched your search

+32 -4
View File
@@ -204,6 +204,7 @@ impl Scene {
let selected = handles
.into_iter()
.filter(|handle| self.passes_selection_filter(*handle))
.filter(|handle| !self.is_reference_only(*handle))
.filter(|handle| {
self.document.get_entity(*handle).is_some_and(|entity| {
self.resident_entity_visible(
@@ -1060,7 +1061,15 @@ impl Scene {
// ── Erase ─────────────────────────────────────────────────────────────
pub fn erase_entities(&mut self, handles: &[Handle]) {
self.remove_entities(handles, true);
self.remove_entities(handles, true, true);
}
/// Like [`erase_entities`](Self::erase_entities) but without the
/// reference-only skip. The plugin owns reference-only entities and
/// removes them through the plugin host API; locked-layer policy stays
/// enforced either way.
pub fn erase_entities_plugin(&mut self, handles: &[Handle]) {
self.remove_entities(handles, true, false);
}
/// Roll back entities created by the current command after a downstream
@@ -1069,10 +1078,15 @@ impl Scene {
/// The shared removal path keeps first-touch undo state, history objects,
/// groups, selection, and derived caches coherent.
pub(crate) fn rollback_new_entities(&mut self, handles: &[Handle]) {
self.remove_entities(handles, false);
self.remove_entities(handles, false, false);
}
fn remove_entities(&mut self, handles: &[Handle], respect_layer_locks: bool) {
fn remove_entities(
&mut self,
handles: &[Handle],
respect_layer_locks: bool,
respect_reference_only: bool,
) {
let erase_handles = self.handles_expanded_for_leader_annotations(handles);
// One pass over document.objects instead of a scan per erased handle.
@@ -1096,10 +1110,15 @@ impl Scene {
let mut hover_changed = false;
for &h in &erase_handles {
// Objects on a locked layer can't be erased.
// Objects on a locked layer can't be erased. Reference-only
// entities are plugin-owned: only the plugin-owned erase path
// may remove them.
if respect_layer_locks && self.is_layer_locked(h) {
continue;
}
if respect_reference_only && self.is_reference_only(h) {
continue;
}
let settings_handle = self.document.get_entity(h).and_then(|entity| match entity {
EntityType::Extended(extended) => match &extended.data {
codec::entities::ExtendedEntityData::SectionObject(data)
@@ -1125,6 +1144,12 @@ impl Scene {
let before = self.document.objects.get(&manager_handle).cloned();
self.record_undo_object_before(manager_handle, before);
}
// The designation set is scene state, not document state: a
// designated entity's set entry joins the same first-touch
// recording so undo restores entity and designation together.
if self.reference_only.is_marked(h) {
self.record_undo_reference_designation_before(h);
}
}
self.delete_solid_history(h);
if let Some(settings_handle) = settings_handle {
@@ -1143,6 +1168,9 @@ impl Scene {
}
self.remember_removed_cache_categories(h);
self.document.remove_entity_arc(h);
// The set entry dies with its entity (an absent entity is never
// reference-only). Undo restores it through the recorded channel.
self.reference_only.remove(h);
selection_changed |= self.selected.remove(&h);
self.selected_order.retain(|selected| *selected != h);
if self.hover_highlight == Some(h) {