From 4b72da6b83ff3d68d13050b15f15463c0a40d187 Mon Sep 17 00:00:00 2001 From: F4brizi Date: Tue, 6 Oct 2026 20:30:26 +0300 Subject: [PATCH] fix(viewport): take a command's point on mouse-down (#1623) A point or pick step commits on the press, so the cursor drifting before the button comes up no longer moves it. Selection steps and drag-fence picks still wait for the release. The press runs the existing release path once; the command path clears left_down, so the real release that follows does nothing. This replaces the PR's moved click block and press_consumed_point flag, which could stay set when the release landed outside the viewport and swallow the next release, and which broke TRIM's press-and-drag fence. Co-Authored-By: Claude Opus 5.5 --- src/app/update/viewport.rs | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index 3fa623065..d10c67fda 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -3738,6 +3738,12 @@ impl OpenCADStudio { } } + // A command step that takes a point or a pick commits on the press, + // so the cursor moving before the button comes up cannot move it. + // Selection steps and drag-fence picks still wait for the release. + let picks_on_press = self.tabs[i].active_cmd.as_ref().is_some_and(|command| { + !command.is_selection_gathering() && !command.accepts_drag_selection() + }) && self.tabs[i].scene.selection.borrow().box_anchor.is_none(); let mut sel = self.tabs[i].scene.selection.borrow_mut(); sel.left_down = true; // Stored in full-canvas space (like ViewportMove's cursor and @@ -3746,6 +3752,12 @@ impl OpenCADStudio { sel.left_press_pos = Some(p_full); sel.left_press_time = Some(Instant::now()); sel.left_dragging = false; + drop(sel); + if picks_on_press { + // The command path clears `left_down`, so the real release + // that follows finds nothing pressed and does nothing. + return self.on_viewport_left_release(); + } Task::none() } @@ -7245,6 +7257,44 @@ mod selection_preview_tests { } } + #[test] + fn a_point_step_takes_the_point_where_the_button_went_down() { + use codec::EntityType; + let mut app = OpenCADStudio::new_for_test(); + app.automation_op(r#"{"op":"new"}"#); + let i = app.active_tab; + app.tabs[i].scene.selection.borrow_mut().vp_size = (800.0, 600.0); + let bounds = iced::Rectangle::with_size(iced::Size::new(800.0, 600.0)); + let cursor = |app: &OpenCADStudio, x: f64, y: f64| { + let at = app.tabs[i].scene.camera.borrow().project(glam::DVec3::new(x, y, 0.0), bounds); + let at = at.unwrap(); + iced::Point::new(at.x, at.y) + }; + let _ = app.run_command_line("LINE"); + // Away from the origin, where the UCS icon takes presses. + for (down, up) in [((40.0, 30.0), (45.0, 34.0)), ((80.0, 30.0), (86.0, 25.0))] { + let _ = app.on_viewport_move(cursor(&app, down.0, down.1)); + let _ = app.on_viewport_left_press(); + // The hand moves before the button comes up. + let _ = app.on_viewport_move(cursor(&app, up.0, up.1)); + let _ = app.on_viewport_left_release(); + } + let _ = app.feed_command(crate::command::StepInput::Enter); + let lines: Vec<_> = app.tabs[i] + .scene + .document + .entities() + .filter_map(|entity| match entity { + EntityType::Line(line) => Some(line.clone()), + _ => None, + }) + .collect(); + assert_eq!(lines.len(), 1, "one point per click, not one per press and release"); + let line = &lines[0]; + assert!((line.start.x - 40.0).abs() < 0.5 && (line.start.y - 30.0).abs() < 0.5, "{line:?}"); + assert!((line.end.x - 80.0).abs() < 0.5 && (line.end.y - 30.0).abs() < 0.5, "{line:?}"); + } + #[test] fn xline_direction_grip_drag_changes_direction() { use codec::{types::Vector3, EntityType};