feat(commands): prompt ATTDISP and LAYERSTATE by keyword
Add KeywordCommand, a generic stepped front-end for keyword sub-verb commands: bare NAME shows the sub-verbs as clickable buttons, and picking one either dispatches `NAME <verb>` (action verbs like ATTDISP On/Off/Normal) or prompts for an argument and dispatches `NAME <verb> <value>` (LAYERSTATE Save/Restore/Delete <name>). It delegates to the existing inline handler, so the logic stays in one place. First two of bucket B converted; the rest (VIEW, LINETYPE, SCALELISTEDIT, SETVAR, LAYDEL/LAYMRG, and the selection-first XDATA/UNDERLAY/ADJUST/DRAWORDER/CHPROP) can follow the same pattern.
This commit is contained in:
parent
97cc647055
commit
4fa6c49445
3 changed files with 127 additions and 5 deletions
|
|
@ -149,7 +149,21 @@ impl OpenCADStudio {
|
||||||
// ATTDISP ON — make all AttributeDefinitions visible
|
// ATTDISP ON — make all AttributeDefinitions visible
|
||||||
// ATTDISP OFF — make all AttributeDefinitions invisible
|
// ATTDISP OFF — make all AttributeDefinitions invisible
|
||||||
// ATTDISP NORMAL — restore: show only those without the invisible flag
|
// ATTDISP NORMAL — restore: show only those without the invisible flag
|
||||||
cmd if cmd == "ATTDISP" || cmd.starts_with("ATTDISP ") => {
|
"ATTDISP" => {
|
||||||
|
use crate::command::KeywordCommand;
|
||||||
|
let c = KeywordCommand::new(
|
||||||
|
"ATTDISP",
|
||||||
|
"ATTDISP attribute display [On / Off / Normal]:",
|
||||||
|
vec![
|
||||||
|
("On", "ON", None),
|
||||||
|
("Off", "OFF", None),
|
||||||
|
("Normal", "NORMAL", None),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("ATTDISP ") => {
|
||||||
let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
|
let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
|
||||||
match sub.as_str() {
|
match sub.as_str() {
|
||||||
"ON" | "OFF" | "NORMAL" => {
|
"ON" | "OFF" | "NORMAL" => {
|
||||||
|
|
|
||||||
|
|
@ -191,10 +191,22 @@ impl OpenCADStudio {
|
||||||
// LAYERSTATE — save / restore named snapshots of all layer states
|
// LAYERSTATE — save / restore named snapshots of all layer states
|
||||||
// (on/off, freeze, lock, colour, linetype, lineweight).
|
// (on/off, freeze, lock, colour, linetype, lineweight).
|
||||||
// LAYERSTATE SAVE <name> | RESTORE <name> | DELETE <name> | ? (list)
|
// LAYERSTATE SAVE <name> | RESTORE <name> | DELETE <name> | ? (list)
|
||||||
cmd if cmd == "LAYERSTATE"
|
"LAYERSTATE" | "LAS" | "LMAN" => {
|
||||||
|| cmd == "LAS"
|
use crate::command::KeywordCommand;
|
||||||
|| cmd == "LMAN"
|
let c = KeywordCommand::new(
|
||||||
|| cmd.starts_with("LAYERSTATE ")
|
"LAYERSTATE",
|
||||||
|
"LAYERSTATE [List / Save / Restore / Delete]:",
|
||||||
|
vec![
|
||||||
|
("List", "LIST", None),
|
||||||
|
("Save", "SAVE", Some("LAYERSTATE SAVE new state name:")),
|
||||||
|
("Restore", "RESTORE", Some("LAYERSTATE RESTORE state name:")),
|
||||||
|
("Delete", "DELETE", Some("LAYERSTATE DELETE state name:")),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("LAYERSTATE ")
|
||||||
|| cmd.starts_with("LAS ")
|
|| cmd.starts_with("LAS ")
|
||||||
|| cmd.starts_with("LMAN ") =>
|
|| cmd.starts_with("LMAN ") =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,102 @@ impl CadCommand for UserRegCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generic interactive front-end for a keyword sub-verb command (ATTDISP,
|
||||||
|
/// LAYERSTATE, SCALELISTEDIT, VIEW…). Bare `<name>` enters this command, which
|
||||||
|
/// shows the sub-verbs as clickable buttons; picking one either dispatches
|
||||||
|
/// `<name> <keyword>` straight away (an action verb) or, when the verb needs an
|
||||||
|
/// argument, prompts for it and then dispatches `<name> <keyword> <value>`.
|
||||||
|
/// Delegates to the existing inline `<name> …` handler so the logic stays in
|
||||||
|
/// one place, the way [`ValuePromptCommand`] does for single values.
|
||||||
|
pub struct KeywordCommand {
|
||||||
|
name: &'static str,
|
||||||
|
prompt: &'static str,
|
||||||
|
/// `(button label, keyword, value prompt)`. A `Some` value prompt means the
|
||||||
|
/// verb takes one argument collected in a second step; `None` acts alone.
|
||||||
|
options: Vec<(&'static str, &'static str, Option<&'static str>)>,
|
||||||
|
/// Set once a value-taking verb is chosen: `(keyword, value prompt)`.
|
||||||
|
pending: Option<(&'static str, &'static str)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeywordCommand {
|
||||||
|
pub fn new(
|
||||||
|
name: &'static str,
|
||||||
|
prompt: &'static str,
|
||||||
|
options: Vec<(&'static str, &'static str, Option<&'static str>)>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
prompt,
|
||||||
|
options,
|
||||||
|
pending: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CadCommand for KeywordCommand {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prompt(&self) -> String {
|
||||||
|
match self.pending {
|
||||||
|
Some((_, value_prompt)) => value_prompt.to_string(),
|
||||||
|
None => self.prompt.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn options(&self) -> Vec<CmdOption> {
|
||||||
|
match self.pending {
|
||||||
|
Some(_) => Vec::new(),
|
||||||
|
None => self
|
||||||
|
.options
|
||||||
|
.iter()
|
||||||
|
.map(|(label, keyword, _)| CmdOption::new(label, keyword))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wants_text_input(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||||
|
let t = text.trim();
|
||||||
|
if t.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match self.pending {
|
||||||
|
// Second step: the argument for the already-chosen verb.
|
||||||
|
Some((keyword, _)) => Some(CmdResult::Dispatch(format!("{} {keyword} {t}", self.name))),
|
||||||
|
// First step: match the typed / clicked token to a sub-verb.
|
||||||
|
None => {
|
||||||
|
let up = t.to_uppercase();
|
||||||
|
let Some((_, keyword, value_prompt)) =
|
||||||
|
self.options.iter().find(|(_, k, _)| k.eq_ignore_ascii_case(&up))
|
||||||
|
else {
|
||||||
|
// Unknown verb — keep prompting rather than dispatch garbage.
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
match value_prompt {
|
||||||
|
Some(vp) => {
|
||||||
|
self.pending = Some((keyword, vp));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
None => Some(CmdResult::Dispatch(format!("{} {keyword}", self.name))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_point(&mut self, _pt: DVec3) -> CmdResult {
|
||||||
|
CmdResult::NeedPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_enter(&mut self) -> CmdResult {
|
||||||
|
CmdResult::Cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Result token ──────────────────────────────────────────────────────────
|
// ── Result token ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Returned by every `CadCommand` method to tell main.rs what to do.
|
/// Returned by every `CadCommand` method to tell main.rs what to do.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue