diff --git a/Cargo.lock b/Cargo.lock index f6a9c5ea..78a789a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3269,7 +3269,7 @@ dependencies = [ [[package]] name = "solvecraft" -version = "0.2.1" +version = "0.3.0" dependencies = [ "eframe", "egui", @@ -3284,7 +3284,7 @@ dependencies = [ [[package]] name = "solvecraft-cli" -version = "0.2.1" +version = "0.3.0" dependencies = [ "serde_json", "solvecraft-engine", @@ -3293,7 +3293,7 @@ dependencies = [ [[package]] name = "solvecraft-doc" -version = "0.2.1" +version = "0.3.0" dependencies = [ "log", "serde", @@ -3308,7 +3308,7 @@ dependencies = [ [[package]] name = "solvecraft-engine" -version = "0.2.1" +version = "0.3.0" dependencies = [ "log", "serde", @@ -3324,7 +3324,7 @@ dependencies = [ [[package]] name = "solvecraft-geom" -version = "0.2.1" +version = "0.3.0" dependencies = [ "serde", "serde_json", @@ -3332,7 +3332,7 @@ dependencies = [ [[package]] name = "solvecraft-io" -version = "0.2.1" +version = "0.3.0" dependencies = [ "quick-xml 0.38.4", "solvecraft-doc", @@ -3344,7 +3344,7 @@ dependencies = [ [[package]] name = "solvecraft-kernel" -version = "0.2.1" +version = "0.3.0" dependencies = [ "log", "serde", @@ -3359,7 +3359,7 @@ dependencies = [ [[package]] name = "solvecraft-mcp" -version = "0.2.1" +version = "0.3.0" dependencies = [ "serde_json", "solvecraft-engine", @@ -3367,7 +3367,7 @@ dependencies = [ [[package]] name = "solvecraft-render" -version = "0.2.1" +version = "0.3.0" dependencies = [ "image", "serde", @@ -3377,7 +3377,7 @@ dependencies = [ [[package]] name = "solvecraft-sketch" -version = "0.2.1" +version = "0.3.0" dependencies = [ "serde", "serde_json", @@ -3388,7 +3388,7 @@ dependencies = [ [[package]] name = "solvecraft-ui-egui" -version = "0.2.1" +version = "0.3.0" dependencies = [ "egui", "egui-wgpu", @@ -3403,7 +3403,7 @@ dependencies = [ [[package]] name = "solvecraft-web" -version = "0.2.1" +version = "0.3.0" dependencies = [ "eframe", "egui", @@ -4775,7 +4775,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" [[package]] name = "xtask" -version = "0.2.1" +version = "0.3.0" dependencies = [ "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 3cfba562..8650aaf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"] exclude = ["vendor"] [workspace.package] -version = "0.2.1" +version = "0.3.0" edition = "2024" license = "MIT OR Apache-2.0" rust-version = "1.95" diff --git a/ROADMAP.md b/ROADMAP.md index 2deb5c5d..b019b0ee 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -250,6 +250,9 @@ headline counts in-scope tabs only and lists the deferred ones separately. record their coordinate frame; legacy wires migrate from known provenance without changing coordinates. Broken legacy links with missing provenance can require explicit refresh or relink. See [occurrence workflows](docs/occurrence-workflows.md). +- Parameters and Configurations have distinct actions in the SOLID toolbar's CONFIGURE panel. + Parameters opens the user/model variable editor; Configurations opens the configuration table. + Configure remains available in the panel dropdown. - Configuration rules: ordered typed conditions can drive parameters and suppressions or restrict values. Preview explains effective values without modifying the document; applying rules validates rows atomically. Length expressions retain their authoring units across diff --git a/crates/ui-egui/src/icons.rs b/crates/ui-egui/src/icons.rs index 01b93641..b1034fc5 100644 --- a/crates/ui-egui/src/icons.rs +++ b/crates/ui-egui/src/icons.rs @@ -240,6 +240,12 @@ pub fn paint(p: &Painter, r: Rect, name: &str, ink: Color32, fill: Color32, acce pen.arrow((12.0, 12.0), (12.0 + 9.0 * dx, 12.0 + 9.0 * dy), a); } } + "configuration_table" => { + pen.poly(&[(3.0, 4.0), (21.0, 4.0), (21.0, 20.0), (3.0, 20.0)], pen.fill, ink); + pen.line(&[(3.0, 9.0), (21.0, 9.0)], a); + pen.line(&[(3.0, 14.0), (21.0, 14.0)], ink); + pen.line(&[(9.0, 4.0), (9.0, 20.0)], ink); + } "params" => { pen.line(&[(8.0, 20.0), (9.0, 8.0), (10.0, 5.0), (13.0, 4.0)], ink); pen.line(&[(6.0, 11.0), (12.0, 11.0)], ink); diff --git a/crates/ui-egui/src/toolbar.rs b/crates/ui-egui/src/toolbar.rs index 3585bbfc..e1857258 100644 --- a/crates/ui-egui/src/toolbar.rs +++ b/crates/ui-egui/src/toolbar.rs @@ -481,9 +481,19 @@ pub fn toolbar(app: &mut SolveApp, ui: &mut egui::Ui) { } else { (t.border, t.panel_header, t.border) }; - let icon = if c.id == "sketch.finish" { "finish" } else { c.icon }; + let icon = match c.id { + "sketch.finish" => "finish", + "config.table" => "configuration_table", + _ => c.icon, + }; icons::paint(&painter, br.shrink(6.0), icon, ink, fill, acc); - let tr_label = app.tr_label(c.label); + let label = match c.id { + "parameters.change" if *panel == "CONFIGURE" => "Parameters", + "config.table" if *panel == "CONFIGURE" => "Configurations", + _ => c.label, + }; + let tr_label = app.tr_label(label); + resp.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, ui.is_enabled() && info.enabled, tr_label)); let tip = match &info.disabled_reason { Some(why) => format!("{}\n{why}", tr_label), None => format!("{}{}", tr_label, c.shortcut.map(|k| format!(" ({k})")).unwrap_or_default()), diff --git a/crates/ui-egui/src/workspace.rs b/crates/ui-egui/src/workspace.rs index 6e8e88a2..6040d84e 100644 --- a/crates/ui-egui/src/workspace.rs +++ b/crates/ui-egui/src/workspace.rs @@ -100,6 +100,7 @@ pub fn panels(tab: &str) -> Option<&'static [&'static str]> { /// to the registry. pub fn layout<'a>(tab: &str, panel: &str, specs: &[&'a CommandSpec]) -> Option<(Vec<&'a CommandSpec>, usize)> { let (ids, promoted, own_tab): (&[&str], usize, &str) = match (tab, panel) { + ("SOLID", "CONFIGURE") => (&["parameters.change", "config.table"], 2, tab), ("SOLID" | "SHEET METAL" | "PLASTIC", "ASSEMBLE") => (ASSEMBLE, 3, tab), ("SHEET METAL", "CREATE") => (SHEET_CREATE, 3, tab), ("SHEET METAL", "MODIFY") => (SHEET_MODIFY, 2, tab), @@ -146,6 +147,38 @@ mod tests { layout(tab, panel, &specs).map(|(v, _)| v.iter().map(|c| c.id).collect()).unwrap_or_default() } + #[test] + fn configure_header_clicks_open_parameters_and_configurations_separately() { + use serde_json::json; + let mut h = crate::scenario::Harness::new(); + let commands = ids("SOLID", "CONFIGURE"); + assert_eq!(&commands[..2], &["parameters.change", "config.table"]); + assert!(commands.contains(&"config.configure"), "Configure remains available in the dropdown"); + h.step(&json!({"widget":"Parameters"})).unwrap(); + assert!(h.app.params.is_some(), "the ordinary Parameters button opens the editor"); + assert!(h.app.dialog.is_none()); + assert!(h.widget("User Parameters").is_some()); + assert!(h.widget("Model Parameters").is_some()); + // Exercise the editor's Add button, not a script-only parameter command. + let parameters = h.app.params.as_mut().unwrap(); + parameters.new_name = "width".into(); + parameters.new_expr = "25 mm".into(); + h.frames(3); + h.step(&json!({"widget":"Add"})).unwrap(); + let values = h.app.session.execute("parameters.list", &json!({})).unwrap(); + assert!(values["parameters"].as_array().unwrap().iter().any(|p| p["name"] == "width" && p["value"] == 25.0)); + h.step(&json!({"click":{"handle":"params:close"}})).unwrap(); + assert!(h.app.params.is_none()); + h.step(&json!({"widget":"Configurations"})).unwrap(); + assert!(h.app.params.is_none()); + assert!(matches!( + h.app.dialog.as_ref().map(|d| &d.kind), + Some(crate::dialogs::Kind::Motion(crate::dialogs_motion::Mo::Configurations { .. })) + )); + assert!(h.widget("Add Column").is_some()); + assert!(h.widget("Add Row").is_some()); + } + /// Fusion's layout: joints on every tab's ASSEMBLE panel, sheet metal and plastic tabs /// reach the shared solid commands, the plastic rules sit on SETUP. #[test] diff --git a/docs/book/src/guide/parameters.md b/docs/book/src/guide/parameters.md index 1f45b177..dbe92a21 100644 --- a/docs/book/src/guide/parameters.md +++ b/docs/book/src/guide/parameters.md @@ -5,6 +5,10 @@ are ones you add yourself, with a unit, an expression and an optional comment. C **Change Parameters** (`parameters.change`). The timeline rebuilds from the first feature that uses them. +In the SOLID toolbar's CONFIGURE panel, click **Parameters** to open the user and model +parameter editor. The separate **Configurations** button opens the configuration table; +**Configure** remains in the dropdown for configuring columns. + Expressions are unit-aware: `2 * width + 5 mm`, `angle / 2`, `sqrt(area)`, `1 in + 3 mm`, using `+ - * / ^`, parentheses, `sin cos tan asin acos atan atan2 sqrt abs min max floor ceil round mod trunc` and unit conversion. A length where an angle is expected (or the other way round) is an