From b2b2bfb24604eccd91327a597fc0c3acedea3b3f Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 2 Jul 2026 22:46:19 +0300 Subject: [PATCH] refactor(properties): entity property groups return Vec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Groundwork for making the Properties panel conform to the per-entity group/row spec (PROPERTIES.md). The panel already renders a Vec of sections, but each entity could only express a single geometry section. Change PropertyEditable::geometry_properties (and the EntityTypeOps enum dispatch) to return Vec, update the two impl macros, and flatten in properties_sectioned so the panel shows [General] + the entity's groups. Every entity builder is wrapped to return its existing section as a one-element Vec — pure mechanical change, no rows or titles altered, panel output identical. Enables Phase 2: rewriting each entity's groups/rows to match the spec. Co-Authored-By: Claude Opus 4.8 --- src/entities/arc.rs | 8 ++++---- src/entities/attribute.rs | 12 ++++++------ src/entities/circle.rs | 6 +++--- src/entities/dimension.rs | 8 ++++---- src/entities/ellipse.rs | 6 +++--- src/entities/hatch.rs | 8 ++++---- src/entities/insert.rs | 6 +++--- src/entities/leader.rs | 8 ++++---- src/entities/line.rs | 8 ++++---- src/entities/lwpolyline.rs | 8 ++++---- src/entities/mesh.rs | 24 ++++++++++++------------ src/entities/mline.rs | 6 +++--- src/entities/mtext.rs | 8 ++++---- src/entities/multileader.rs | 8 ++++---- src/entities/ole2frame.rs | 6 +++--- src/entities/point.rs | 6 +++--- src/entities/polyline.rs | 18 +++++++++--------- src/entities/raster_image.rs | 12 ++++++------ src/entities/ray.rs | 12 ++++++------ src/entities/shape.rs | 6 +++--- src/entities/solid.rs | 6 +++--- src/entities/solid3d.rs | 18 +++++++++--------- src/entities/spline.rs | 8 ++++---- src/entities/table.rs | 6 +++--- src/entities/text.rs | 8 ++++---- src/entities/tolerance.rs | 6 +++--- src/entities/traits.rs | 16 +++++++++------- src/entities/underlay.rs | 6 +++--- src/entities/viewport.rs | 6 +++--- src/scene/selection.rs | 9 ++++++--- src/scene/view/dispatch.rs | 13 ++++++++----- 31 files changed, 147 insertions(+), 139 deletions(-) diff --git a/src/entities/arc.rs b/src/entities/arc.rs index 25a211f01..0719806e9 100644 --- a/src/entities/arc.rs +++ b/src/entities/arc.rs @@ -121,8 +121,8 @@ fn grips(arc: &Arc) -> Vec { ] } -fn properties(arc: &Arc) -> PropSection { - PropSection { +fn properties(arc: &Arc) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Center X", "center_x", arc.center.x), @@ -136,7 +136,7 @@ fn properties(arc: &Arc) -> PropSection { ), edit("End Angle (deg)", "end_angle", arc.end_angle.to_degrees()), ], - } + }] } fn apply_geom_prop(arc: &mut Arc, field: &str, value: &str) { @@ -313,7 +313,7 @@ impl crate::entities::traits::Grippable for Arc { } impl crate::entities::traits::PropertyEditable for Arc { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/attribute.rs b/src/entities/attribute.rs index 88fffc081..be207379b 100644 --- a/src/entities/attribute.rs +++ b/src/entities/attribute.rs @@ -384,7 +384,7 @@ impl Grippable for AttributeDefinition { } impl PropertyEditable for AttributeDefinition { - fn geometry_properties(&self, text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, text_style_names: &[String]) -> Vec { let mut props = vec![ ro("Tag", "att_tag", self.tag.clone()), ro("Prompt", "att_prompt", self.prompt.clone()), @@ -478,10 +478,10 @@ impl PropertyEditable for AttributeDefinition { p.value = PropValue::ReadOnly(self.default_value.clone()); } } - PropSection { + vec![PropSection { title: "Geometry".into(), props, - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -625,8 +625,8 @@ impl Grippable for AttributeEntity { } impl PropertyEditable for AttributeEntity { - fn geometry_properties(&self, text_style_names: &[String]) -> PropSection { - PropSection { + fn geometry_properties(&self, text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Tag", "atte_tag", self.tag.clone()), @@ -720,7 +720,7 @@ impl PropertyEditable for AttributeEntity { bool_yn(self.flags.annotative), ), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/circle.rs b/src/entities/circle.rs index beb3f9241..119232b5b 100644 --- a/src/entities/circle.rs +++ b/src/entities/circle.rs @@ -118,8 +118,8 @@ fn grips(circle: &Circle) -> Vec { ] } -fn properties(circle: &Circle) -> PropSection { - PropSection { +fn properties(circle: &Circle) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Center X", "center_x", circle.center.x), @@ -137,7 +137,7 @@ fn properties(circle: &Circle) -> PropSection { format!("{:.4}", circle.radius * 2.0 * std::f64::consts::PI), ), ], - } + }] } fn apply_geom_prop(circle: &mut Circle, field: &str, value: &str) { diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index c78e9f130..aa8048888 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -57,7 +57,7 @@ fn base_props(base: &DimensionBase) -> Vec PropSection { +fn properties(dim: &Dimension) -> Vec { let mut props = base_props(dim.base()); match dim { Dimension::Aligned(d) => { @@ -129,10 +129,10 @@ fn properties(dim: &Dimension) -> PropSection { )); } } - PropSection { + vec![PropSection { title: "Geometry".into(), props, - } + }] } fn linear_like_props( @@ -579,7 +579,7 @@ fn mirror_point(p: &mut acadrust::types::Vector3, p1: DVec3, p2: DVec3) { } impl PropertyEditable for Dimension { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { properties(self) } diff --git a/src/entities/ellipse.rs b/src/entities/ellipse.rs index ab8e99bdd..1ee3170ee 100644 --- a/src/entities/ellipse.rs +++ b/src/entities/ellipse.rs @@ -158,12 +158,12 @@ fn grips(ell: &Ellipse) -> Vec { ] } -fn properties(ell: &Ellipse) -> PropSection { +fn properties(ell: &Ellipse) -> Vec { let r_major = (ell.major_axis.x * ell.major_axis.x + ell.major_axis.y * ell.major_axis.y + ell.major_axis.z * ell.major_axis.z) .sqrt(); - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Center X", "center_x", ell.center.x), @@ -181,7 +181,7 @@ fn properties(ell: &Ellipse) -> PropSection { format!("{:.4}", ell.minor_axis_ratio), ), ], - } + }] } fn apply_geom_prop(_ell: &mut Ellipse, _field: &str, _value: &str) {} diff --git a/src/entities/hatch.rs b/src/entities/hatch.rs index 7a349494c..685aec79c 100644 --- a/src/entities/hatch.rs +++ b/src/entities/hatch.rs @@ -8,7 +8,7 @@ use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Pr use crate::scene::convert::tess_util::{arc_segments, arc_signed_span, wire_chord_tol, FallbackGeometry}; use crate::scene::model::wire_model::SnapHint; -fn properties(h: &Hatch) -> PropSection { +fn properties(h: &Hatch) -> Vec { let pattern_type = match h.pattern_type { acadrust::entities::HatchPatternType::Predefined => "Predefined", acadrust::entities::HatchPatternType::UserDefined => "User Defined", @@ -39,7 +39,7 @@ fn properties(h: &Hatch) -> PropSection { .sum::() }) .sum(); - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Fill Type", "fill_type", fill_type), @@ -73,7 +73,7 @@ fn properties(h: &Hatch) -> PropSection { format!("{:.3}, {:.3}, {:.3}", h.normal.x, h.normal.y, h.normal.z), ), ], - } + }] } fn apply_geom_prop(h: &mut Hatch, field: &str, value: &str) { @@ -103,7 +103,7 @@ fn apply_transform(h: &mut Hatch, t: &EntityTransform) { } impl PropertyEditable for Hatch { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { properties(self) } diff --git a/src/entities/insert.rs b/src/entities/insert.rs index acf359609..0f4120bd9 100644 --- a/src/entities/insert.rs +++ b/src/entities/insert.rs @@ -19,8 +19,8 @@ fn grips(ins: &Insert) -> Vec { vec![square_grip(0, glam::DVec3::new(w.x, w.y, w.z))] } -fn properties(ins: &Insert) -> PropSection { - PropSection { +fn properties(ins: &Insert) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Insert X", "ins_x", ins.insert_point.x), @@ -32,7 +32,7 @@ fn properties(ins: &Insert) -> PropSection { edit("Rotation", "rotation", ins.rotation.to_degrees()), ro("Block", "block", ins.block_name.clone()), ], - } + }] } fn apply_geom_prop(ins: &mut Insert, field: &str, value: &str) { diff --git a/src/entities/leader.rs b/src/entities/leader.rs index b1a08cfdc..e4fa7fb3d 100644 --- a/src/entities/leader.rs +++ b/src/entities/leader.rs @@ -191,7 +191,7 @@ fn hookline_dir_str(hd: &HooklineDirection) -> &'static str { } } -fn properties(leader: &Leader) -> PropSection { +fn properties(leader: &Leader) -> Vec { let n = leader.vertices.len(); let mut props = vec![ // Style @@ -283,10 +283,10 @@ fn properties(leader: &Leader) -> PropSection { } } - PropSection { + vec![PropSection { title: "Geometry".into(), props, - } + }] } fn apply_geom_prop(leader: &mut Leader, field: &str, value: &str) { @@ -540,7 +540,7 @@ impl crate::entities::traits::Grippable for Leader { } impl crate::entities::traits::PropertyEditable for Leader { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/line.rs b/src/entities/line.rs index f9a9af98d..7ae3dbd42 100644 --- a/src/entities/line.rs +++ b/src/entities/line.rs @@ -72,8 +72,8 @@ fn grips(line: &Line) -> Vec { vec![square_grip(0, s), square_grip(1, e), center_grip(2, m)] } -fn properties(line: &Line) -> PropSection { - PropSection { +fn properties(line: &Line) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Start X", "start_x", line.start.x), @@ -84,7 +84,7 @@ fn properties(line: &Line) -> PropSection { edit("End Z", "end_z", line.end.z), ro("Length", "length", format!("{:.4}", line.length())), ], - } + }] } fn apply_geom_prop(line: &mut Line, field: &str, value: &str) { @@ -232,7 +232,7 @@ impl crate::entities::traits::Grippable for Line { } impl crate::entities::traits::PropertyEditable for Line { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/lwpolyline.rs b/src/entities/lwpolyline.rs index 538288f22..83bea3c70 100644 --- a/src/entities/lwpolyline.rs +++ b/src/entities/lwpolyline.rs @@ -322,9 +322,9 @@ fn grips(pline: &LwPolyline) -> Vec { out } -fn properties(pline: &LwPolyline) -> PropSection { +fn properties(pline: &LwPolyline) -> Vec { let (length, area) = length_and_area(pline); - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Vertices", "vertices", pline.vertices.len().to_string()), @@ -337,7 +337,7 @@ fn properties(pline: &LwPolyline) -> PropSection { ro("Length", "length", format!("{length:.4}")), ro("Area", "area", format!("{area:.4}")), ], - } + }] } /// Path length and enclosed area of a polyline, accounting for arc @@ -587,7 +587,7 @@ impl crate::entities::traits::PropertyEditable for LwPolyline { fn geometry_properties( &self, _text_style_names: &[String], - ) -> crate::scene::model::object::PropSection { + ) -> Vec { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/mesh.rs b/src/entities/mesh.rs index 4c318ffef..2083a0837 100644 --- a/src/entities/mesh.rs +++ b/src/entities/mesh.rs @@ -115,9 +115,9 @@ impl Grippable for Face3D { } impl PropertyEditable for Face3D { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { use crate::entities::common::edit_prop as edit; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("P1 X", "f3_p1x", self.first_corner.x), @@ -133,7 +133,7 @@ impl PropertyEditable for Face3D { edit("P4 Y", "f3_p4y", self.fourth_corner.y), edit("P4 Z", "f3_p4z", self.fourth_corner.z), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -276,14 +276,14 @@ impl Grippable for PolygonMesh { } impl PropertyEditable for PolygonMesh { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let smooth = match self.smooth_type { acadrust::entities::polygon_mesh::SurfaceSmoothType::NoSmooth => "None", acadrust::entities::polygon_mesh::SurfaceSmoothType::Quadratic => "Quadratic", acadrust::entities::polygon_mesh::SurfaceSmoothType::Cubic => "Cubic", acadrust::entities::polygon_mesh::SurfaceSmoothType::Bezier => "Bezier", }; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("M count", "pm_m", self.m_vertex_count.to_string()), @@ -314,7 +314,7 @@ impl PropertyEditable for PolygonMesh { ), ), ], - } + }] } fn apply_geom_prop(&mut self, _field: &str, _value: &str) {} @@ -424,14 +424,14 @@ impl Grippable for PolyfaceMesh { } impl PropertyEditable for PolyfaceMesh { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let smooth = match self.smooth_surface { acadrust::entities::PolyfaceSmoothType::None => "None", acadrust::entities::PolyfaceSmoothType::Quadratic => "Quadratic", acadrust::entities::PolyfaceSmoothType::Cubic => "Cubic", acadrust::entities::PolyfaceSmoothType::Bezier => "Bezier", }; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Vertices", "pfm_v", self.vertices.len().to_string()), @@ -464,7 +464,7 @@ impl PropertyEditable for PolyfaceMesh { format!("{:.4}", self.thickness), ), ], - } + }] } fn apply_geom_prop(&mut self, _field: &str, _value: &str) {} @@ -597,9 +597,9 @@ impl Grippable for Mesh { } impl PropertyEditable for Mesh { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let creased_edges = self.edges.iter().filter(|e| e.has_crease()).count(); - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Vertices", "msh_v", self.vertices.len().to_string()), @@ -618,7 +618,7 @@ impl PropertyEditable for Mesh { if self.blend_crease { "Yes" } else { "No" }, ), ], - } + }] } fn apply_geom_prop(&mut self, _field: &str, _value: &str) {} diff --git a/src/entities/mline.rs b/src/entities/mline.rs index f761c4b39..b7b726bad 100644 --- a/src/entities/mline.rs +++ b/src/entities/mline.rs @@ -188,13 +188,13 @@ impl Grippable for MLine { } impl PropertyEditable for MLine { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let just_str = match self.justification { acadrust::entities::MLineJustification::Top => "Top", acadrust::entities::MLineJustification::Zero => "Zero", acadrust::entities::MLineJustification::Bottom => "Bottom", }; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Style", "ml_style", self.style_name.clone()), @@ -233,7 +233,7 @@ impl PropertyEditable for MLine { }, }, ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/mtext.rs b/src/entities/mtext.rs index 1233a9826..76cd4b012 100644 --- a/src/entities/mtext.rs +++ b/src/entities/mtext.rs @@ -192,8 +192,8 @@ fn grips(t: &MText) -> Vec { vec![square_grip(0, p), triangle_grip(1, width_grip)] } -fn properties(t: &MText, text_style_names: &[String]) -> PropSection { - PropSection { +fn properties(t: &MText, text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Insert X", "ins_x", t.insertion_point.x), @@ -254,7 +254,7 @@ fn properties(t: &MText, text_style_names: &[String]) -> PropSection { }, }, ], - } + }] } fn apply_geom_prop(t: &mut MText, field: &str, value: &str) { @@ -413,7 +413,7 @@ impl Grippable for MText { } impl PropertyEditable for MText { - fn geometry_properties(&self, text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, text_style_names: &[String]) -> Vec { properties(self, text_style_names) } diff --git a/src/entities/multileader.rs b/src/entities/multileader.rs index 7ab571c02..ac6339614 100644 --- a/src/entities/multileader.rs +++ b/src/entities/multileader.rs @@ -520,7 +520,7 @@ fn choice(label: &str, field: &'static str, selected: &str, opts: &[&str]) -> Pr } } -fn properties(ml: &MultiLeader) -> PropSection { +fn properties(ml: &MultiLeader) -> Vec { let ctx = &ml.context; let total_pts: usize = ctx .leader_roots @@ -714,10 +714,10 @@ fn properties(ml: &MultiLeader) -> PropSection { props.push(edit("Root Conn Z", "conn_z", root.connection_point.z)); } - PropSection { + vec![PropSection { title: "Geometry".into(), props, - } + }] } fn apply_geom_prop(ml: &mut MultiLeader, field: &str, value: &str) { @@ -1047,7 +1047,7 @@ impl crate::entities::traits::Grippable for MultiLeader { } impl crate::entities::traits::PropertyEditable for MultiLeader { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/ole2frame.rs b/src/entities/ole2frame.rs index 29ca46a9d..f4a3958ee 100644 --- a/src/entities/ole2frame.rs +++ b/src/entities/ole2frame.rs @@ -73,13 +73,13 @@ fn grips(ole: &Ole2Frame) -> Vec { ] } -fn properties(ole: &Ole2Frame) -> PropSection { +fn properties(ole: &Ole2Frame) -> Vec { let type_str = match ole.ole_object_type { OleObjectType::Link => "Link", OleObjectType::Embedded => "Embedded", OleObjectType::Static => "Static", }; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Type", "ole_type", type_str), @@ -114,7 +114,7 @@ fn properties(ole: &Ole2Frame) -> PropSection { if ole.is_paper_space { "Yes" } else { "No" }, ), ], - } + }] } fn apply_geom_prop(ole: &mut Ole2Frame, field: &str, value: &str) { diff --git a/src/entities/point.rs b/src/entities/point.rs index 479353321..21c62a39a 100644 --- a/src/entities/point.rs +++ b/src/entities/point.rs @@ -178,15 +178,15 @@ fn grips(pt: &Point) -> Vec { vec![square_grip(0, p)] } -fn properties(pt: &Point) -> PropSection { - PropSection { +fn properties(pt: &Point) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("X", "loc_x", pt.location.x), edit("Y", "loc_y", pt.location.y), edit("Z", "loc_z", pt.location.z), ], - } + }] } fn apply_geom_prop(pt: &mut Point, field: &str, value: &str) { diff --git a/src/entities/polyline.rs b/src/entities/polyline.rs index 9be2cf1f8..35e727a2f 100644 --- a/src/entities/polyline.rs +++ b/src/entities/polyline.rs @@ -117,8 +117,8 @@ impl Grippable for Polyline { } impl PropertyEditable for Polyline { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { - PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Vertices", "vertices", self.vertices.len().to_string()), @@ -131,7 +131,7 @@ impl PropertyEditable for Polyline { }, }, ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -386,14 +386,14 @@ impl Grippable for Polyline2D { } impl PropertyEditable for Polyline2D { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let smooth = match self.smooth_surface { acadrust::entities::SmoothSurfaceType::None => "None", acadrust::entities::SmoothSurfaceType::QuadraticBSpline => "Quadratic", acadrust::entities::SmoothSurfaceType::CubicBSpline => "Cubic", acadrust::entities::SmoothSurfaceType::Bezier => "Bezier", }; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Vertices", "vertices", self.vertices.len().to_string()), @@ -419,7 +419,7 @@ impl PropertyEditable for Polyline2D { }, }, ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -604,7 +604,7 @@ impl Grippable for Polyline3D { } impl PropertyEditable for Polyline3D { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { use acadrust::entities::polyline3d::SmoothSurfaceType as SST; let smooth = match self.smooth_type { SST::None => "None", @@ -612,7 +612,7 @@ impl PropertyEditable for Polyline3D { SST::CubicBSpline => "Cubic", SST::Bezier => "Bezier", }; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Vertices", "vertices", self.vertices.len().to_string()), @@ -640,7 +640,7 @@ impl PropertyEditable for Polyline3D { }, }, ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/raster_image.rs b/src/entities/raster_image.rs index 0e774b355..0bf9eb378 100644 --- a/src/entities/raster_image.rs +++ b/src/entities/raster_image.rs @@ -162,8 +162,8 @@ impl Grippable for RasterImage { } impl PropertyEditable for RasterImage { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { - PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("File", "ri_file", self.file_path.clone()), @@ -203,7 +203,7 @@ impl PropertyEditable for RasterImage { }, ), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -423,8 +423,8 @@ impl Grippable for Wipeout { } impl PropertyEditable for Wipeout { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { - PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Insert X", "wo_ox", self.insertion_point.x), @@ -464,7 +464,7 @@ impl PropertyEditable for Wipeout { }, ), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/ray.rs b/src/entities/ray.rs index b9306cff6..d96276479 100644 --- a/src/entities/ray.rs +++ b/src/entities/ray.rs @@ -88,8 +88,8 @@ impl Grippable for Ray { } impl PropertyEditable for Ray { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { - PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Base X", "ray_bx", self.base_point.x), @@ -99,7 +99,7 @@ impl PropertyEditable for Ray { edit("Dir Y", "ray_dy", self.direction.y), edit("Dir Z", "ray_dz", self.direction.z), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -225,8 +225,8 @@ impl Grippable for XLine { } impl PropertyEditable for XLine { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { - PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Base X", "xl_bx", self.base_point.x), @@ -236,7 +236,7 @@ impl PropertyEditable for XLine { edit("Dir Y", "xl_dy", self.direction.y), edit("Dir Z", "xl_dz", self.direction.z), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/shape.rs b/src/entities/shape.rs index caebcfb9d..4d59d179c 100644 --- a/src/entities/shape.rs +++ b/src/entities/shape.rs @@ -118,12 +118,12 @@ impl Grippable for Shape { // ── PropertyEditable ────────────────────────────────────────────────────────── impl PropertyEditable for Shape { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let style_handle_display = match self.style_handle { Some(h) if !h.is_null() => format!("{:X}", h.value()), _ => "(none)".to_string(), }; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Name", "shp_name", self.shape_name.clone()), @@ -142,7 +142,7 @@ impl PropertyEditable for Shape { edit("Normal Y", "shp_ny", self.normal.y), edit("Normal Z", "shp_nz", self.normal.z), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/solid.rs b/src/entities/solid.rs index d376b69e5..cac5f1012 100644 --- a/src/entities/solid.rs +++ b/src/entities/solid.rs @@ -97,8 +97,8 @@ impl Grippable for Solid { } impl PropertyEditable for Solid { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { - PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("P1 X", "sl_p1x", self.first_corner.x), @@ -115,7 +115,7 @@ impl PropertyEditable for Solid { edit("P4 Z", "sl_p4z", self.fourth_corner.z), edit("Thickness", "sl_thick", self.thickness), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/solid3d.rs b/src/entities/solid3d.rs index 23e455a29..bf6731a18 100644 --- a/src/entities/solid3d.rs +++ b/src/entities/solid3d.rs @@ -84,14 +84,14 @@ impl Grippable for Solid3D { } impl PropertyEditable for Solid3D { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let size = acis_size_str( self.acis_data.has_data(), self.acis_data.sat_data.len(), self.acis_data.sab_data.len(), self.acis_data.is_binary, ); - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro( @@ -133,7 +133,7 @@ impl PropertyEditable for Solid3D { }, ), ], - } + }] } fn apply_geom_prop(&mut self, _field: &str, _value: &str) {} @@ -160,14 +160,14 @@ impl Grippable for Region { } impl PropertyEditable for Region { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let size = acis_size_str( self.acis_data.has_data(), self.acis_data.sat_data.len(), self.acis_data.sab_data.len(), self.acis_data.is_binary, ); - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro( @@ -201,7 +201,7 @@ impl PropertyEditable for Region { self.silhouettes.len().to_string(), ), ], - } + }] } fn apply_geom_prop(&mut self, _field: &str, _value: &str) {} @@ -228,14 +228,14 @@ impl Grippable for Body { } impl PropertyEditable for Body { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let size = acis_size_str( self.acis_data.has_data(), self.acis_data.sat_data.len(), self.acis_data.sab_data.len(), self.acis_data.is_binary, ); - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro( @@ -269,7 +269,7 @@ impl PropertyEditable for Body { self.silhouettes.len().to_string(), ), ], - } + }] } fn apply_geom_prop(&mut self, _field: &str, _value: &str) {} diff --git a/src/entities/spline.rs b/src/entities/spline.rs index 83e529546..6bd77f67f 100644 --- a/src/entities/spline.rs +++ b/src/entities/spline.rs @@ -106,8 +106,8 @@ fn grips(spline: &Spline) -> Vec { .collect() } -fn properties(spline: &Spline) -> PropSection { - PropSection { +fn properties(spline: &Spline) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Degree", "degree", spline.degree.to_string()), @@ -118,7 +118,7 @@ fn properties(spline: &Spline) -> PropSection { ), ro("Fit Pts", "fit_pts", spline.fit_points.len().to_string()), ], - } + }] } fn apply_geom_prop(_spline: &mut Spline, _field: &str, _value: &str) {} @@ -257,7 +257,7 @@ impl crate::entities::traits::Grippable for Spline { } impl crate::entities::traits::PropertyEditable for Spline { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/table.rs b/src/entities/table.rs index 9fca6e4b8..22d7aca28 100644 --- a/src/entities/table.rs +++ b/src/entities/table.rs @@ -818,14 +818,14 @@ impl Grippable for Table { } impl PropertyEditable for Table { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let fmt_h = |oh: &Option| -> String { match oh { Some(h) if !h.is_null() => format!("{:X}", h.value()), _ => "(none)".to_string(), } }; - PropSection { + vec![PropSection { title: "Table".into(), props: vec![ ro("Rows", "tbl_rows", self.rows.len().to_string()), @@ -921,7 +921,7 @@ impl PropertyEditable for Table { ), ), ], - } + }] } fn apply_geom_prop(&mut self, _field: &str, _value: &str) {} diff --git a/src/entities/text.rs b/src/entities/text.rs index 86f833a1d..fe4b20595 100644 --- a/src/entities/text.rs +++ b/src/entities/text.rs @@ -169,8 +169,8 @@ fn grips(t: &Text) -> Vec { vec![square_grip(0, p)] } -fn properties(t: &Text, text_style_names: &[String]) -> PropSection { - PropSection { +fn properties(t: &Text, text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Insert X", "ins_x", t.insertion_point.x), @@ -224,7 +224,7 @@ fn properties(t: &Text, text_style_names: &[String]) -> PropSection { }, }, ], - } + }] } fn apply_geom_prop(t: &mut Text, field: &str, value: &str) { @@ -377,7 +377,7 @@ impl Grippable for Text { } impl PropertyEditable for Text { - fn geometry_properties(&self, text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, text_style_names: &[String]) -> Vec { properties(self, text_style_names) } diff --git a/src/entities/tolerance.rs b/src/entities/tolerance.rs index 14b6d953d..e1a76ae47 100644 --- a/src/entities/tolerance.rs +++ b/src/entities/tolerance.rs @@ -293,8 +293,8 @@ impl Grippable for Tolerance { // ── PropertyEditable ────────────────────────────────────────────────────────── impl PropertyEditable for Tolerance { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { - PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Text", "tol_text", self.text.clone()), @@ -337,7 +337,7 @@ impl PropertyEditable for Tolerance { ), ), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/traits.rs b/src/entities/traits.rs index b6820011c..7de68d596 100644 --- a/src/entities/traits.rs +++ b/src/entities/traits.rs @@ -60,7 +60,9 @@ pub trait Grippable { } pub trait PropertyEditable { - fn geometry_properties(&self, text_style_names: &[String]) -> PropSection; + /// Type-specific property groups (Geometry, Misc, Text, …) shown after the + /// common General group. May be several groups; empty means none. + fn geometry_properties(&self, text_style_names: &[String]) -> Vec; fn apply_geom_prop(&mut self, field: &str, value: &str); } @@ -147,7 +149,7 @@ pub trait EntityTypeOps { fn to_truck_entity(&self, document: &CadDocument) -> Option; fn grips(&self) -> Vec; fn grip_menu(&self, grip_id: usize) -> Vec; - fn geometry_properties(&self, text_style_names: &[String]) -> Option; + fn geometry_properties(&self, text_style_names: &[String]) -> Vec; fn apply_geom_prop(&mut self, field: &str, value: &str); fn apply_grip(&mut self, grip_id: usize, apply: GripApply); fn apply_grip_menu(&mut self, grip_id: usize, action: GripMenuAction); @@ -207,7 +209,7 @@ macro_rules! impl_entity_basics { fn geometry_properties( &self, _text_style_names: &[String], - ) -> $crate::scene::model::object::PropSection { + ) -> Vec<$crate::scene::model::object::PropSection> { properties(self) } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -244,7 +246,7 @@ macro_rules! impl_entity_basics_with_text_styles { fn geometry_properties( &self, text_style_names: &[String], - ) -> $crate::scene::model::object::PropSection { + ) -> Vec<$crate::scene::model::object::PropSection> { properties(self, text_style_names) } fn apply_geom_prop(&mut self, field: &str, value: &str) { @@ -291,9 +293,9 @@ impl EntityTypeOps for EntityType { ) } - fn geometry_properties(&self, text_style_names: &[String]) -> Option { + fn geometry_properties(&self, text_style_names: &[String]) -> Vec { dispatch!(self, - |e| Some(PropertyEditable::geometry_properties(e, text_style_names)), + |e| PropertyEditable::geometry_properties(e, text_style_names), [ Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D, Polyline3D, Ray, XLine, RasterImage, Wipeout, @@ -303,7 +305,7 @@ impl EntityTypeOps for EntityType { Viewport, Insert, Dimension, Leader, MultiLeader, Underlay, Shape, Ole2Frame, ], - _ => None, + _ => vec![], ) } diff --git a/src/entities/underlay.rs b/src/entities/underlay.rs index 2c1652a4a..7529e9651 100644 --- a/src/entities/underlay.rs +++ b/src/entities/underlay.rs @@ -146,13 +146,13 @@ impl Grippable for Underlay { // ── PropertyEditable ────────────────────────────────────────────────────────── impl PropertyEditable for Underlay { - fn geometry_properties(&self, _text_style_names: &[String]) -> PropSection { + fn geometry_properties(&self, _text_style_names: &[String]) -> Vec { let type_str = match self.underlay_type { acadrust::entities::UnderlayType::Pdf => "PDF", acadrust::entities::UnderlayType::Dwf => "DWF", acadrust::entities::UnderlayType::Dgn => "DGN", }; - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ ro("Type", "ul_type", type_str), @@ -198,7 +198,7 @@ impl PropertyEditable for Underlay { if self.clip_inverted { "Yes" } else { "No" }, ), ], - } + }] } fn apply_geom_prop(&mut self, field: &str, value: &str) { diff --git a/src/entities/viewport.rs b/src/entities/viewport.rs index a98530c9d..edeff1120 100644 --- a/src/entities/viewport.rs +++ b/src/entities/viewport.rs @@ -116,7 +116,7 @@ fn grips(vp: &Viewport) -> Vec { ] } -fn properties(vp: &Viewport) -> PropSection { +fn properties(vp: &Viewport) -> Vec { let scale_opts: Vec = STANDARD_SCALES.iter().map(|(s, _)| s.to_string()).collect(); let effective_scale = crate::scene::vp_effective_scale(vp.custom_scale, vp.view_height, vp.height); @@ -131,7 +131,7 @@ fn properties(vp: &Viewport) -> PropSection { let shade_opts: Vec = SHADE_PLOT_LABELS.iter().map(|s| s.to_string()).collect(); let current_shade = shade_plot_label(vp.shade_plot_mode).to_string(); - PropSection { + vec![PropSection { title: "Geometry".into(), props: vec![ edit("Center X", "center_x", vp.center.x), @@ -311,7 +311,7 @@ fn properties(vp: &Viewport) -> PropSection { format!("{:#010x}", vp.ambient_color as u32), ), ], - } + }] } /// Identify which standard view matches the viewport's view direction. diff --git a/src/scene/selection.rs b/src/scene/selection.rs index 9c5ae0fc0..b5c3b6730 100644 --- a/src/scene/selection.rs +++ b/src/scene/selection.rs @@ -246,7 +246,7 @@ impl Scene { .filter_map(|h| self.document.get_entity(h)) .find(|e| entity_type_name(e) == t); if let Some(sample) = sample { - if let Some(section) = sample.geometry_properties(&text_style_names) { + for section in sample.geometry_properties(&text_style_names) { for prop in section.props { // Skip rows that don't sensibly compare via // `entity_property_value` (read-only labels are @@ -284,8 +284,11 @@ impl Scene { .iter() .map(|s| s.name.clone()) .collect(); - let section = entity.geometry_properties(&text_style_names)?; - let prop = section.props.into_iter().find(|p| p.field == field)?; + let prop = entity + .geometry_properties(&text_style_names) + .into_iter() + .flat_map(|s| s.props) + .find(|p| p.field == field)?; Some(match prop.value { PropValue::ReadOnly(s) | PropValue::EditText(s) => s, PropValue::LayerChoice(s) => s, diff --git a/src/scene/view/dispatch.rs b/src/scene/view/dispatch.rs index 29646ffdf..15eacd34b 100644 --- a/src/scene/view/dispatch.rs +++ b/src/scene/view/dispatch.rs @@ -17,11 +17,14 @@ pub fn properties_sectioned( entity: &EntityType, text_style_names: &[String], ) -> Vec { - let general = properties::general_section(entity); - let geometry = entity - .geometry_properties(text_style_names) - .unwrap_or_else(|| properties::fallback_properties(handle, entity)); - vec![general, geometry] + let mut sections = vec![properties::general_section(entity)]; + let groups = entity.geometry_properties(text_style_names); + if groups.is_empty() { + sections.push(properties::fallback_properties(handle, entity)); + } else { + sections.extend(groups); + } + sections } pub fn apply_common_prop(entity: &mut EntityType, field: &str, value: &str) {