From 1533c66a3ecf6607afdb96639ab81a482d576439 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 16 Jul 2026 13:14:37 +0300 Subject: [PATCH] feat(userreg): prompt USERI/USERR for register then value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit USERI/USERR set one of five integer/real header registers and needed both inputs (register + value) typed on one line; bare printed usage. Add a two-step front-end (UserRegCommand): it prompts for the register as 1–5 buttons, then the value, and dispatches USERI/USERR to the existing inline handler. The argument arm now requires a space so the bare name reaches the stepped command. --- src/app/commands/view.rs | 9 ++++- src/command.rs | 71 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/app/commands/view.rs b/src/app/commands/view.rs index 30467f870..40120c92f 100644 --- a/src/app/commands/view.rs +++ b/src/app/commands/view.rs @@ -135,7 +135,14 @@ impl OpenCADStudio { // even though we don't have a LISP / DIESEL runtime yet. // USERI 1 42 → header.user_int1 = 42 // USERR 3 1.5e-3 → header.user_real3 = 0.0015 - cmd if cmd.starts_with("USERI") || cmd.starts_with("USERR") => { + "USERI" | "USERR" => { + use crate::command::UserRegCommand; + let name = if cmd == "USERR" { "USERR" } else { "USERI" }; + let c = UserRegCommand::new(name); + self.command_line.push_info(&c.prompt()); + self.tabs[i].active_cmd = Some(Box::new(c)); + } + cmd if cmd.starts_with("USERI ") || cmd.starts_with("USERR ") => { let is_real = cmd.starts_with("USERR"); let rest = if is_real { cmd.trim_start_matches("USERR").trim() diff --git a/src/command.rs b/src/command.rs index 799ff33b8..1eca915c8 100644 --- a/src/command.rs +++ b/src/command.rs @@ -242,6 +242,77 @@ impl CadCommand for RenameCommand { } } +/// Interactive front-end for USERI / USERR — the five integer / real user +/// registers in the drawing header. They take two inputs (which register, then +/// the value), so they prompt for the register as clickable `1`–`5` buttons and +/// then the value, and delegate to the inline `USERI ` handler. +pub struct UserRegCommand { + /// `"USERI"` or `"USERR"`. + name: &'static str, + /// Chosen register 1–5 once the first step is answered. + slot: Option, +} + +impl UserRegCommand { + pub fn new(name: &'static str) -> Self { + Self { name, slot: None } + } +} + +impl CadCommand for UserRegCommand { + fn name(&self) -> &'static str { + self.name + } + + fn prompt(&self) -> String { + match self.slot { + None => format!("{} which register? [1-5]:", self.name), + Some(n) => format!("{}{n} new value:", self.name), + } + } + + fn options(&self) -> Vec { + match self.slot { + None => (1..=5).map(|n| CmdOption::new(&n.to_string(), &n.to_string())).collect(), + Some(_) => 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() { + return None; + } + match self.slot { + None => { + // Accept a register 1–5; re-prompt on anything else. + match t.parse::() { + Ok(n @ 1..=5) => { + self.slot = Some(n); + None + } + _ => None, + } + } + // The inline handler validates the value (int vs real) and reports + // usage if it doesn't parse, so just hand the whole line over. + Some(n) => Some(CmdResult::Dispatch(format!("{} {n} {t}", self.name))), + } + } + + fn on_point(&mut self, _pt: DVec3) -> CmdResult { + CmdResult::NeedPoint + } + + fn on_enter(&mut self) -> CmdResult { + CmdResult::Cancel + } +} + // ── Result token ────────────────────────────────────────────────────────── /// Returned by every `CadCommand` method to tell main.rs what to do.