forked from 1b-agent/opencadstudio-upstream
fix(extrude): typed height is ignored on web (#1082)
On wasm the canvas holds focus, so a typed value reaches the dynamic Distance field through CommandAppendChar+CommandFinalize — and try_dyn_commit only commits as text when wants_text() && ! point_step_accepts_keywords(), or dyn_commit_as_text(). EXTRUDE's Height step accepts keywords and never overrode dyn_commit_as_text (unlike PRESSPULL and REVOLVE), so the value resolved as an in-plane point whose height is ~0 and silently re-prompted. Commit the Height step as text, matching its siblings; regression test drives the exact web message sequence.
This commit is contained in:
1 parent
a60b52a462
commit
9e4c3837dd
2 files changed
+101
No files matched your search
@@ -1680,4 +1680,95 @@ mod tests {
|
|||||||
let _ = app.update(Message::ToggleRibbonDropdown("PROP_COLOR".to_string()));
|
let _ = app.update(Message::ToggleRibbonDropdown("PROP_COLOR".to_string()));
|
||||||
let _ = app.view(wid);
|
let _ = app.view(wid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Regression test for issue #1082: a height typed while EXTRUDE waits at
|
||||||
|
/// its Height prompt must extrude, including when the keystrokes arrive
|
||||||
|
/// through the global key-press subscription (`CommandAppendChar` → the
|
||||||
|
/// dynamic-input field buffer → `CommandFinalize`) instead of the focused
|
||||||
|
/// command-line widget (`CommandInput` → `CommandSubmit`). The former is
|
||||||
|
/// the only route on the web build, where the canvas — not the command
|
||||||
|
/// line — holds keyboard focus.
|
||||||
|
#[test]
|
||||||
|
fn extrude_typed_height_from_dyn_field() {
|
||||||
|
use crate::app::Message;
|
||||||
|
let mut app = OpenCADStudio::new_for_test();
|
||||||
|
app.automation_op(r#"{"op":"new"}"#);
|
||||||
|
// A closed rectangle in the XY plane.
|
||||||
|
use acadrust::entities::LwVertex;
|
||||||
|
use acadrust::types::Vector2;
|
||||||
|
let rect = acadrust::LwPolyline {
|
||||||
|
vertices: [
|
||||||
|
(-10.0, -10.0),
|
||||||
|
(10.0, -10.0),
|
||||||
|
(10.0, 10.0),
|
||||||
|
(-10.0, 10.0),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|(x, y)| LwVertex::new(Vector2::new(*x, *y)))
|
||||||
|
.collect(),
|
||||||
|
is_closed: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let handle = app.tabs[0]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.add_entity(acadrust::EntityType::LwPolyline(rect))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Start EXTRUDE via the command line.
|
||||||
|
let _ = app.update(Message::CommandInput("EXTRUDE".to_string()));
|
||||||
|
let _ = app.update(Message::CommandSubmit);
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[0].active_cmd.as_ref().map(|c| c.name()),
|
||||||
|
Some("EXTRUDE")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pick the rectangle: the GUI route is an entity pick at a world point.
|
||||||
|
let _ = app.feed_command(crate::command::StepInput::EntityPick(
|
||||||
|
handle,
|
||||||
|
glam::DVec3::new(-10.0, -10.0, 0.0),
|
||||||
|
));
|
||||||
|
// Bare Enter finishes selection → Height step (DYN input is on by
|
||||||
|
// default, so the prompt shows a Distance field).
|
||||||
|
let _ = app.update(Message::CommandFinalize);
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[0].active_cmd.as_ref().map(|c| c.name()),
|
||||||
|
Some("EXTRUDE")
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
app.dyn_input
|
||||||
|
&& app.tabs[0]
|
||||||
|
.dyn_fields
|
||||||
|
.iter()
|
||||||
|
.any(|f| f.role == crate::command::DynRole::Distance)
|
||||||
|
);
|
||||||
|
|
||||||
|
// The unfocused-input route for digits while dyn fields show:
|
||||||
|
// CommandAppendChar fills the Distance field buffer, not the
|
||||||
|
// command-line input.
|
||||||
|
for ch in ["5"] {
|
||||||
|
let _ = app.update(Message::CommandAppendChar(ch.to_string()));
|
||||||
|
}
|
||||||
|
assert_eq!(app.command_line.input, "");
|
||||||
|
assert!(
|
||||||
|
app.tabs[0]
|
||||||
|
.dyn_fields
|
||||||
|
.iter()
|
||||||
|
.any(|f| f.buffer.as_deref() == Some("5"))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enter commits the typed height through the dyn-field buffer.
|
||||||
|
let _ = app.update(Message::CommandFinalize);
|
||||||
|
let solid_count = app.tabs[0]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.entities()
|
||||||
|
.filter(|e| matches!(e, acadrust::EntityType::Solid3D(_)))
|
||||||
|
.count();
|
||||||
|
assert!(solid_count > 0, "typed height must extrude");
|
||||||
|
assert!(
|
||||||
|
app.tabs[0].active_cmd.is_none(),
|
||||||
|
"EXTRUDE must finish after the typed height"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -472,6 +472,16 @@ impl CadCommand for ExtrudeCommand {
|
|||||||
fn dyn_live_value(&self, cursor: DVec3) -> Option<f64> {
|
fn dyn_live_value(&self, cursor: DVec3) -> Option<f64> {
|
||||||
Some((cursor - self.anchor).dot(self.profile_direction?))
|
Some((cursor - self.anchor).dot(self.profile_direction?))
|
||||||
}
|
}
|
||||||
|
// The Height step's dynamic box is a span along the profile normal, not a
|
||||||
|
// point in the drawing plane. Without this, a typed value committed from
|
||||||
|
// the box (the only route on web, where the canvas — not the command
|
||||||
|
// line — holds keyboard focus) is resolved into an in-plane point whose
|
||||||
|
// height projects to ~0 and is silently dropped. `on_text_input` parses
|
||||||
|
// the same string the command-line path would (keywords and
|
||||||
|
// `parse_typed_length`), so both entry routes behave identically (#1082).
|
||||||
|
fn dyn_commit_as_text(&self) -> bool {
|
||||||
|
self.step == ExtrudeStep::Height
|
||||||
|
}
|
||||||
fn is_selection_gathering(&self) -> bool {
|
fn is_selection_gathering(&self) -> bool {
|
||||||
self.step == ExtrudeStep::Pick
|
self.step == ExtrudeStep::Pick
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user