forked from 1b-agent/opencadstudio-upstream
feat(display): plugin entity-class labels in the panel, records, and automation
Resolve entities' XDATA class-identity keys through the live plugin manager (new `src/plugin/entity_labels.rs`) and qualify display types as "<Label> (<Native>)" wherever the bare native type is shown today. - The single-entity Properties panel title (and the node graph, which shares the same computation) becomes e.g. "Linear Element (Line)". Host-owned classifications (xref, dynamic constraints, underlays, surfaces) keep the native parenthesized part, so nothing they tell the user is lost. - The records API's `entities` collection: `display_type` carries the qualified label; the raw `type` / `record_type` fields stay native so existing scripts, type filters, and by-type grouping keep working. - The automation entity summary gains a `display_type` field with the same semantics; `type` stays raw. Exposed by `query`, `user_select` sessions, and `get_selection`. Semantics: the identity (a stable class KEY like "CVIFC-CLASS:LINE") lives in the entity's XDATA and round-trips the drawing; the display LABELS live in the declaring plugin's manifest. Keys only — no display text is ever written to the file, and the label never governs behavior (display-only: it does not affect snapping, selection, filtering, or grouping). Gating rides live manager state at query time, so without a loaded declaring plugin every surface here is byte-identical to today, immediately after any reopen, with no re-assert pass. The command-line LIST output deliberately stays on the raw dxf name. An app id claimed by more than one loaded plugin resolves through the first-loaded declaration and debug-logs the ignored duplicate; a prefix-only value, an unknown key, or an empty label mapping fall back to the native type. Tests: resolver core (known key, foreign app ignored, record without an identity value ignored, prefix-only / unknown key / empty label fallback, duplicate app id first-wins), the empty-manager gating end-to-end through `with_manager`, the "<Label> (<Native>)" form, and the byte-identical fallbacks asserted at the panel, records, and query layers for entities stamped with a perfect identity record.
This commit is contained in:
1 parent
60eb7b086e
commit
ab3e0723fd
6 files changed
+518
-36
No files matched your search
+57
-2
@@ -257,12 +257,25 @@ pub(crate) fn entity_type_matches(entity: &codec::EntityType, requested: &str) -
|
||||
|
||||
/// One entity as JSON. Summary mode carries identity only, geometry adds the
|
||||
/// entity's defining values, and full also includes its world bounds.
|
||||
pub(crate) fn entity_json(e: &codec::EntityType, detail: &str) -> Value {
|
||||
///
|
||||
/// `custom_label` is the plugin-declared class label for the entity's XDATA
|
||||
/// class identity, if a live declaring plugin provides one; it surfaces as
|
||||
/// `display_type` ("`<Label>` (`<Native>`)"), while `type` stays the raw
|
||||
/// native name so existing scripts and filters keep working.
|
||||
pub(crate) fn entity_json(
|
||||
e: &codec::EntityType,
|
||||
detail: &str,
|
||||
custom_label: Option<String>,
|
||||
) -> Value {
|
||||
use codec::EntityType as E;
|
||||
let c = e.common();
|
||||
let mut obj = json!({
|
||||
"handle": format!("{:X}", c.handle.value()),
|
||||
"type": crate::entities::names::ui_name(e),
|
||||
"display_type": crate::plugin::entity_labels::display_type_title(
|
||||
custom_label,
|
||||
crate::entities::names::ui_name(e).to_string(),
|
||||
),
|
||||
"layer": c.layer,
|
||||
});
|
||||
if detail == "summary" {
|
||||
@@ -1343,7 +1356,11 @@ impl OpenCADStudio {
|
||||
if near.is_some() && nearest.is_none() {
|
||||
continue;
|
||||
}
|
||||
let mut entity = entity_json(e, detail);
|
||||
let mut entity = entity_json(
|
||||
e,
|
||||
detail,
|
||||
crate::plugin::entity_labels::custom_entity_class_label(&self.disabled_plugins, e),
|
||||
);
|
||||
if let Some(nearest) = nearest {
|
||||
let object = entity.as_object_mut().expect("entity JSON object");
|
||||
object.insert("distance".into(), json!(nearest.distance));
|
||||
@@ -4319,4 +4336,42 @@ mod tests {
|
||||
.expect("second ready banner");
|
||||
assert!(banner2.contains("ready"), "{banner2}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entity_json_display_type_is_native_without_a_label() {
|
||||
let entity = codec::EntityType::Point(codec::entities::Point::new());
|
||||
let json = super::entity_json(&entity, "summary", None);
|
||||
assert_eq!(json["type"], "Point");
|
||||
assert_eq!(json["display_type"], "Point");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entity_json_display_type_qualifies_the_label() {
|
||||
let entity = codec::EntityType::Point(codec::entities::Point::new());
|
||||
let json = super::entity_json(&entity, "summary", Some("Survey Point".to_string()));
|
||||
assert_eq!(json["type"], "Point");
|
||||
assert_eq!(json["display_type"], "Survey Point (Point)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_reports_native_display_type_for_plugin_classed_entities() {
|
||||
// End-to-end through the automation query surface: an entity stamped
|
||||
// with a plugin class identity keeps the native display while no
|
||||
// declaring plugin is loaded (labels activate only through the live
|
||||
// plugin manager, which the test harness does not populate).
|
||||
let mut app = super::OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
let i = app.active_tab;
|
||||
let mut entity = codec::EntityType::Point(codec::entities::Point::new());
|
||||
let mut record = codec::xdata::ExtendedDataRecord::new("CVIFC");
|
||||
record.add_value(codec::xdata::XDataValue::String(
|
||||
"CVIFC-CLASS:POINTMARK".to_string(),
|
||||
));
|
||||
entity.common_mut().extended_data.add_record(record);
|
||||
app.tabs[i].scene.add_entity(entity);
|
||||
|
||||
let query = app.automation_op(r#"{"op":"query","type":"Point"}"#);
|
||||
assert_eq!(query["entities"][0]["type"], "Point");
|
||||
assert_eq!(query["entities"][0]["display_type"], "Point");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user