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:
parent
4fa6c49445
commit
4c3509fa51
7 changed files with 186 additions and 16 deletions
|
|
@ -324,7 +324,13 @@ impl OpenCADStudio {
|
||||||
// block's current attribute definitions: drop attributes whose tag
|
// block's current attribute definitions: drop attributes whose tag
|
||||||
// no longer exists and add any newly-defined ones (keeping the values
|
// no longer exists and add any newly-defined ones (keeping the values
|
||||||
// of attributes that remain).
|
// of attributes that remain).
|
||||||
cmd if cmd == "ATTSYNC" || cmd.starts_with("ATTSYNC ") => {
|
"ATTSYNC" => {
|
||||||
|
use crate::command::ValuePromptCommand;
|
||||||
|
let c = ValuePromptCommand::new("ATTSYNC", "ATTSYNC block name to sync:");
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("ATTSYNC ") => {
|
||||||
let arg = cmd.trim_start_matches("ATTSYNC").trim();
|
let arg = cmd.trim_start_matches("ATTSYNC").trim();
|
||||||
let blocks = self.tabs[i].scene.custom_block_names();
|
let blocks = self.tabs[i].scene.custom_block_names();
|
||||||
if arg.is_empty() {
|
if arg.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,22 @@ impl OpenCADStudio {
|
||||||
// UCSICON OFF — hide UCS icon in all viewports
|
// UCSICON OFF — hide UCS icon in all viewports
|
||||||
// UCSICON NOORIGIN — show icon but not at origin (show at corner)
|
// UCSICON NOORIGIN — show icon but not at origin (show at corner)
|
||||||
// UCSICON ORIGIN — show icon at UCS origin
|
// UCSICON ORIGIN — show icon at UCS origin
|
||||||
cmd if cmd == "UCSICON" || cmd.starts_with("UCSICON ") => {
|
"UCSICON" => {
|
||||||
|
use crate::command::KeywordCommand;
|
||||||
|
let c = KeywordCommand::new(
|
||||||
|
"UCSICON",
|
||||||
|
"UCSICON [On / Off / NoOrigin / Origin]:",
|
||||||
|
vec![
|
||||||
|
("On", "ON", None),
|
||||||
|
("Off", "OFF", None),
|
||||||
|
("NoOrigin", "NOORIGIN", None),
|
||||||
|
("Origin", "ORIGIN", None),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("UCSICON ") => {
|
||||||
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" | "NOORIGIN" | "ORIGIN" => {
|
"ON" | "OFF" | "NOORIGIN" | "ORIGIN" => {
|
||||||
|
|
@ -746,7 +761,20 @@ impl OpenCADStudio {
|
||||||
// SCALELISTEDIT list
|
// SCALELISTEDIT list
|
||||||
// SCALELISTEDIT ADD 1:50 add (name is a paper:drawing ratio)
|
// SCALELISTEDIT ADD 1:50 add (name is a paper:drawing ratio)
|
||||||
// SCALELISTEDIT DELETE 1:50 remove (not the current scale)
|
// SCALELISTEDIT DELETE 1:50 remove (not the current scale)
|
||||||
cmd if cmd == "SCALELISTEDIT" || cmd.starts_with("SCALELISTEDIT ") => {
|
"SCALELISTEDIT" => {
|
||||||
|
use crate::command::KeywordCommand;
|
||||||
|
let c = KeywordCommand::new(
|
||||||
|
"SCALELISTEDIT",
|
||||||
|
"SCALELISTEDIT [Add / Delete]:",
|
||||||
|
vec![
|
||||||
|
("Add", "ADD", Some("SCALELISTEDIT ADD new scale ratio (e.g. 1:50):")),
|
||||||
|
("Delete", "DELETE", Some("SCALELISTEDIT DELETE scale ratio to remove:")),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("SCALELISTEDIT ") => {
|
||||||
let rest = cmd.trim_start_matches("SCALELISTEDIT").trim();
|
let rest = cmd.trim_start_matches("SCALELISTEDIT").trim();
|
||||||
let mut parts = rest.splitn(2, char::is_whitespace);
|
let mut parts = rest.splitn(2, char::is_whitespace);
|
||||||
let sub = parts.next().unwrap_or("").to_uppercase();
|
let sub = parts.next().unwrap_or("").to_uppercase();
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,26 @@ impl OpenCADStudio {
|
||||||
// chosen style is reported so the mapping is explicit. (`SOLID` is
|
// chosen style is reported so the mapping is explicit. (`SOLID` is
|
||||||
// intentionally NOT a visual-style verb — it is the 2D filled-polygon
|
// intentionally NOT a visual-style verb — it is the 2D filled-polygon
|
||||||
// draw command; the shaded ribbon button drives `SetWireframe`.)
|
// draw command; the shaded ribbon button drives `SetWireframe`.)
|
||||||
cmd if cmd == "VS"
|
"VS" | "VSCURRENT" | "SHADEMODE" => {
|
||||||
|| cmd == "VSCURRENT"
|
use crate::command::KeywordCommand;
|
||||||
|| cmd == "SHADEMODE"
|
let c = KeywordCommand::new(
|
||||||
|| cmd == "HIDDENLINE"
|
"VSCURRENT",
|
||||||
|
"VSCURRENT visual style [Shaded / Wireframe / Hidden / Realistic / Conceptual / X-ray]:",
|
||||||
|
vec![
|
||||||
|
("Shaded", "SHADED", None),
|
||||||
|
("Wireframe", "WIREFRAME", None),
|
||||||
|
("Hidden", "HIDDEN", None),
|
||||||
|
("Realistic", "REALISTIC", None),
|
||||||
|
("Conceptual", "CONCEPTUAL", None),
|
||||||
|
("X-Ray", "XRAY", None),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
// The named visual-style shortcuts still switch directly, and the
|
||||||
|
// `<name> <style>` argument form (also what the picker dispatches).
|
||||||
|
cmd if cmd == "HIDDENLINE"
|
||||||
|| cmd == "XRAY"
|
|| cmd == "XRAY"
|
||||||
|| cmd == "REALISTIC"
|
|| cmd == "REALISTIC"
|
||||||
|| cmd == "CONCEPTUAL"
|
|| cmd == "CONCEPTUAL"
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,22 @@ impl OpenCADStudio {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd if cmd == "VIEW" || cmd.starts_with("VIEW ") => {
|
"VIEW" => {
|
||||||
|
use crate::command::KeywordCommand;
|
||||||
|
let c = KeywordCommand::new(
|
||||||
|
"VIEW",
|
||||||
|
"VIEW [List / Save / Restore / Delete]:",
|
||||||
|
vec![
|
||||||
|
("List", "LIST", None),
|
||||||
|
("Save", "SAVE", Some("VIEW SAVE new view name:")),
|
||||||
|
("Restore", "RESTORE", Some("VIEW RESTORE view name:")),
|
||||||
|
("Delete", "DELETE", Some("VIEW DELETE view name:")),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("VIEW ") => {
|
||||||
let parts: Vec<&str> = cmd.splitn(3, ' ').collect();
|
let parts: Vec<&str> = cmd.splitn(3, ' ').collect();
|
||||||
let sub = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
|
let sub = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
|
||||||
match sub.as_str() {
|
match sub.as_str() {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,13 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LAYDEL <name> — delete a layer and erase the objects on it.
|
// LAYDEL <name> — delete a layer and erase the objects on it.
|
||||||
cmd if cmd == "LAYDEL" || cmd.starts_with("LAYDEL ") => {
|
"LAYDEL" => {
|
||||||
|
use crate::command::ValuePromptCommand;
|
||||||
|
let c = ValuePromptCommand::new("LAYDEL", "LAYDEL layer to delete:");
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("LAYDEL ") => {
|
||||||
let name = cmd.trim_start_matches("LAYDEL").trim();
|
let name = cmd.trim_start_matches("LAYDEL").trim();
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
self.command_line.push_info("Usage: LAYDEL <layer name>");
|
self.command_line.push_info("Usage: LAYDEL <layer name>");
|
||||||
|
|
@ -127,7 +133,17 @@ impl OpenCADStudio {
|
||||||
|
|
||||||
// LAYMRG <source> <target> — move every object from <source> onto
|
// LAYMRG <source> <target> — move every object from <source> onto
|
||||||
// <target>, then delete the emptied <source> layer.
|
// <target>, then delete the emptied <source> layer.
|
||||||
cmd if cmd == "LAYMRG" || cmd.starts_with("LAYMRG ") => {
|
"LAYMRG" => {
|
||||||
|
use crate::command::TwoValuePromptCommand;
|
||||||
|
let c = TwoValuePromptCommand::new(
|
||||||
|
"LAYMRG",
|
||||||
|
"LAYMRG source layer (merged away):",
|
||||||
|
"LAYMRG target layer (kept):",
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("LAYMRG ") => {
|
||||||
let rest = cmd.trim_start_matches("LAYMRG").trim();
|
let rest = cmd.trim_start_matches("LAYMRG").trim();
|
||||||
let parts: Vec<&str> = rest.split_whitespace().collect();
|
let parts: Vec<&str> = rest.split_whitespace().collect();
|
||||||
if parts.len() != 2 {
|
if parts.len() != 2 {
|
||||||
|
|
|
||||||
|
|
@ -69,11 +69,20 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── LINETYPE management ───────────────────────────────────────
|
// ── LINETYPE management ───────────────────────────────────────
|
||||||
cmd if cmd == "LINETYPE"
|
"LINETYPE" | "LT" => {
|
||||||
|| cmd == "LT"
|
use crate::command::KeywordCommand;
|
||||||
|| cmd.starts_with("LINETYPE ")
|
let c = KeywordCommand::new(
|
||||||
|| cmd.starts_with("LT ") =>
|
"LINETYPE",
|
||||||
{
|
"LINETYPE [List / Set]:",
|
||||||
|
vec![
|
||||||
|
("List", "LIST", None),
|
||||||
|
("Set", "SET", Some("LINETYPE SET linetype name (ByLayer / ByBlock / …):")),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("LINETYPE ") || cmd.starts_with("LT ") => {
|
||||||
let raw_rest = cmd.split_once(' ').map(|(_, r)| r.trim()).unwrap_or("");
|
let raw_rest = cmd.split_once(' ').map(|(_, r)| r.trim()).unwrap_or("");
|
||||||
let parts: Vec<&str> = raw_rest.split_whitespace().collect();
|
let parts: Vec<&str> = raw_rest.split_whitespace().collect();
|
||||||
let sub = parts.get(0).map(|s| s.to_uppercase()).unwrap_or_default();
|
let sub = parts.get(0).map(|s| s.to_uppercase()).unwrap_or_default();
|
||||||
|
|
@ -587,7 +596,17 @@ impl OpenCADStudio {
|
||||||
return self.dispatch_styleprops(&format!("SETVAR {cmd}"), i);
|
return self.dispatch_styleprops(&format!("SETVAR {cmd}"), i);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd if cmd == "SETVAR" || cmd.starts_with("SETVAR ") => {
|
"SETVAR" => {
|
||||||
|
use crate::command::TwoValuePromptCommand;
|
||||||
|
let c = TwoValuePromptCommand::new(
|
||||||
|
"SETVAR",
|
||||||
|
"SETVAR variable name:",
|
||||||
|
"SETVAR new value (blank to read):",
|
||||||
|
);
|
||||||
|
self.command_line.push_info(&c.prompt());
|
||||||
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||||
|
}
|
||||||
|
cmd if cmd.starts_with("SETVAR ") => {
|
||||||
let rest = cmd.strip_prefix("SETVAR").unwrap_or("").trim();
|
let rest = cmd.strip_prefix("SETVAR").unwrap_or("").trim();
|
||||||
let mut it = rest.splitn(2, char::is_whitespace);
|
let mut it = rest.splitn(2, char::is_whitespace);
|
||||||
let name = it.next().unwrap_or("").to_uppercase();
|
let name = it.next().unwrap_or("").to_uppercase();
|
||||||
|
|
|
||||||
|
|
@ -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 ──────────────────────────────────────────────────────────
|
// ── 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