forked from 1b-agent/opencadstudio-upstream
feat(view): complete ZOOM options
Add command-line navigation modes and persistent wheel controls.\n\nRefs #412
This commit is contained in:
1 parent
3f103d2f8b
commit
adb8ced8b5
17 files changed
+380
-23
No files matched your search
@@ -660,16 +660,22 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
// drag. Orbit exits silently; PAN keeps its existing message.
|
||||
if self.tabs[self.active_tab].pan_mode
|
||||
|| self.tabs[self.active_tab].orbit_mode
|
||||
|| self.tabs[self.active_tab].zoom_dynamic_mode
|
||||
{
|
||||
let i = self.active_tab;
|
||||
let was_pan = self.tabs[i].pan_mode;
|
||||
self.tabs[i].pan_mode = false;
|
||||
self.tabs[i].orbit_mode = false;
|
||||
self.tabs[i].zoom_dynamic_mode = false;
|
||||
{
|
||||
let mut sel = self.tabs[i].scene.selection.borrow_mut();
|
||||
sel.middle_down = false;
|
||||
sel.middle_last_pos = None;
|
||||
sel.orbit_pivot = None;
|
||||
sel.box_anchor = None;
|
||||
sel.box_anchor_world = None;
|
||||
sel.box_current = None;
|
||||
sel.box_crossing_locked = false;
|
||||
}
|
||||
if was_pan {
|
||||
self.command_line.push_output(crate::t!("PAN ended.").as_ref());
|
||||
|
||||
@@ -143,6 +143,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||
&& self.active_modal.is_none()
|
||||
&& !self.tabs[i].pan_mode
|
||||
&& !self.tabs[i].orbit_mode
|
||||
&& !self.tabs[i].zoom_dynamic_mode
|
||||
{
|
||||
self.ribbon.deactivate_tool();
|
||||
}
|
||||
|
||||
@@ -265,6 +265,8 @@ impl OpenCADStudio {
|
||||
dyn_input: self.dyn_input,
|
||||
polar: self.polar_mode,
|
||||
polar_increment_deg: self.polar_increment_deg,
|
||||
zoom_wheel_reversed: self.zoom_wheel_reversed,
|
||||
zoom_factor: self.zoom_factor,
|
||||
otrack: self.snapper.otrack_enabled,
|
||||
default_assoc_prompted: self.default_assoc_prompted,
|
||||
disabled_plugins: {
|
||||
@@ -299,6 +301,8 @@ impl OpenCADStudio {
|
||||
self.dyn_input = s.dyn_input;
|
||||
self.polar_mode = s.polar;
|
||||
self.polar_increment_deg = s.polar_increment_deg;
|
||||
self.zoom_wheel_reversed = s.zoom_wheel_reversed;
|
||||
self.zoom_factor = s.zoom_factor.clamp(3, 100);
|
||||
// Ortho + running OSNAP are per-drawing (adopted from the header on
|
||||
// open / tab switch), not app-global, so they are not applied here.
|
||||
self.snapper.otrack_enabled = s.otrack;
|
||||
|
||||
@@ -2831,6 +2831,7 @@ impl OpenCADStudio {
|
||||
Message::ViewCubeHome => {
|
||||
let i = self.active_tab;
|
||||
self.clear_navigation_hover(i);
|
||||
self.tabs[i].scene.remember_current_view();
|
||||
let r_ucs = self.tabs[i].scene.viewcube_ucs_mat();
|
||||
if self.tabs[i].scene.active_viewport.is_some() {
|
||||
self.tabs[i]
|
||||
@@ -2847,6 +2848,7 @@ impl OpenCADStudio {
|
||||
Message::ViewCubeRoll(cw) => {
|
||||
let i = self.active_tab;
|
||||
self.clear_navigation_hover(i);
|
||||
self.tabs[i].scene.remember_current_view();
|
||||
let ang = if cw {
|
||||
std::f32::consts::FRAC_PI_2
|
||||
} else {
|
||||
@@ -2873,6 +2875,7 @@ impl OpenCADStudio {
|
||||
};
|
||||
let i = self.active_tab;
|
||||
self.clear_navigation_hover(i);
|
||||
self.tabs[i].scene.remember_current_view();
|
||||
if self.tabs[i].scene.active_viewport.is_some() {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
|
||||
@@ -739,6 +739,39 @@ impl OpenCADStudio {
|
||||
if mid_down {
|
||||
if let Some(last) = mid_last {
|
||||
let (dx, dy) = (p.x - last.x, p.y - last.y);
|
||||
if self.tabs[i].zoom_dynamic_mode {
|
||||
let bounds = self.tabs[i]
|
||||
.scene
|
||||
.active_model_tile_bounds(vp_size.0, vp_size.1);
|
||||
drop(sel);
|
||||
let zoom_delta = -dy * 0.03;
|
||||
if self.tabs[i].scene.active_viewport.is_some() {
|
||||
self.tabs[i].scene.pan_active_viewport(dx, 0.0, bounds);
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.zoom_active_viewport(zoom_delta, None);
|
||||
} else {
|
||||
let local = iced::Point {
|
||||
x: p.x - bounds.x,
|
||||
y: p.y - bounds.y,
|
||||
};
|
||||
let local_bounds = iced::Rectangle {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
width: bounds.width,
|
||||
height: bounds.height,
|
||||
};
|
||||
let mut camera = self.tabs[i].scene.camera.borrow_mut();
|
||||
camera.pan_screen(dx, 0.0, bounds.height);
|
||||
camera.zoom_about_point(local, local_bounds, zoom_delta);
|
||||
}
|
||||
self.tabs[i].scene.camera_generation += 1;
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.record_nav_perf(crate::scene::NavPerfOp::Zoom, move_started);
|
||||
self.tabs[i].scene.selection.borrow_mut().middle_last_pos = Some(p);
|
||||
return Task::none();
|
||||
}
|
||||
// Shift+MMB drag orbits the model view instead of panning
|
||||
// — the requested Zoom=wheel / Pan=MMB / Rotate=Shift+MMB
|
||||
// scheme (#229). Floating viewports and paper keep the
|
||||
@@ -2288,11 +2321,21 @@ impl OpenCADStudio {
|
||||
|
||||
// Interactive navigation tools reuse the middle-button movement path,
|
||||
// so no selection/pick logic runs while the left button drives them.
|
||||
if self.tabs[i].orbit_mode || self.tabs[i].pan_mode {
|
||||
if self.tabs[i].orbit_mode
|
||||
|| self.tabs[i].pan_mode
|
||||
|| self.tabs[i].zoom_dynamic_mode
|
||||
{
|
||||
self.clear_navigation_hover(i);
|
||||
self.tabs[i].scene.remember_current_view();
|
||||
let mut sel = self.tabs[i].scene.selection.borrow_mut();
|
||||
sel.middle_down = true;
|
||||
sel.middle_last_pos = Some(p);
|
||||
if self.tabs[i].zoom_dynamic_mode {
|
||||
sel.box_anchor = Some(p);
|
||||
sel.box_current = Some(p);
|
||||
sel.box_crossing = false;
|
||||
sel.box_crossing_locked = true;
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
@@ -2460,11 +2503,18 @@ impl OpenCADStudio {
|
||||
|
||||
// Navigation mode: end this drag but keep the tool armed for the next
|
||||
// left drag (exit is Esc / another command). Mirror of the press.
|
||||
if self.tabs[i].orbit_mode || self.tabs[i].pan_mode {
|
||||
if self.tabs[i].orbit_mode
|
||||
|| self.tabs[i].pan_mode
|
||||
|| self.tabs[i].zoom_dynamic_mode
|
||||
{
|
||||
let mut sel = self.tabs[i].scene.selection.borrow_mut();
|
||||
sel.middle_down = false;
|
||||
sel.middle_last_pos = None;
|
||||
sel.orbit_pivot = None;
|
||||
sel.box_anchor = None;
|
||||
sel.box_anchor_world = None;
|
||||
sel.box_current = None;
|
||||
sel.box_crossing_locked = false;
|
||||
drop(sel);
|
||||
self.arm_hover_after_navigation(i);
|
||||
return Task::none();
|
||||
@@ -4007,6 +4057,7 @@ impl OpenCADStudio {
|
||||
let i = self.active_tab;
|
||||
self.clear_navigation_hover(i);
|
||||
self.ribbon.close_dropdown();
|
||||
self.tabs[i].scene.remember_current_view();
|
||||
let now = Instant::now();
|
||||
let is_double = {
|
||||
let sel = self.tabs[i].scene.selection.borrow();
|
||||
@@ -4035,11 +4086,16 @@ impl OpenCADStudio {
|
||||
|
||||
pub(super) fn on_viewport_scroll(&mut self, delta: mouse::ScrollDelta) -> Task<Message> {
|
||||
let nav_started = Instant::now();
|
||||
let s = match delta {
|
||||
let mut s = match delta {
|
||||
mouse::ScrollDelta::Lines { y, .. } => y,
|
||||
mouse::ScrollDelta::Pixels { y, .. } => y * 0.01,
|
||||
};
|
||||
s *= self.zoom_factor as f32 / 60.0;
|
||||
if self.zoom_wheel_reversed {
|
||||
s = -s;
|
||||
}
|
||||
let i = self.active_tab;
|
||||
self.tabs[i].scene.remember_current_view();
|
||||
self.clear_navigation_hover(i);
|
||||
let cursor = self.tabs[i].scene.selection.borrow().last_move_pos;
|
||||
let (vw, vh) = self.tabs[i].scene.selection.borrow().vp_size;
|
||||
@@ -4204,6 +4260,7 @@ impl OpenCADStudio {
|
||||
fn snap_view_region(&mut self, region: CubeRegion, r_ucs: glam::Mat4) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
self.clear_navigation_hover(i);
|
||||
self.tabs[i].scene.remember_current_view();
|
||||
let mut region = region;
|
||||
// "Already there → flip to opposite" check: compare the
|
||||
// current gaze direction with the region's target gaze.
|
||||
|
||||
Reference in new issue
Block a user