Add Properties-panel Constraints/Parameters sections, a constraint-pill

value toggle, and redesigned constraint-ribbon icons

Properties panel:
- New "Parameters" section (shown with no selection) listing every named
  parameter with inline name/formula editing, add, and delete, backed by
  new PropValue::ParamRow/ParamAddRow and command_driver handlers
  (resolve_named_parameter_edit, on_prop_param_commit/_delete/_add_new).
  A fresh parameter's formula now defaults to "1", not "0" — a 0-length
  seed value is a genuine solver singularity for a Distance constraint
  driven by that parameter, with no defined direction to grow back out of.
- New "Constraints" section (shown for a single selected entity) listing
  every constraint touching it as a clickable PropValue::EntityLink that
  selects the linked entities in the viewport, danger-tinted when the
  constraint conflicts.

Constraint pills:
- New Options > General toggle, "Show values and parameter names on
  constraint markers" (on by default, persisted). Off, every pill shows
  just its bare glyph instead of glyph+driven-value/parameter-name — the
  glyph alone is enough to see a constraint is present, without the value
  text covering nearby geometry on a dense sketch.

Constraint-ribbon icons:
- Replaced all 18 unicode/letter glyph icons (including the plain "F",
  "L", "M", "S" letters) with proper SVGs under assets/icons/constrain/,
  matching the Modify section's two-color style (#B4B6B9 white / #6DB7ED
  blue, consistent stroke width). Tangent reuses the Draw section's
  white-arc-plus-blue-dots motif.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Felix RiestraandClaude Sonnet 5 committed 2026-09-10 23:16:11 +02:00
1 parent faf3473b2b
commit bd9a2d08f7
36 files changed
+707 -26

No files matched your search

+88
View File
@@ -406,6 +406,38 @@ impl OpenCADStudio {
read_only(t!("Visual Style").as_ref(), tab.visual_style.clone()),
],
},
// Document-wide named-parameter table (AutoCAD
// Parameters Manager equivalent), embedded here
// instead of a separate modal — see `PropValue::
// ParamRow`'s doc comment. Belongs on the no-
// selection (drawing-level) page, not a per-entity
// one: a parameter isn't owned by any one entity.
PropSection {
title: t!("Parameters").into_owned(),
props: {
let mut props: Vec<Property> = scene
.named_parameters()
.iter()
.enumerate()
.map(|(index, p)| Property {
label: String::new(),
field: "named_parameter",
value: PropValue::ParamRow {
index,
name: p.name.clone(),
formula: p.source.clone(),
resolved: scene.named_parameters().resolve(&p.name).map_err(|e| e.to_string()),
},
})
.collect();
props.push(Property {
label: String::new(),
field: "named_parameter_add",
value: PropValue::ParamAddRow,
});
props
},
},
];
ui::PropertiesPanel {
title: t!("No selection").into_owned(),
@@ -2126,6 +2158,53 @@ impl OpenCADStudio {
if compact_solid {
retain_compact_solid_sections(&mut sections);
}
// Persistent sketch-constraint list for this entity —
// the Properties-panel analogue of the viewport's
// constraint glyph pills: one row per constraint
// touching `handle`, clickable to select every entity
// it relates (PropConstraintLinkClick). Omitted
// entirely when the entity has none — an unconstrained
// entity (the common case) shouldn't gain empty
// bookkeeping.
{
let scope = self.tabs[i].current_sketch_scope();
let scene = &self.tabs[i].scene;
if let Some(set) = scene.sketch_constraint_set(scope) {
let props: Vec<crate::scene::model::object::Property> = set
.constraints_touching(handle)
.map(|c| {
let conflicting = set.conflicts.iter().any(|(id, _)| *id == c.id);
let value_suffix = match &c.driving_param {
Some(crate::scene::named_parameters::DrivingValue::Literal(v)) => {
format!(" = {v:.2}")
}
Some(crate::scene::named_parameters::DrivingValue::Named(name)) => {
match scene.named_parameters().resolve(name) {
Ok(v) => format!(" = {name} ({v:.2})"),
Err(_) => format!(" = {name} (?)"),
}
}
None => String::new(),
};
let label = format!("{} {:?}{}", c.kind.glyph_symbol(), c.kind, value_suffix);
let mut handles: Vec<acadrust::Handle> = c.refs.iter().map(|r| r.entity).collect();
handles.sort_by_key(|h| h.value());
handles.dedup();
crate::scene::model::object::Property {
label,
field: "sketch_constraint",
value: crate::scene::model::object::PropValue::EntityLink { handles, conflicting },
}
})
.collect();
if !props.is_empty() {
sections.push(crate::scene::model::object::PropSection {
title: t!("Constraints").into_owned(),
props,
});
}
}
}
let title = match entity {
acadrust::EntityType::Insert(ins) => {
let is_xref = self.tabs[i]
@@ -3102,6 +3181,15 @@ fn make_sections_read_only(
}
PropValue::Stepper { display, .. } => display.clone(),
PropValue::AttrText { value, .. } => value.clone(),
// The Constraints/Parameters sections aren't per-entity editable
// fields to begin with (a constraint link is a navigation
// action, not a value; a parameter belongs to the whole
// document, not the locked entity) — locking the entity doesn't
// change either, so just carry through readable text instead of
// collapsing them to a value that was never editable anyway.
PropValue::EntityLink { handles, .. } => format!("{} entity link(s)", handles.len()),
PropValue::ParamRow { name, formula, .. } => format!("{name} = {formula}"),
PropValue::ParamAddRow => String::new(),
};
property.field = "locked_read_only";
property.value = PropValue::ReadOnly(text);