forked from 1b-agent/opencadstudio-upstream
feat(commandline): always-on keyboard capture + space submits like enter
Two behaviour changes that bring the command line in line with established CAD workflow: 1. **Always-on keyboard capture.** The input was blurred whenever no command was running, so typed letters went nowhere until the user clicked into the field. Replace the blur with a re-focus on every command lifecycle transition (cmd_result.rs) and on viewport / tab / ribbon clicks (update.rs), focus the input at startup, and add a global key subscription that catches any unhandled character key or backspace and routes it into the buffer via two new messages (CommandAppendChar / CommandBackspace) — so the command line keeps accumulating keystrokes even if focus parks somewhere not focusable. Removes the unused blur_cmd_input helper. 2. **Space submits.** Typing a space at the end of the input now submits the buffer the same way Enter does. Only exception: an active command that's currently collecting free-form prose (TEXT / MTEXT / DDEDIT body, ATTDEF prompt / default value, INSERT attribute value, ATTEDIT cell). Those advertise the new `CadCommand::wants_text_with_spaces()` flag so the Space character reaches the buffer as a literal. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
e55b280dda
commit
39150d617f
16 files changed
+136
-42
No files matched your search
@@ -80,6 +80,10 @@ impl CadCommand for DdeditCommand {
|
||||
matches!(self.step, DdeditStep::EnterText { .. })
|
||||
}
|
||||
|
||||
fn wants_text_with_spaces(&self) -> bool {
|
||||
matches!(self.step, DdeditStep::EnterText { .. })
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let (handle, current) = match &self.step {
|
||||
DdeditStep::EnterText { handle, current } => (*handle, current.clone()),
|
||||
|
||||
@@ -65,6 +65,10 @@ impl CadCommand for MTextCommand {
|
||||
matches!(self.step, Step::WaitText(_))
|
||||
}
|
||||
|
||||
fn wants_text_with_spaces(&self) -> bool {
|
||||
matches!(self.step, Step::WaitText(_))
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
if let Step::WaitText(pos) = &self.step {
|
||||
if text.trim().is_empty() {
|
||||
|
||||
@@ -65,6 +65,10 @@ impl CadCommand for TextCommand {
|
||||
matches!(self.step, Step::WaitText(_))
|
||||
}
|
||||
|
||||
fn wants_text_with_spaces(&self) -> bool {
|
||||
matches!(self.step, Step::WaitText(_))
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
if let Step::WaitText(pos) = &self.step {
|
||||
if text.trim().is_empty() {
|
||||
|
||||
@@ -65,6 +65,12 @@ impl CadCommand for AttdefCommand {
|
||||
!matches!(self.step, Step::Insertion { .. })
|
||||
}
|
||||
|
||||
fn wants_text_with_spaces(&self) -> bool {
|
||||
// Tag must be a single token (so disallow spaces there); the
|
||||
// Prompt and Default value steps are free-form text.
|
||||
matches!(self.step, Step::Prompt { .. } | Step::Default { .. })
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
match &self.step {
|
||||
Step::Tag => {
|
||||
|
||||
@@ -134,18 +134,18 @@ impl CadCommand for HatchCommand {
|
||||
match &self.mode {
|
||||
Mode::PickInside => {
|
||||
let miss = if self.missed {
|
||||
" ⚠ Kapalı sınır bulunamadı."
|
||||
" ⚠ No closed boundary found."
|
||||
} else {
|
||||
""
|
||||
};
|
||||
format!("HATCH İçini tıklayın [S=Elle çiz]:{miss}")
|
||||
format!("HATCH Pick internal point [S=Draw manually]:{miss}")
|
||||
}
|
||||
Mode::Manual => {
|
||||
if self.manual_pts.is_empty() {
|
||||
"HATCH Sınır noktası 1:".into()
|
||||
"HATCH Boundary point 1:".into()
|
||||
} else {
|
||||
format!(
|
||||
"HATCH Nokta {} [Enter=onayla, ≥3 nokta]:",
|
||||
"HATCH Point {} [Enter=accept, ≥3 points]:",
|
||||
self.manual_pts.len() + 1
|
||||
)
|
||||
}
|
||||
@@ -271,18 +271,18 @@ impl CadCommand for GradientCommand {
|
||||
match &self.mode {
|
||||
Mode::PickInside => {
|
||||
let miss = if self.missed {
|
||||
" ⚠ Kapalı sınır bulunamadı."
|
||||
" ⚠ No closed boundary found."
|
||||
} else {
|
||||
""
|
||||
};
|
||||
format!("GRADIENT İçini tıklayın [S=Elle çiz]:{miss}")
|
||||
format!("GRADIENT Pick internal point [S=Draw manually]:{miss}")
|
||||
}
|
||||
Mode::Manual => {
|
||||
if self.manual_pts.is_empty() {
|
||||
"GRADIENT Sınır noktası 1:".into()
|
||||
"GRADIENT Boundary point 1:".into()
|
||||
} else {
|
||||
format!(
|
||||
"GRADIENT Nokta {} [Enter=onayla, ≥3 nokta]:",
|
||||
"GRADIENT Point {} [Enter=accept, ≥3 points]:",
|
||||
self.manual_pts.len() + 1
|
||||
)
|
||||
}
|
||||
@@ -386,11 +386,11 @@ impl CadCommand for BoundaryCommand {
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
let miss = if self.missed {
|
||||
" ⚠ Kapalı sınır bulunamadı."
|
||||
" ⚠ No closed boundary found."
|
||||
} else {
|
||||
""
|
||||
};
|
||||
format!("BOUNDARY İçini tıklayın:{miss}")
|
||||
format!("BOUNDARY Pick internal point:{miss}")
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
||||
|
||||
@@ -82,6 +82,12 @@ impl CadCommand for AtteditCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn wants_text_with_spaces(&self) -> bool {
|
||||
// Editing an attribute value — same free-form semantics as the
|
||||
// text-content prompts in TEXT / MTEXT / DDEDIT.
|
||||
self.wants_text_input()
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let Step::EditAttr { handle, attrs, idx } = &mut self.step else {
|
||||
return None;
|
||||
|
||||
@@ -116,6 +116,11 @@ impl CadCommand for InsertBlockCommand {
|
||||
matches!(self.step, Step::Name | Step::FillAttr { .. })
|
||||
}
|
||||
|
||||
fn wants_text_with_spaces(&self) -> bool {
|
||||
// Block names don't embed whitespace, but attribute values do.
|
||||
matches!(self.step, Step::FillAttr { .. })
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
match &self.step {
|
||||
Step::Name => {
|
||||
|
||||
@@ -43,9 +43,9 @@ impl CadCommand for MviewCommand {
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
if self.corner1.is_none() {
|
||||
"MVIEW İlk köşeyi belirtin:".to_string()
|
||||
"MVIEW Specify first corner:".to_string()
|
||||
} else {
|
||||
"MVIEW Karşı köşeyi belirtin:".to_string()
|
||||
"MVIEW Specify opposite corner:".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ impl CadCommand for MviewCommand {
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
if self.corner1.is_none() {
|
||||
"MVIEW İlk köşeyi belirtin:".to_string()
|
||||
"MVIEW Specify first corner:".to_string()
|
||||
} else {
|
||||
"MVIEW Karşı köşeyi belirtin:".to_string()
|
||||
"MVIEW Specify opposite corner:".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user