diff --git a/src/app/commands/styleprops.rs b/src/app/commands/styleprops.rs index 8444fb544..32f310b7d 100644 --- a/src/app/commands/styleprops.rs +++ b/src/app/commands/styleprops.rs @@ -1372,7 +1372,16 @@ impl OpenCADStudio { } // ── RENAME table entries ────────────────────────────────────── - cmd if cmd == "RENAME" || cmd.starts_with("RENAME ") => { + // Bare RENAME prompts step by step (type → old → new); the argument + // form below both handles a fully-typed line and is the target the + // stepped front-end dispatches to. + "RENAME" => { + use crate::command::RenameCommand; + let c = RenameCommand::new(); + self.command_line.push_info(&c.prompt()); + self.tabs[i].active_cmd = Some(Box::new(c)); + } + cmd if cmd.starts_with("RENAME ") => { // Usage: RENAME // Types: LAYER BLOCK STYLE DIMSTYLE LINETYPE UCS VIEW let parts: Vec<&str> = cmd.split_whitespace().collect(); diff --git a/src/command.rs b/src/command.rs index b46940ace..799ff33b8 100644 --- a/src/command.rs +++ b/src/command.rs @@ -130,6 +130,118 @@ impl CadCommand for ValuePromptCommand { } } +/// Interactive front-end for RENAME. Prompts for the object type (as clickable +/// buttons), then the current name, then the new name, and delegates to the +/// inline `RENAME ` handler via [`CmdResult::Dispatch`] — the +/// rename logic stays in one place while the command prompts step by step like +/// every other command instead of only working with all three arguments typed +/// on one line. +pub struct RenameCommand { + step: RenameStep, +} + +enum RenameStep { + Type, + Old { ty: String }, + New { ty: String, old: String }, +} + +impl RenameCommand { + /// `(button label, keyword)` for the object types the inline handler can + /// actually rename. BLOCK is intentionally omitted — the inline handler + /// does not implement it, so offering the button would do nothing. + const TYPES: [(&'static str, &'static str); 6] = [ + ("Layer", "LAYER"), + ("Text style", "STYLE"), + ("Dim style", "DIMSTYLE"), + ("Linetype", "LINETYPE"), + ("UCS", "UCS"), + ("View", "VIEW"), + ]; + + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + step: RenameStep::Type, + } + } +} + +impl CadCommand for RenameCommand { + fn name(&self) -> &'static str { + "RENAME" + } + + fn prompt(&self) -> String { + match &self.step { + RenameStep::Type => "RENAME Select the object type to rename:".to_string(), + RenameStep::Old { ty } => format!("RENAME {ty} Enter the current name:"), + RenameStep::New { ty, old } => format!("RENAME {ty} Rename \"{old}\" to:"), + } + } + + fn options(&self) -> Vec { + match self.step { + RenameStep::Type => Self::TYPES + .iter() + .map(|(label, keyword)| CmdOption::new(label, keyword)) + .collect(), + _ => Vec::new(), + } + } + + fn wants_text_input(&self) -> bool { + true + } + + fn on_text_input(&mut self, text: &str) -> Option { + let t = text.trim(); + if t.is_empty() { + // Keep prompting for the current step. + return None; + } + match &self.step { + RenameStep::Type => { + // Accept a known type (or a common alias); re-prompt otherwise so + // a typo doesn't advance to a rename that can't happen. + let canonical = match t.to_uppercase().as_str() { + "LAYER" | "LA" => "LAYER", + "STYLE" | "TEXTSTYLE" | "ST" => "STYLE", + "DIMSTYLE" | "D" => "DIMSTYLE", + "LINETYPE" | "LT" => "LINETYPE", + "UCS" => "UCS", + "VIEW" | "V" => "VIEW", + _ => return None, + }; + self.step = RenameStep::Old { + ty: canonical.to_string(), + }; + None + } + RenameStep::Old { ty } => { + self.step = RenameStep::New { + ty: ty.clone(), + old: t.to_string(), + }; + None + } + RenameStep::New { ty, old } => { + Some(CmdResult::Dispatch(format!("RENAME {ty} {old} {t}"))) + } + } + } + + fn on_point(&mut self, _pt: DVec3) -> CmdResult { + // A rename takes no point; ignore stray clicks and keep prompting. + CmdResult::NeedPoint + } + + fn on_enter(&mut self) -> CmdResult { + // Bare Enter with nothing typed cancels, like the other prompt commands. + CmdResult::Cancel + } +} + // ── Result token ────────────────────────────────────────────────────────── /// Returned by every `CadCommand` method to tell main.rs what to do.