feat(commands): step the rest of the keyword/value commands

Convert the remaining bucket-B commands to prompt step by step instead of
requiring arguments or printing usage:

- KeywordCommand (buttons): VIEW, LINETYPE, SCALELISTEDIT, UCSICON, VSCURRENT.
- ValuePromptCommand (one value): LAYDEL (layer), ATTSYNC (block).
- TwoValuePromptCommand (new; two values): LAYMRG (source + target),
  SETVAR (variable + value, blank second reads).

Each bare form now starts the stepped front-end and delegates to the existing
inline handler; the argument form is kept and is the dispatch target. The
direct visual-style shortcuts (HIDDENLINE / XRAY / …) and UCSICON's argument
form are untouched.

COUNT is intentionally left alone: bare COUNT already reports the tally (a
useful inquiry action, not a usage line), so prompting would only add friction.
This commit is contained in:
Hakan Seven committed 2026-07-16 13:29:04 +03:00
1 parent 4fa6c49445
commit 4c3509fa51
7 files changed
+186 -16

No files matched your search

+70
View File
@@ -409,6 +409,76 @@ impl CadCommand for KeywordCommand {
}
}
/// Generic interactive front-end for a two-argument command (LAYMRG source +
/// target, SETVAR variable + value…). Prompts for the two values in turn and
/// dispatches `<name> <first> <second>` to the existing inline handler. A bare
/// Enter on the second value dispatches `<name> <first>` (no second token) so a
/// getter-style command (SETVAR reading a variable) still works.
pub struct TwoValuePromptCommand {
name: &'static str,
prompt1: &'static str,
prompt2: &'static str,
first: Option<String>,
}
impl TwoValuePromptCommand {
pub fn new(name: &'static str, prompt1: &'static str, prompt2: &'static str) -> Self {
Self {
name,
prompt1,
prompt2,
first: None,
}
}
}
impl CadCommand for TwoValuePromptCommand {
fn name(&self) -> &'static str {
self.name
}
fn prompt(&self) -> String {
match &self.first {
None => self.prompt1.to_string(),
Some(_) => self.prompt2.to_string(),
}
}
fn wants_text_input(&self) -> bool {
true
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
let t = text.trim();
match &self.first {
None => {
if t.is_empty() {
return None;
}
self.first = Some(t.to_string());
None
}
Some(first) => Some(CmdResult::Dispatch(if t.is_empty() {
format!("{} {first}", self.name)
} else {
format!("{} {first} {t}", self.name)
})),
}
}
fn on_point(&mut self, _pt: DVec3) -> CmdResult {
CmdResult::NeedPoint
}
fn on_enter(&mut self) -> CmdResult {
match &self.first {
// Enter on the second step with nothing typed = report / no-op form.
Some(first) => CmdResult::Dispatch(format!("{} {first}", self.name)),
None => CmdResult::Cancel,
}
}
}
// ── Result token ──────────────────────────────────────────────────────────
/// Returned by every `CadCommand` method to tell main.rs what to do.