diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index 168d90be6..df2f1db17 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -1685,10 +1685,15 @@ impl OpenCADStudio { let mut touched = Vec::new(); for (kind, refs) in inferred { touched.extend(refs.iter().map(|reference| reference.entity)); - self.tabs[i] + let id = self.tabs[i] .scene .parametric_constraint_set_mut(scope) .add(kind, refs, None); + self.tabs[i].scene.note_parametric_constraint_applied( + scope, + id, + self.constraint_bar_display, + ); } touched.sort_unstable_by_key(|handle| handle.value()); touched.dedup(); @@ -3051,11 +3056,16 @@ impl OpenCADStudio { .scene .record_undo_parametric_constraints_before(scope, constraints_before); let retain_size = self.constraint_solve_mode && driving_param.is_none(); - self.tabs[i].scene.parametric_constraint_set_mut(scope).add( + let id = self.tabs[i].scene.parametric_constraint_set_mut(scope).add( kind, refs, driving_param, ); + self.tabs[i].scene.note_parametric_constraint_applied( + scope, + id, + self.constraint_bar_display, + ); let changes: Vec<(Handle, crate::scene::ChangeKind)> = touched .into_iter() .map(|h| (h, crate::scene::ChangeKind::Modified)) @@ -3156,10 +3166,15 @@ impl OpenCADStudio { self.tabs[i] .scene .record_undo_parametric_constraints_before(scope, constraints_before); - self.tabs[i] + let id = self.tabs[i] .scene .parametric_constraint_set_mut(scope) .add(kind, refs, None); + self.tabs[i].scene.note_parametric_constraint_applied( + scope, + id, + self.constraint_bar_display, + ); let changes: Vec<_> = touched .iter() .copied() @@ -3215,10 +3230,15 @@ impl OpenCADStudio { .scene .record_undo_parametric_constraints_before(scope, constraints_before); for refs in inferred { - self.tabs[i] + let id = self.tabs[i] .scene .parametric_constraint_set_mut(scope) .add(ConstraintKind::Coincident, refs, None); + self.tabs[i].scene.note_parametric_constraint_applied( + scope, + id, + self.constraint_bar_display, + ); } let changes: Vec<_> = handles .iter() diff --git a/src/app/commands/draw.rs b/src/app/commands/draw.rs index d021b1d6b..1fb484a01 100644 --- a/src/app/commands/draw.rs +++ b/src/app/commands/draw.rs @@ -1165,10 +1165,15 @@ impl OpenCADStudio { .record_undo_parametric_constraints_before(scope, before); let count = inferred.len(); for (kind, refs) in inferred { - self.tabs[i] + let id = self.tabs[i] .scene .parametric_constraint_set_mut(scope) .add(kind, refs, None); + self.tabs[i].scene.note_parametric_constraint_applied( + scope, + id, + self.constraint_bar_display, + ); } let changes: Vec<_> = handles .iter() @@ -1281,11 +1286,16 @@ impl OpenCADStudio { .scene .record_undo_parametric_constraints_before(scope, before); for (_, kind, refs, value) in conversions { - self.tabs[i].scene.parametric_constraint_set_mut(scope).add( + let id = self.tabs[i].scene.parametric_constraint_set_mut(scope).add( kind, refs, Some(value), ); + self.tabs[i].scene.note_parametric_constraint_applied( + scope, + id, + self.constraint_bar_display, + ); } self.tabs[i].scene.erase_entities(&dimensions); let changes: Vec<_> = touched diff --git a/src/scene/parametric_constraints.rs b/src/scene/parametric_constraints.rs index 9c7fe40cb..d031179fa 100644 --- a/src/scene/parametric_constraints.rs +++ b/src/scene/parametric_constraints.rs @@ -871,10 +871,28 @@ impl super::Scene { ) -> bool { self.is_parametric_constraint_visible(scope, id) && (self.shown_parametric_constraints.contains(&(scope, id)) - || display_mode & 1 != 0 || (display_mode & 2 != 0 && related_entity_selected)) } + /// Record the session-only display state for a constraint that was just + /// applied. `CONSTRAINTBARDISPLAY` bit 1 applies only at creation time; it + /// must not make every constraint loaded from a drawing permanently + /// visible. Bit 2 is evaluated independently from the current selection in + /// [`should_display_parametric_constraint`](Self::should_display_parametric_constraint). + pub fn note_parametric_constraint_applied( + &mut self, + scope: ParametricScope, + id: ConstraintId, + display_mode: i16, + ) { + self.hidden_parametric_constraints.remove(&(scope, id)); + if display_mode & 1 != 0 { + self.shown_parametric_constraints.insert((scope, id)); + } else { + self.shown_parametric_constraints.remove(&(scope, id)); + } + } + pub fn set_parametric_constraint_visibility( &mut self, scope: ParametricScope, diff --git a/src/ui/overlay.rs b/src/ui/overlay.rs index 001dc39d5..83b1c276f 100644 --- a/src/ui/overlay.rs +++ b/src/ui/overlay.rs @@ -28,8 +28,16 @@ const CONSTRAINT_GLYPH_PAD_Y: f32 = 4.0; const CONSTRAINT_GLYPH_GAP: f32 = 6.0; const CONSTRAINT_GLYPH_ROW_GAP: f32 = 4.0; const CONSTRAINT_HOVER_MARKER_RADIUS: f32 = 7.0; +const COINCIDENT_GLYPH_SIZE: f32 = 9.0; + +fn is_compact_coincident_glyph(label: &str) -> bool { + matches!(label, "≡" | "∈") +} fn constraint_glyph_size(label: &str) -> Size { + if is_compact_coincident_glyph(label) { + return Size::new(COINCIDENT_GLYPH_SIZE, COINCIDENT_GLYPH_SIZE); + } let w = label.chars().count() as f32 * CONSTRAINT_GLYPH_SIZE * 0.62 + CONSTRAINT_GLYPH_PAD_X * 2.0; let h = CONSTRAINT_GLYPH_SIZE + CONSTRAINT_GLYPH_PAD_Y * 2.0; @@ -59,9 +67,14 @@ fn constraint_glyph_box( tangent_offset: f32, ) -> (Point, Size) { let size = constraint_glyph_size(label); + let gap = if is_compact_coincident_glyph(label) { + 1.0 + } else { + CONSTRAINT_GLYPH_GAP + }; let distance = outward[0].abs() * size.width * 0.5 + outward[1].abs() * size.height * 0.5 - + CONSTRAINT_GLYPH_GAP; + + gap; let tangent = [-outward[1], outward[0]]; ( Point::new( @@ -1785,19 +1798,32 @@ impl canvas::Program for SelectionCanvas { let conflict_bg = theme.palette().danger.base.color; let conflict_fg = theme.palette().danger.base.text; let selected_ring = theme.palette().primary.strong.color; + let coincident_bg = Color::from_rgb8(35, 145, 230); for ((anchor, outward, label, is_conflicting, is_selected, _), tangent_offset) in self.constraint_glyphs.iter().zip(offsets) { if !anchor.x.is_finite() || !anchor.y.is_finite() { continue; } - let (bg, fg) = if *is_conflicting { (conflict_bg, conflict_fg) } else { (normal_bg, normal_fg) }; + let compact_coincident = is_compact_coincident_glyph(label); + let (bg, fg) = if *is_conflicting { + (conflict_bg, conflict_fg) + } else if compact_coincident { + (coincident_bg, normal_fg) + } else { + (normal_bg, normal_fg) + }; let (top_left, size) = constraint_glyph_box(*anchor, *outward, label, tangent_offset); let pill = canvas::Path::rounded_rectangle( top_left, size, - (size.height * 0.5).into(), + (if compact_coincident { + 1.0 + } else { + size.height * 0.5 + }) + .into(), ); frame.fill(&pill, bg); if *is_selected { @@ -1806,23 +1832,25 @@ impl canvas::Program for SelectionCanvas { canvas::Stroke::default().with_color(selected_ring).with_width(2.0), ); } - let glyph_center = Point::new( - top_left.x + size.width * 0.5, - top_left.y + size.height * 0.5, - ); - if label == "T" { - draw_tangent_constraint_glyph(&mut frame, glyph_center, fg); - } else { - frame.fill_text(canvas::Text { - content: label.clone(), - position: glyph_center, - color: fg, - size: iced::Pixels(CONSTRAINT_GLYPH_SIZE), - align_x: iced::alignment::Horizontal::Center.into(), - align_y: iced::alignment::Vertical::Center, - shaping: iced::advanced::text::Shaping::Advanced, - ..Default::default() - }); + if !compact_coincident { + let glyph_center = Point::new( + top_left.x + size.width * 0.5, + top_left.y + size.height * 0.5, + ); + if label == "T" { + draw_tangent_constraint_glyph(&mut frame, glyph_center, fg); + } else { + frame.fill_text(canvas::Text { + content: label.clone(), + position: glyph_center, + color: fg, + size: iced::Pixels(CONSTRAINT_GLYPH_SIZE), + align_x: iced::alignment::Horizontal::Center.into(), + align_y: iced::alignment::Vertical::Center, + shaping: iced::advanced::text::Shaping::Advanced, + ..Default::default() + }); + } } } if let Some(index) = hovered {