forked from 1b-agent/opencadstudio-upstream
Add per-constraint delete; fix named-parameter reference bugs
Constraint delete: a Properties-panel row delete button, viewport
click-to-select on a constraint glyph pill with Delete-key support, and a
right-click "Delete Constraint" menu item — all routed through one
undoable Scene::selected_constraint-aware path.
Fixes two named-parameter bugs found while testing that feature:
- DIMCONSTRAINT: a parameter named like a mode-switch keyword ("r", "d",
"a", "x", "y") could never be referenced, since the keyword check ran
before the parameter-name check.
- PARAMETERS modal Apply rebuilt Scene::named_parameters but never
refreshed the Properties panel, leaving renamed/deleted rows stale
until an unrelated refresh (e.g. a selection change) happened to occur.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
753df67ee4
commit
59ae38ae1a
14 files changed
+370
-77
No files matched your search
+13
-63
@@ -817,72 +817,21 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
let point_cursor = tab.active_cmd.as_ref().is_some_and(|cmd| {
|
||||
!cmd.needs_entity_pick() && !cmd.is_selection_gathering()
|
||||
});
|
||||
// Constraint glyphs are displayed in model space only.
|
||||
let constraint_glyphs: Vec<(iced::Point, [f32; 2], String, bool)> = if is_paper {
|
||||
// Persistent sketch-constraint glyphs (design doc §6.3/§7) — model
|
||||
// space only, like the constraints themselves; paper space shows
|
||||
// none (out of scope per the design doc §3.1).
|
||||
let constraint_glyphs: Vec<(iced::Point, [f32; 2], String, bool, bool)> = if is_paper {
|
||||
Vec::new()
|
||||
} else {
|
||||
let (vw, vh) = sel_ref.vp_size;
|
||||
let scope = tab.current_sketch_scope();
|
||||
match tab.scene.sketch_constraint_set(scope) {
|
||||
Some(set) if !set.constraints.is_empty() => {
|
||||
let edit_frame = tab.scene.viewport_edit_frame((vw, vh));
|
||||
let bounds = match &edit_frame {
|
||||
Some((_, full)) => *full,
|
||||
None => tab.scene.active_model_tile_bounds(vw, vh),
|
||||
};
|
||||
let (view_rot, eye) = if let Some((cam, _)) = &edit_frame {
|
||||
(cam.view_proj_rte(bounds), cam.eye())
|
||||
} else {
|
||||
let cam = tab.scene.camera.borrow();
|
||||
(cam.view_proj_rte(bounds), cam.eye())
|
||||
};
|
||||
set.constraints
|
||||
.iter()
|
||||
.filter(|c| c.enabled)
|
||||
.filter(|c| tab.scene.is_sketch_constraint_visible(scope, c.id))
|
||||
.filter_map(|c| {
|
||||
let (anchor, outward) =
|
||||
crate::scene::sketch_constraints::glyph_placement(
|
||||
&tab.scene.document,
|
||||
c,
|
||||
)?;
|
||||
let screen = crate::scene::pick::grip::project_rte(
|
||||
glam::DVec3::new(anchor.x, anchor.y, anchor.z),
|
||||
view_rot,
|
||||
eye,
|
||||
bounds,
|
||||
)?;
|
||||
let outward_screen = crate::scene::pick::grip::project_rte(
|
||||
glam::DVec3::new(
|
||||
anchor.x + outward.x,
|
||||
anchor.y + outward.y,
|
||||
anchor.z + outward.z,
|
||||
),
|
||||
view_rot,
|
||||
eye,
|
||||
bounds,
|
||||
)?;
|
||||
let direction = (outward_screen - screen)
|
||||
.normalize_or(glam::Vec2::NEG_Y);
|
||||
let point = iced::Point::new(
|
||||
bounds.x + screen.x,
|
||||
bounds.y + screen.y,
|
||||
);
|
||||
let is_conflicting =
|
||||
set.conflicts.iter().any(|(id, _)| *id == c.id);
|
||||
let label = if self.show_constraint_values {
|
||||
crate::scene::sketch_constraints::glyph_label(c)
|
||||
} else {
|
||||
c.kind.glyph_symbol().to_string()
|
||||
};
|
||||
point.x.is_finite().then(|| {
|
||||
(point, direction.to_array(), label, is_conflicting)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
tab.scene
|
||||
.constraint_glyph_placements_screen(scope, sel_ref.vp_size, self.show_constraint_values)
|
||||
.into_iter()
|
||||
.map(|(id, point, direction, label, is_conflicting)| {
|
||||
let selected = tab.scene.selected_constraint == Some(id);
|
||||
(point, direction, label, is_conflicting, selected)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
crate::ui::overlay::selection_overlay(
|
||||
std::sync::Arc::clone(&tab.scene.selection),
|
||||
@@ -1811,6 +1760,7 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
command_line_inset,
|
||||
has_cmd,
|
||||
has_selection,
|
||||
tab.scene.selected_constraint,
|
||||
isolation_active,
|
||||
last_cmds,
|
||||
draworder_open,
|
||||
|
||||
@@ -1114,6 +1114,7 @@ pub(super) fn viewport_context_menu_overlay(
|
||||
bottom_inset: f32,
|
||||
has_cmd: bool,
|
||||
has_selection: bool,
|
||||
selected_constraint: Option<crate::scene::sketch_constraints::ConstraintId>,
|
||||
isolation_active: bool,
|
||||
last_cmds: Vec<String>,
|
||||
draworder_open: bool,
|
||||
@@ -1250,6 +1251,15 @@ pub(super) fn viewport_context_menu_overlay(
|
||||
t!("Invert Selection").into_owned(),
|
||||
Message::InvertSelection,
|
||||
));
|
||||
} else if let Some(id) = selected_constraint {
|
||||
// A selected constraint-glyph pill gets its own minimal menu —
|
||||
// none of the entity-selection actions above (Move/Copy/Isolate/
|
||||
// Select Similar) apply to it.
|
||||
items.push(item(
|
||||
t!("Delete Constraint").into_owned(),
|
||||
Message::PropConstraintDelete(id),
|
||||
));
|
||||
items.push(sep());
|
||||
}
|
||||
if isolation_active {
|
||||
items.push(item(
|
||||
|
||||
Reference in new issue
Block a user