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
@@ -1539,13 +1539,14 @@ impl OpenCADStudio {
|
|||||||
self.restore_pre_cmd_tangent();
|
self.restore_pre_cmd_tangent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Focus the command-line input while a command is active; blur it when the command ends.
|
// Keep the command-line input focused at all times — every typed
|
||||||
if self.tabs[i].active_cmd.is_some() {
|
// character is meant to route there (the command processor reads
|
||||||
self.focus_cmd_input()
|
// its keystroke stream from this widget). When no command is
|
||||||
} else {
|
// running the ribbon tool button still has to visually deactivate.
|
||||||
|
if self.tabs[i].active_cmd.is_none() {
|
||||||
self.ribbon.deactivate_tool();
|
self.ribbon.deactivate_tool();
|
||||||
self.blur_cmd_input()
|
|
||||||
}
|
}
|
||||||
|
self.focus_cmd_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore the tangent snap state that was in effect before the command started.
|
/// Restore the tangent snap state that was in effect before the command started.
|
||||||
|
|||||||
+10
-1
@@ -343,6 +343,14 @@ pub enum Message {
|
|||||||
CommandInput(String),
|
CommandInput(String),
|
||||||
CommandSubmit,
|
CommandSubmit,
|
||||||
Command(String),
|
Command(String),
|
||||||
|
/// Append one typed character to the command-line input from the
|
||||||
|
/// global key-press subscription. Used when the text-input widget
|
||||||
|
/// itself isn't focused (focus parked on viewport / button / etc.)
|
||||||
|
/// so typing still routes to the command line.
|
||||||
|
CommandAppendChar(String),
|
||||||
|
/// Pop the trailing character off the command-line input — backspace
|
||||||
|
/// counterpart to `CommandAppendChar`.
|
||||||
|
CommandBackspace,
|
||||||
/// Recall previous command in history (↑ arrow key).
|
/// Recall previous command in history (↑ arrow key).
|
||||||
CommandHistoryPrev,
|
CommandHistoryPrev,
|
||||||
/// Recall next command in history (↓ arrow key).
|
/// Recall next command in history (↓ arrow key).
|
||||||
@@ -848,7 +856,8 @@ impl OpenCADStudio {
|
|||||||
crate::update_check::check_for_update(),
|
crate::update_check::check_for_update(),
|
||||||
Message::UpdateCheckResult,
|
Message::UpdateCheckResult,
|
||||||
);
|
);
|
||||||
(s, Task::batch([open_main, check_update]))
|
let focus_cmd = s.focus_cmd_input();
|
||||||
|
(s, Task::batch([open_main, check_update, focus_cmd]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+46
-13
@@ -691,7 +691,7 @@ impl OpenCADStudio {
|
|||||||
|
|
||||||
Message::RibbonSelectTab(idx) => {
|
Message::RibbonSelectTab(idx) => {
|
||||||
self.ribbon.select(idx);
|
self.ribbon.select(idx);
|
||||||
Task::none()
|
self.focus_cmd_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::RibbonToolClick { tool_id, event } => {
|
Message::RibbonToolClick { tool_id, event } => {
|
||||||
@@ -727,7 +727,7 @@ impl OpenCADStudio {
|
|||||||
return Task::done(Message::ToggleLayers);
|
return Task::done(Message::ToggleLayers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Task::none()
|
self.focus_cmd_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Application menu ──────────────────────────────────────────
|
// ── Application menu ──────────────────────────────────────────
|
||||||
@@ -759,7 +759,7 @@ impl OpenCADStudio {
|
|||||||
// fresh tab's defaults (ByLayer) instead of inheriting the
|
// fresh tab's defaults (ByLayer) instead of inheriting the
|
||||||
// previous tab's last selection.
|
// previous tab's last selection.
|
||||||
self.sync_ribbon_from_selection();
|
self.sync_ribbon_from_selection();
|
||||||
Task::none()
|
self.focus_cmd_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::TabSwitch(idx) => {
|
Message::TabSwitch(idx) => {
|
||||||
@@ -772,7 +772,7 @@ impl OpenCADStudio {
|
|||||||
// if there is one), not the prior tab's choice.
|
// if there is one), not the prior tab's choice.
|
||||||
self.sync_ribbon_from_selection();
|
self.sync_ribbon_from_selection();
|
||||||
}
|
}
|
||||||
Task::none()
|
self.focus_cmd_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::TabClose(idx) => {
|
Message::TabClose(idx) => {
|
||||||
@@ -807,11 +807,40 @@ impl OpenCADStudio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Message::CommandInput(s) => {
|
Message::CommandInput(s) => {
|
||||||
// Any manual typing resets the history recall cursor.
|
// Space submits the current input the same way Enter does
|
||||||
|
// (CAD convention) — unless the active command is collecting
|
||||||
|
// free-form text (TEXT / MTEXT / DDEDIT / attribute value
|
||||||
|
// prompts) where Space must reach the buffer as a literal
|
||||||
|
// character. `wants_text_with_spaces()` flags those prompts.
|
||||||
|
let i = self.active_tab;
|
||||||
|
let allow_literal_space = self
|
||||||
|
.tabs[i]
|
||||||
|
.active_cmd
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| c.wants_text_input() && c.wants_text_with_spaces())
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !allow_literal_space && s.ends_with(' ') {
|
||||||
|
self.command_line.input = s.trim_end_matches(' ').to_string();
|
||||||
|
return Task::done(Message::CommandSubmit);
|
||||||
|
}
|
||||||
self.command_line.input = s;
|
self.command_line.input = s;
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Message::CommandAppendChar(s) => {
|
||||||
|
// Filter out control characters — only push the typed
|
||||||
|
// glyph(s). `Tab`, etc. arrive as Named keys, not here.
|
||||||
|
if s.chars().all(|c| !c.is_control()) {
|
||||||
|
self.command_line.input.push_str(&s);
|
||||||
|
}
|
||||||
|
self.focus_cmd_input()
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::CommandBackspace => {
|
||||||
|
self.command_line.input.pop();
|
||||||
|
self.focus_cmd_input()
|
||||||
|
}
|
||||||
|
|
||||||
Message::CommandHistoryPrev => {
|
Message::CommandHistoryPrev => {
|
||||||
self.command_line.history_prev();
|
self.command_line.history_prev();
|
||||||
Task::none()
|
Task::none()
|
||||||
@@ -1723,11 +1752,15 @@ impl OpenCADStudio {
|
|||||||
|
|
||||||
Message::ViewportLeftPress => {
|
Message::ViewportLeftPress => {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
|
// Clicking inside the viewport must not park focus on the
|
||||||
|
// viewport widget — typed characters need to keep flowing
|
||||||
|
// into the command-line input. Refocus on every exit path.
|
||||||
|
let refocus = self.focus_cmd_input();
|
||||||
let (p, vp_size) = {
|
let (p, vp_size) = {
|
||||||
let sel = self.tabs[i].scene.selection.borrow();
|
let sel = self.tabs[i].scene.selection.borrow();
|
||||||
let p = match sel.last_move_pos {
|
let p = match sel.last_move_pos {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return Task::none(),
|
None => return refocus,
|
||||||
};
|
};
|
||||||
(p, sel.vp_size)
|
(p, sel.vp_size)
|
||||||
};
|
};
|
||||||
@@ -1744,7 +1777,7 @@ impl OpenCADStudio {
|
|||||||
if scene::hit_test(p.x, p.y, vw, vh, cam.view_rotation_mat(), VIEWCUBE_PX)
|
if scene::hit_test(p.x, p.y, vw, vh, cam.view_rotation_mat(), VIEWCUBE_PX)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
return Task::none();
|
return refocus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1780,7 +1813,7 @@ impl OpenCADStudio {
|
|||||||
origin_world: world,
|
origin_world: world,
|
||||||
last_world: world,
|
last_world: world,
|
||||||
});
|
});
|
||||||
return Task::none();
|
return refocus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1791,7 +1824,7 @@ impl OpenCADStudio {
|
|||||||
sel.left_press_pos = Some(p);
|
sel.left_press_pos = Some(p);
|
||||||
sel.left_press_time = Some(Instant::now());
|
sel.left_press_time = Some(Instant::now());
|
||||||
sel.left_dragging = false;
|
sel.left_dragging = false;
|
||||||
Task::none()
|
refocus
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::ViewportLeftRelease => {
|
Message::ViewportLeftRelease => {
|
||||||
@@ -2443,7 +2476,7 @@ impl OpenCADStudio {
|
|||||||
) {
|
) {
|
||||||
return Task::done(Message::ViewCubeSnap(region));
|
return Task::done(Message::ViewCubeSnap(region));
|
||||||
}
|
}
|
||||||
Task::none()
|
self.focus_cmd_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::WindowResized(w, h) => {
|
Message::WindowResized(w, h) => {
|
||||||
@@ -3129,13 +3162,13 @@ impl OpenCADStudio {
|
|||||||
self.ribbon.select(idx);
|
self.ribbon.select(idx);
|
||||||
}
|
}
|
||||||
self.command_line.push_output(&format!(
|
self.command_line.push_output(&format!(
|
||||||
"Layout \"{new_name}\" oluşturuldu — MVIEW ile viewport ekleyin"
|
"Layout \"{new_name}\" created — use MVIEW to add a viewport"
|
||||||
));
|
));
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
}
|
}
|
||||||
Err(e) => self
|
Err(e) => self
|
||||||
.command_line
|
.command_line
|
||||||
.push_error(&format!("Layout oluşturulamadı: {e}")),
|
.push_error(&format!("Failed to create layout: {e}")),
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -3187,7 +3220,7 @@ impl OpenCADStudio {
|
|||||||
.any(|n| *n == new_name);
|
.any(|n| *n == new_name);
|
||||||
if exists {
|
if exists {
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_error(&format!("\"{}\" adı zaten kullanımda", new_name));
|
.push_error(&format!("\"{}\" name already in use", new_name));
|
||||||
} else {
|
} else {
|
||||||
self.push_undo_snapshot(i, "LAYOUT RENAME");
|
self.push_undo_snapshot(i, "LAYOUT RENAME");
|
||||||
self.tabs[i].scene.rename_layout(&orig, &new_name);
|
self.tabs[i].scene.rename_layout(&orig, &new_name);
|
||||||
|
|||||||
+14
-5
@@ -678,6 +678,11 @@ impl OpenCADStudio {
|
|||||||
{
|
{
|
||||||
Some(Message::DeleteSelected)
|
Some(Message::DeleteSelected)
|
||||||
}
|
}
|
||||||
|
keyboard::Key::Named(keyboard::key::Named::Backspace)
|
||||||
|
if status == Status::Ignored =>
|
||||||
|
{
|
||||||
|
Some(Message::CommandBackspace)
|
||||||
|
}
|
||||||
keyboard::Key::Named(keyboard::key::Named::ArrowUp) => {
|
keyboard::Key::Named(keyboard::key::Named::ArrowUp) => {
|
||||||
Some(Message::CommandHistoryPrev)
|
Some(Message::CommandHistoryPrev)
|
||||||
}
|
}
|
||||||
@@ -718,6 +723,15 @@ impl OpenCADStudio {
|
|||||||
"v" => Some(Message::Command("PASTECLIP".to_string())),
|
"v" => Some(Message::Command("PASTECLIP".to_string())),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
// Plain character keys (no ctrl) that nothing
|
||||||
|
// else consumed — route them into the command
|
||||||
|
// line input so typing always reaches it even
|
||||||
|
// when focus is parked on a button / viewport.
|
||||||
|
keyboard::Key::Character(c)
|
||||||
|
if !ctrl && status == Status::Ignored =>
|
||||||
|
{
|
||||||
|
Some(Message::CommandAppendChar(c.to_string()))
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -730,11 +744,6 @@ impl OpenCADStudio {
|
|||||||
pub(super) fn focus_cmd_input(&self) -> Task<Message> {
|
pub(super) fn focus_cmd_input(&self) -> Task<Message> {
|
||||||
iced::widget::operation::focus(iced::widget::Id::new(crate::ui::command_line::CMD_INPUT_ID))
|
iced::widget::operation::focus(iced::widget::Id::new(crate::ui::command_line::CMD_INPUT_ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn blur_cmd_input(&self) -> Task<Message> {
|
|
||||||
let op = iced::advanced::widget::operation::focusable::unfocus::<Message>();
|
|
||||||
iced::advanced::widget::operate(op)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Paper canvas ──────────────────────────────────────────────────────────
|
// ── Paper canvas ──────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -241,6 +241,19 @@ pub trait CadCommand: Send {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` when the active text prompt expects free-form prose
|
||||||
|
/// that can legitimately contain whitespace (the body of a TEXT /
|
||||||
|
/// MTEXT / DDEDIT entity, an attribute default value, etc.). For
|
||||||
|
/// these prompts the command-line input must let `Space` be typed as
|
||||||
|
/// a literal character; for every other prompt `Space` submits the
|
||||||
|
/// input the same way `Enter` does.
|
||||||
|
///
|
||||||
|
/// Default `false` — single-token prompts (option letters, numeric
|
||||||
|
/// radius, block name) do not embed spaces.
|
||||||
|
fn wants_text_with_spaces(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when the user submits text via the command line while `wants_text_input` is true.
|
/// Called when the user submits text via the command line while `wants_text_input` is true.
|
||||||
fn on_text_input(&mut self, _text: &str) -> Option<CmdResult> {
|
fn on_text_input(&mut self, _text: &str) -> Option<CmdResult> {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ impl CadCommand for DdeditCommand {
|
|||||||
matches!(self.step, DdeditStep::EnterText { .. })
|
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> {
|
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||||
let (handle, current) = match &self.step {
|
let (handle, current) = match &self.step {
|
||||||
DdeditStep::EnterText { handle, current } => (*handle, current.clone()),
|
DdeditStep::EnterText { handle, current } => (*handle, current.clone()),
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ impl CadCommand for MTextCommand {
|
|||||||
matches!(self.step, Step::WaitText(_))
|
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> {
|
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||||
if let Step::WaitText(pos) = &self.step {
|
if let Step::WaitText(pos) = &self.step {
|
||||||
if text.trim().is_empty() {
|
if text.trim().is_empty() {
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ impl CadCommand for TextCommand {
|
|||||||
matches!(self.step, Step::WaitText(_))
|
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> {
|
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||||
if let Step::WaitText(pos) = &self.step {
|
if let Step::WaitText(pos) = &self.step {
|
||||||
if text.trim().is_empty() {
|
if text.trim().is_empty() {
|
||||||
|
|||||||
@@ -65,6 +65,12 @@ impl CadCommand for AttdefCommand {
|
|||||||
!matches!(self.step, Step::Insertion { .. })
|
!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> {
|
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||||
match &self.step {
|
match &self.step {
|
||||||
Step::Tag => {
|
Step::Tag => {
|
||||||
|
|||||||
@@ -134,18 +134,18 @@ impl CadCommand for HatchCommand {
|
|||||||
match &self.mode {
|
match &self.mode {
|
||||||
Mode::PickInside => {
|
Mode::PickInside => {
|
||||||
let miss = if self.missed {
|
let miss = if self.missed {
|
||||||
" ⚠ Kapalı sınır bulunamadı."
|
" ⚠ No closed boundary found."
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
format!("HATCH İçini tıklayın [S=Elle çiz]:{miss}")
|
format!("HATCH Pick internal point [S=Draw manually]:{miss}")
|
||||||
}
|
}
|
||||||
Mode::Manual => {
|
Mode::Manual => {
|
||||||
if self.manual_pts.is_empty() {
|
if self.manual_pts.is_empty() {
|
||||||
"HATCH Sınır noktası 1:".into()
|
"HATCH Boundary point 1:".into()
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"HATCH Nokta {} [Enter=onayla, ≥3 nokta]:",
|
"HATCH Point {} [Enter=accept, ≥3 points]:",
|
||||||
self.manual_pts.len() + 1
|
self.manual_pts.len() + 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -271,18 +271,18 @@ impl CadCommand for GradientCommand {
|
|||||||
match &self.mode {
|
match &self.mode {
|
||||||
Mode::PickInside => {
|
Mode::PickInside => {
|
||||||
let miss = if self.missed {
|
let miss = if self.missed {
|
||||||
" ⚠ Kapalı sınır bulunamadı."
|
" ⚠ No closed boundary found."
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
format!("GRADIENT İçini tıklayın [S=Elle çiz]:{miss}")
|
format!("GRADIENT Pick internal point [S=Draw manually]:{miss}")
|
||||||
}
|
}
|
||||||
Mode::Manual => {
|
Mode::Manual => {
|
||||||
if self.manual_pts.is_empty() {
|
if self.manual_pts.is_empty() {
|
||||||
"GRADIENT Sınır noktası 1:".into()
|
"GRADIENT Boundary point 1:".into()
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"GRADIENT Nokta {} [Enter=onayla, ≥3 nokta]:",
|
"GRADIENT Point {} [Enter=accept, ≥3 points]:",
|
||||||
self.manual_pts.len() + 1
|
self.manual_pts.len() + 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -386,11 +386,11 @@ impl CadCommand for BoundaryCommand {
|
|||||||
|
|
||||||
fn prompt(&self) -> String {
|
fn prompt(&self) -> String {
|
||||||
let miss = if self.missed {
|
let miss = if self.missed {
|
||||||
" ⚠ Kapalı sınır bulunamadı."
|
" ⚠ No closed boundary found."
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
format!("BOUNDARY İçini tıklayın:{miss}")
|
format!("BOUNDARY Pick internal point:{miss}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
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> {
|
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||||
let Step::EditAttr { handle, attrs, idx } = &mut self.step else {
|
let Step::EditAttr { handle, attrs, idx } = &mut self.step else {
|
||||||
return None;
|
return None;
|
||||||
|
|||||||
@@ -116,6 +116,11 @@ impl CadCommand for InsertBlockCommand {
|
|||||||
matches!(self.step, Step::Name | Step::FillAttr { .. })
|
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> {
|
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||||
match &self.step {
|
match &self.step {
|
||||||
Step::Name => {
|
Step::Name => {
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ impl CadCommand for MviewCommand {
|
|||||||
|
|
||||||
fn prompt(&self) -> String {
|
fn prompt(&self) -> String {
|
||||||
if self.corner1.is_none() {
|
if self.corner1.is_none() {
|
||||||
"MVIEW İlk köşeyi belirtin:".to_string()
|
"MVIEW Specify first corner:".to_string()
|
||||||
} else {
|
} 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 {
|
fn prompt(&self) -> String {
|
||||||
if self.corner1.is_none() {
|
if self.corner1.is_none() {
|
||||||
"MVIEW İlk köşeyi belirtin:".to_string()
|
"MVIEW Specify first corner:".to_string()
|
||||||
} else {
|
} else {
|
||||||
"MVIEW Karşı köşeyi belirtin:".to_string()
|
"MVIEW Specify opposite corner:".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -909,11 +909,11 @@ pub fn build_geometry() -> (Vec<CubeVertex>, Vec<u32>) {
|
|||||||
&mut is,
|
&mut is,
|
||||||
);
|
);
|
||||||
for &([sx, sy, sz], region) in &[
|
for &([sx, sy, sz], region) in &[
|
||||||
([1.0f32, 1.0, 1.0], CORNER_TBK_R), // sy=+1 = BACK yönü
|
([1.0f32, 1.0, 1.0], CORNER_TBK_R), // sy=+1 → BACK direction
|
||||||
([-1.0, 1.0, 1.0], CORNER_TBK_L),
|
([-1.0, 1.0, 1.0], CORNER_TBK_L),
|
||||||
([1.0, 1.0, -1.0], CORNER_BBK_R),
|
([1.0, 1.0, -1.0], CORNER_BBK_R),
|
||||||
([-1.0, 1.0, -1.0], CORNER_BBK_L),
|
([-1.0, 1.0, -1.0], CORNER_BBK_L),
|
||||||
([1.0, -1.0, 1.0], CORNER_TPF_R), // sy=-1 = FRONT yönü
|
([1.0, -1.0, 1.0], CORNER_TPF_R), // sy=-1 → FRONT direction
|
||||||
([-1.0, -1.0, 1.0], CORNER_TPF_L),
|
([-1.0, -1.0, 1.0], CORNER_TPF_L),
|
||||||
([1.0, -1.0, -1.0], CORNER_BTF_R),
|
([1.0, -1.0, -1.0], CORNER_BTF_R),
|
||||||
([-1.0, -1.0, -1.0], CORNER_BTF_L),
|
([-1.0, -1.0, -1.0], CORNER_BTF_L),
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ pub(super) fn render_small<'a>(
|
|||||||
.height(ROW_H)
|
.height(ROW_H)
|
||||||
.padding([4, 4]);
|
.padding([4, 4]);
|
||||||
|
|
||||||
let arr_tip = format!("{} seçenekleri", cur_label);
|
let arr_tip = format!("{} options", cur_label);
|
||||||
let arr_btn = button(
|
let arr_btn = button(
|
||||||
container(text("▾").size(7).color(ARROW_COLOR))
|
container(text("▾").size(7).color(ARROW_COLOR))
|
||||||
.width(Fill)
|
.width(Fill)
|
||||||
@@ -488,7 +488,7 @@ pub(super) fn render_large<'a>(
|
|||||||
.or_else(|| items.first().map(|(_, lbl, _)| *lbl))
|
.or_else(|| items.first().map(|(_, lbl, _)| *lbl))
|
||||||
.unwrap_or(label);
|
.unwrap_or(label);
|
||||||
let tip_text = format!("{}\nCommand: {}", cur_label, last);
|
let tip_text = format!("{}\nCommand: {}", cur_label, last);
|
||||||
let arr_tip = format!("{} seçenekleri", label);
|
let arr_tip = format!("{} options", label);
|
||||||
|
|
||||||
let top_btn = button(
|
let top_btn = button(
|
||||||
column![
|
column![
|
||||||
|
|||||||
Reference in new issue
Block a user