diff --git a/src/app/commands/draw.rs b/src/app/commands/draw.rs index 4c747e0a1..4dfc7fa7e 100644 --- a/src/app/commands/draw.rs +++ b/src/app/commands/draw.rs @@ -1016,6 +1016,26 @@ impl OpenCADStudio { } // ── Persistent constraints ──────────────────────────────────── + "CONSTRAINTBAR" | "CONSTRAINTBAR_OPTIONS" => { + use crate::modules::parametric::ConstraintBarOptionCommand; + + let handles = self.tabs[i].scene.selected_handles_in_order(); + if handles.is_empty() && cmd == "CONSTRAINTBAR" { + use crate::modules::draw::select::SelectObjectsCommand; + let command = + SelectObjectsCommand::routed("CONSTRAINTBAR", "CONSTRAINTBAR_OPTIONS"); + self.command_line.push_info(&command.prompt()); + self.tabs[i].active_cmd = Some(Box::new(command)); + } else if handles.is_empty() { + self.command_line.push_output("No objects selected."); + } else { + let command = ConstraintBarOptionCommand::new(handles); + self.command_line.push_info(&command.prompt()); + self.command_line.set_step_options(command.options()); + self.tabs[i].active_cmd = Some(Box::new(command)); + } + } + "GCSHOW" | "GCHIDE" | "DCSHOW" | "DCHIDE" => { let handles = self.tabs[i].scene.selected_handles_in_order(); if handles.is_empty() { @@ -1048,6 +1068,24 @@ impl OpenCADStudio { .push_output(format!("{} constraint indicator(s) reset.", count).as_str()); } + "CONSTRAINTBAR_RESET" => { + let handles = self.tabs[i].scene.selected_handles_in_order(); + if handles.is_empty() { + self.command_line.push_output("No objects selected."); + } else { + let scope = self.tabs[i].current_parametric_scope(); + let count = self.tabs[i].scene.set_parametric_constraint_visibility( + scope, + Some(&handles), + false, + true, + ); + self.command_line.push_output( + format!("{} constraint bar(s) reset.", count).as_str(), + ); + } + } + "GCSHOWALL" | "GCHIDEALL" | "DCSHOWALL" | "DCHIDEALL" => { let scope = self.tabs[i].current_parametric_scope(); let dimensional = cmd.starts_with("DC"); diff --git a/src/modules/draw/select.rs b/src/modules/draw/select.rs index d695ce6d7..94f78b3bc 100644 --- a/src/modules/draw/select.rs +++ b/src/modules/draw/select.rs @@ -68,6 +68,21 @@ impl SelectObjectsCommand { } } + /// Standard selection behavior routed to a private apply command while + /// retaining the public command name in the visible prompt. + pub fn routed(prompt_cmd: &str, pending_cmd: &str) -> Self { + Self { + prompt_cmd: prompt_cmd.to_string(), + pending_cmd: pending_cmd.to_string(), + handles: Vec::new(), + commit_on_enter: true, + show_options: true, + pick: None, + pick_crossing: true, + auto_constrain_settings: false, + } + } + /// Single-object variant: the first completed selection action applies /// immediately, with no Enter (used by commands that act on one object). pub fn instant(pending_cmd: &str) -> Self { diff --git a/src/modules/parametric/constraint_bar.rs b/src/modules/parametric/constraint_bar.rs new file mode 100644 index 000000000..7bbc0c099 --- /dev/null +++ b/src/modules/parametric/constraint_bar.rs @@ -0,0 +1,66 @@ +use acadrust::Handle; +use glam::DVec3; + +use crate::command::{CadCommand, CmdOption, CmdResult}; + +pub struct ConstraintBarOptionCommand { + handles: Vec, +} + +impl ConstraintBarOptionCommand { + pub fn new(handles: Vec) -> Self { + Self { handles } + } + + fn finish(&mut self, command: &str) -> CmdResult { + CmdResult::Relaunch(command.to_string(), std::mem::take(&mut self.handles)) + } +} + +impl CadCommand for ConstraintBarOptionCommand { + fn name(&self) -> &'static str { + "CONSTRAINTBAR" + } + + fn prompt(&self) -> String { + "CONSTRAINTBAR Enter an option [Show/Hide/Reset] :".to_string() + } + + fn options(&self) -> Vec { + vec![ + CmdOption::new("Show", "S"), + CmdOption::new("Hide", "H"), + CmdOption::new("Reset", "R"), + ] + } + + fn wants_text_input(&self) -> bool { + true + } + + fn on_text_input(&mut self, text: &str) -> Option { + let keyword = text.trim().trim_start_matches('_').to_ascii_uppercase(); + match keyword.as_str() { + "S" | "SHOW" => Some(self.finish("GCSHOW")), + "H" | "HIDE" => Some(self.finish("GCHIDE")), + "R" | "RESET" => Some(self.finish("CONSTRAINTBAR_RESET")), + _ => None, + } + } + + fn on_point(&mut self, _point: DVec3) -> CmdResult { + CmdResult::NeedPoint + } + + fn on_enter(&mut self) -> CmdResult { + self.finish("GCSHOW") + } + + fn on_escape(&mut self) -> CmdResult { + CmdResult::Cancel + } +} + +inventory::submit!(crate::command::CommandRegistration { + names: &["CONSTRAINTBAR"] +}); diff --git a/src/modules/parametric/mod.rs b/src/modules/parametric/mod.rs index 056c17686..166c7169f 100644 --- a/src/modules/parametric/mod.rs +++ b/src/modules/parametric/mod.rs @@ -3,6 +3,7 @@ //! resulting constraints through the geometry kernel. mod coincident; +mod constraint_bar; mod equal_distance; mod geom_constraint; #[path = "perpendicular.rs"] @@ -13,6 +14,7 @@ mod tangent_command; mod tools; mod value; pub use coincident::{coincident_tool, CoincidentConstraintCommand}; +pub use constraint_bar::ConstraintBarOptionCommand; pub use equal_distance::{equal_distance_tool, EqualDistanceConstraintCommand}; pub use geom_constraint::GeomConstraintCommand; pub use perpendicular_command::{PerpendicularConstraintCommand, PerpendicularPick};