forked from 1b-agent/opencadstudio-upstream
feat(scene): reference-only entities refuse the edit paths (API v9 host)
Host side of HostApi::set_reference_only: plugin-owned display geometry that renders and snaps normally but must not be edited or picked by the person at the screen, while the owning plugin keeps full write access through the host. - Storage: a human-readable marker value in an XDATA record under the host-owned OCS APPID (registered on first use; foreign values survive verbatim; stale verbatim DWG EED is dropped on rewrite), so the flag round-trips DWG and DXF with the entity and cannot collide with plugin APPID namespaces. - Grips: refresh_selected_grips yields none for a reference-only entity and an active grip edit on a newly designated target is cancelled like a locked-layer one. - Picking: a reference-only wire flag extends the snap_only skips in every hit test, so a click passes through to the editable entity underneath, and box, window, lasso, fence and select-all gathers skip the designation; fills, solids and instanced meshes are excluded through the shared candidate handle set. - Write policy: built-in user paths treat a reference-only entity like a locked-layer one (Scene::update_entity / update_entities refuse, erase_entities skips), while the plugin-owned variants (update_entity_plugin / erase_entities_plugin, mirroring the remove_entities editor split) and the whole HostSession surface write through. The MCP entities read exposes a top-level reference_only boolean and set_properties refuses with the reference_only failure code. - Undo: the entity-write contract - a change joins a plugin-opened push_undo scope, a scope-less change is one undo step of its own, refusals and idempotent re-marks record nothing. - Known limit: entities nested inside block instances (the block cache rebuilds instance wires without the flag). Evidence: marking_flags_and_unmarking_clears, foreign_values_under_the_host_app_id_survive, other_apps_are_untouched, reference_only_refusals_record_no_undo_step, reference_only_designation_composes_undo_like_entity_writes, reference_only_survives_dxf_and_dwg_round_trips, user_paths_refuse_and_plugin_paths_write_reference_only_entities, select_all_visible_skips_reference_only_entities, clicks_pass_through_reference_only_wires, box_selections_skip_reference_only_wires, entities_records_expose_the_reference_only_designation.
This commit is contained in:
1 parent
1f552038a8
commit
d24adc39b5
16 files changed
+806
-61
No files matched your search
+31
-2
@@ -31,6 +31,8 @@ pub enum BlockScalePolicy {
|
||||
mod block_base_point_tests;
|
||||
mod boundary;
|
||||
mod camera_ops;
|
||||
#[cfg(test)]
|
||||
mod reference_only_tests;
|
||||
pub use camera_ops::{add_background, copy_background, erase_background};
|
||||
pub(crate) mod centerline;
|
||||
pub(crate) mod centermark;
|
||||
@@ -56,6 +58,7 @@ mod page_setup;
|
||||
mod paper;
|
||||
mod preview;
|
||||
mod project;
|
||||
pub mod reference_only;
|
||||
mod scene_markers;
|
||||
mod selection;
|
||||
pub mod viewport_dimension_pick;
|
||||
@@ -9145,6 +9148,16 @@ impl Scene {
|
||||
.is_some_and(|l| l.is_locked())
|
||||
}
|
||||
|
||||
/// True when `handle` addresses a reference-only entity (API v9):
|
||||
/// plugin-owned display geometry the built-in edit paths treat like a
|
||||
/// locked-layer entity while its owning plugin keeps write access through
|
||||
/// the plugin host API. Absent entities are never reference-only.
|
||||
pub fn is_reference_only(&self, handle: Handle) -> bool {
|
||||
self.document
|
||||
.get_entity(handle)
|
||||
.is_some_and(reference_only::entity_is_reference_only)
|
||||
}
|
||||
|
||||
/// The name of the locked layer `handle` sits on, if any (for messages).
|
||||
pub fn locked_layer_name(&self, handle: Handle) -> Option<String> {
|
||||
let name = self
|
||||
@@ -10398,6 +10411,21 @@ impl Scene {
|
||||
pub fn interaction_candidate_handles(
|
||||
&self,
|
||||
candidates: &crate::scene::pick::interaction_index::InteractionCandidates,
|
||||
) -> Option<HashSet<Handle>> {
|
||||
let handles = self.interaction_candidate_handles_all(candidates);
|
||||
// Reference-only entities (API v9) are plugin-owned display geometry:
|
||||
// their fills, solids and instanced meshes are not pickable, so the
|
||||
// click/hover fallback paths that consume this set skip them and a
|
||||
// click passes through to the editable entity underneath.
|
||||
handles.map(|mut handles| {
|
||||
handles.retain(|h| !self.is_reference_only(*h));
|
||||
handles
|
||||
})
|
||||
}
|
||||
|
||||
fn interaction_candidate_handles_all(
|
||||
&self,
|
||||
candidates: &crate::scene::pick::interaction_index::InteractionCandidates,
|
||||
) -> Option<HashSet<Handle>> {
|
||||
let mut handles: HashSet<Handle> = candidates
|
||||
.iter()
|
||||
@@ -10643,7 +10671,7 @@ impl Scene {
|
||||
candidate_handles.as_ref(),
|
||||
));
|
||||
}
|
||||
handles.retain(|&h| self.passes_selection_filter(h));
|
||||
handles.retain(|&h| self.passes_selection_filter(h) && !self.is_reference_only(h));
|
||||
handles
|
||||
}
|
||||
|
||||
@@ -12733,7 +12761,8 @@ vis_index={:.1} visible_probe={:.1}",
|
||||
}
|
||||
|
||||
/// LAYOUTCREATEVIEWPORT: a layout opened for the first time gets a viewport.
|
||||
static LAYOUT_CREATE_VIEWPORT: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
|
||||
static LAYOUT_CREATE_VIEWPORT: std::sync::atomic::AtomicBool =
|
||||
std::sync::atomic::AtomicBool::new(true);
|
||||
|
||||
pub fn layout_create_viewport() -> bool {
|
||||
LAYOUT_CREATE_VIEWPORT.load(std::sync::atomic::Ordering::Relaxed)
|
||||
|
||||
Reference in new issue
Block a user