From d4bacce8853f53b8e550b2e40f0267cb4e995cd3 Mon Sep 17 00:00:00 2001 From: Marco Arnulfo <103679419+marcoarnulfo@users.noreply.github.com> Date: Mon, 19 Oct 2026 08:29:54 +0200 Subject: [PATCH] Tool Sets: the Parametric category offers Fix, Symmetric, the dimensional constraints and Delete Constraints (part of #1134) (#1138) --- crates/ui-egui/src/icons.rs | 33 +++++++++++ crates/ui-egui/src/palettes.rs | 15 ++++- crates/ui-egui/tests/toolsets_parametric.rs | 61 +++++++++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 crates/ui-egui/tests/toolsets_parametric.rs diff --git a/crates/ui-egui/src/icons.rs b/crates/ui-egui/src/icons.rs index e0e238900..df2e31184 100644 --- a/crates/ui-egui/src/icons.rs +++ b/crates/ui-egui/src/icons.rs @@ -137,6 +137,11 @@ pub enum Icon { Concentric, Equal, Fix, + Collinear, + Smooth, + Symmetric, + DelConstraint, + Parameters, Measure, Distance, Area, @@ -824,6 +829,34 @@ pub fn paint(p: &Painter, rect: Rect, icon: Icon, dim: bool) { pen.fill_rect(7.0, 11.0, 10.0, 9.0, Color32::from_rgb(0xe6, 0xb4, 0x3c)); pen.arc(12.0, 11.0, 4.0, 0.0, 180.0, Color32::from_rgb(0xe6, 0xb4, 0x3c)); } + Collinear => { + pen.l(3.0, 19.0, 9.0, 15.0); + pen.la(12.0, 13.0, 21.0, 7.0); + } + Smooth => { + // A line running into an arc without a corner. + pen.l(3.0, 18.0, 11.0, 18.0); + pen.arc(11.0, 10.0, 8.0, -90.0, 0.0, a); + pen.dot(11.0, 18.0); + } + Symmetric => { + pen.poly(&[(9.0, 5.0), (4.0, 12.0), (9.0, 19.0)], false, b); + pen.poly(&[(15.0, 5.0), (20.0, 12.0), (15.0, 19.0)], false, b); + for y in [3.0, 9.0, 15.0] { + pen.la(12.0, y, 12.0, y + 4.0); + } + } + DelConstraint => { + let lock = alpha(Color32::from_rgb(0xe6, 0xb4, 0x3c)); + pen.fill_rect(3.0, 12.0, 9.0, 8.0, lock); + pen.arc(7.5, 12.0, 3.5, 0.0, 180.0, lock); + pen.l(15.0, 13.0, 21.0, 19.0); + pen.l(21.0, 13.0, 15.0, 19.0); + } + Parameters => { + pen.rect(3.0, 4.0, 18.0, 16.0, b); + pen.text(12.0, 12.0, "fx", 10.0, a); + } Coincident => { pen.l(3.0, 20.0, 12.0, 12.0); pen.l(12.0, 12.0, 21.0, 18.0); diff --git a/crates/ui-egui/src/palettes.rs b/crates/ui-egui/src/palettes.rs index ca5cbe499..ca119e592 100644 --- a/crates/ui-egui/src/palettes.rs +++ b/crates/ui-egui/src/palettes.rs @@ -106,7 +106,11 @@ pub const DRAFTING: &[(&str, &[Tool], &[Tool])] = &[ ("Table", &[(Icon::Table, "table", "Table")], &[]), ( "Parametric", - &[(Icon::Constraint, "autoconstrain", "Auto Constrain")], + &[ + (Icon::Constraint, "autoconstrain", "Auto Constrain"), + (Icon::DelConstraint, "delconstraint", "Delete Constraints"), + (Icon::Parameters, "parameters", "Parameters Manager"), + ], &[ (Icon::Coincident, "gccoincident", "Coincident"), (Icon::Parallel, "gcparallel", "Parallel"), @@ -116,6 +120,15 @@ pub const DRAFTING: &[(&str, &[Tool], &[Tool])] = &[ (Icon::Tangent, "gctangent", "Tangent"), (Icon::Concentric, "gcconcentric", "Concentric"), (Icon::Equal, "gcequal", "Equal"), + (Icon::Collinear, "gccollinear", "Collinear"), + (Icon::Smooth, "gcsmooth", "Smooth"), + (Icon::Symmetric, "gcsymmetric", "Symmetric"), + (Icon::Fix, "gcfix", "Fix"), + (Icon::DimLinear, "dclinear", "Linear"), + (Icon::DimAligned, "dcaligned", "Aligned"), + (Icon::DimAngular, "dcangular", "Angular"), + (Icon::DimRadius, "dcradius", "Radius"), + (Icon::DimDiameter, "dcdiameter", "Diameter"), ], ), ]; diff --git a/crates/ui-egui/tests/toolsets_parametric.rs b/crates/ui-egui/tests/toolsets_parametric.rs new file mode 100644 index 000000000..59f4118a3 --- /dev/null +++ b/crates/ui-egui/tests/toolsets_parametric.rs @@ -0,0 +1,61 @@ +//! The Parametric category of the Tool Sets offers the constraint tools that only Tools › +//! Parametric used to reach: Fix, Symmetric, Delete Constraints and the rest (#1134). + +use cadcraft_engine::Session; +use cadcraft_ui_egui::{CadApp, Services}; +use egui::accesskit::Role; +use egui::vec2; +use egui_kittest::Harness; +use egui_kittest::kittest::{NodeT, Queryable}; + +fn harness() -> Harness<'static, CadApp> { + let mut app = CadApp::new(Session::default(), Services::default()); + app.ui.in_window_menu = false; + app.integrated_titlebar = true; + // Tall enough that the Parametric category, last in Drafting, is on screen. + let mut harness = Harness::builder().with_size(vec2(1280.0, 1600.0)).build_ui_state( + |ui, app: &mut CadApp| { + app.logic(&ui.ctx().clone()); + app.ui(ui); + }, + app, + ); + harness.run_steps(5); + harness +} + +#[test] +fn the_parametric_category_lists_fix_symmetric_and_delete_constraints() { + let parametric = cadcraft_ui_egui::palettes::DRAFTING.iter().find(|(name, _, _)| *name == "Parametric").unwrap(); + let ids: Vec<&str> = parametric.1.iter().chain(parametric.2).map(|(_, cmd, _)| *cmd).collect(); + for id in [ + "gcfix", + "gcsymmetric", + "gccollinear", + "gcsmooth", + "dclinear", + "dcaligned", + "dcangular", + "dcradius", + "dcdiameter", + "delconstraint", + "parameters", + ] { + assert!(ids.contains(&id), "{id} is missing from the Parametric tool set: {ids:?}"); + } + + let harness = harness(); + for label in ["Fix", "Symmetric", "Delete Constraints"] { + let button = harness.get_by_role_and_label(Role::Button, label); + assert!(!button.accesskit_node().is_disabled(), "{label} is a known command, so its button is enabled"); + } +} + +#[test] +fn clicking_fix_in_the_tool_set_starts_gcfix() { + let mut harness = harness(); + assert!(harness.state().session.running.is_none()); + harness.get_by_role_and_label(Role::Button, "Fix").click(); + harness.run_steps(5); + assert_eq!(harness.state().session.running.as_ref().map(|r| r.id.as_str()), Some("gcfix")); +}