forked from 1b-agent/opencadstudio-upstream
navigation: selectable mouse navigation style
Options → User Preferences → Navigation style picks which mouse gestures pan and orbit the model view (#1480): - CAD (default): middle button pans, Shift + middle button orbits. - Blender: middle button orbits, Shift + middle button pans. - CATIA: middle button pans; pressing the left button during it orbits, so 3D navigation needs only the mouse. An armed orbit tool always orbits. The choice is saved with the user settings. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
74df9ff84a
commit
19ecf78bd9
30 files changed
+183
-1
No files matched your search
@@ -650,6 +650,11 @@ pub(super) struct OpenCADStudio {
|
||||
polar_increment_deg: f32,
|
||||
/// Reverse the mouse-wheel zoom direction when true (ZOOMWHEEL = 1).
|
||||
zoom_wheel_reversed: bool,
|
||||
/// Which mouse gestures pan and orbit the model view.
|
||||
navigation_style: crate::app::settings::NavigationStyle,
|
||||
/// CATIA navigation: the left button went down while the middle one was
|
||||
/// held, so the middle drag orbits until it is released.
|
||||
nav_left_with_middle: bool,
|
||||
/// Scroll pans and Ctrl+scroll zooms (touchpads); see `scroll_intent`.
|
||||
touchpad_pan: bool,
|
||||
/// Multi-finger gestures on a touch screen.
|
||||
@@ -2657,6 +2662,7 @@ pub enum Message {
|
||||
CommandLineOpacityChanged(u8),
|
||||
/// Toggle reversing the mouse-wheel zoom direction (ZOOMWHEEL).
|
||||
ZoomWheelReversedChanged(bool),
|
||||
NavigationStyleChanged(crate::app::settings::NavigationStyle),
|
||||
TouchpadPanChanged(bool),
|
||||
/// Options: show the pull-down menus (MENUBAR).
|
||||
MenuBarChanged(bool),
|
||||
@@ -4632,6 +4638,8 @@ impl OpenCADStudio {
|
||||
polar_mode: false,
|
||||
polar_increment_deg: 45.0,
|
||||
zoom_wheel_reversed: false,
|
||||
navigation_style: Default::default(),
|
||||
nav_left_with_middle: false,
|
||||
touchpad_pan: false,
|
||||
touch: Default::default(),
|
||||
touch_offset: None,
|
||||
|
||||
@@ -204,6 +204,33 @@ pub fn zoom_notch_percent(factor: i32) -> f32 {
|
||||
(1.0 - ratio) * 100.0
|
||||
}
|
||||
|
||||
/// Which mouse gestures pan and orbit the model view.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum NavigationStyle {
|
||||
/// Middle button pans; Shift + middle button orbits.
|
||||
#[default]
|
||||
Cad,
|
||||
/// Middle button orbits; Shift + middle button pans.
|
||||
Blender,
|
||||
/// Middle button pans; middle and left buttons together orbit.
|
||||
Catia,
|
||||
}
|
||||
|
||||
impl NavigationStyle {
|
||||
pub const ALL: [NavigationStyle; 3] = [Self::Cad, Self::Blender, Self::Catia];
|
||||
}
|
||||
|
||||
impl std::fmt::Display for NavigationStyle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&match self {
|
||||
Self::Cad => crate::t!("CAD: Shift + middle button orbits"),
|
||||
Self::Blender => crate::t!("Blender: middle button orbits, Shift pans"),
|
||||
Self::Catia => crate::t!("CATIA: middle + left button orbits"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Active pair of axes while isometric drafting is enabled.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum IsoPlane {
|
||||
@@ -388,6 +415,7 @@ pub struct UserSettings {
|
||||
pub polar: bool,
|
||||
pub polar_increment_deg: f32,
|
||||
pub zoom_wheel_reversed: bool,
|
||||
pub navigation_style: NavigationStyle,
|
||||
/// Scrolling pans and Ctrl+scroll (a touchpad pinch) zooms, for a
|
||||
/// touchpad that reports two-finger drags as wheel scrolls.
|
||||
pub touchpad_pan: bool,
|
||||
@@ -891,6 +919,7 @@ impl Default for UserSettings {
|
||||
polar: false,
|
||||
polar_increment_deg: 45.0,
|
||||
zoom_wheel_reversed: false,
|
||||
navigation_style: NavigationStyle::Cad,
|
||||
touchpad_pan: false,
|
||||
ui_scale_percent: 100,
|
||||
zoom_factor: 60,
|
||||
|
||||
@@ -778,6 +778,7 @@ impl OpenCADStudio {
|
||||
polar: self.polar_mode,
|
||||
polar_increment_deg: self.polar_increment_deg,
|
||||
zoom_wheel_reversed: self.zoom_wheel_reversed,
|
||||
navigation_style: self.navigation_style,
|
||||
touchpad_pan: self.touchpad_pan,
|
||||
ui_scale_percent: self.ui_scale_percent,
|
||||
zoom_factor: self.zoom_factor,
|
||||
@@ -913,6 +914,7 @@ impl OpenCADStudio {
|
||||
self.polar_mode = s.polar;
|
||||
self.polar_increment_deg = s.polar_increment_deg;
|
||||
self.zoom_wheel_reversed = s.zoom_wheel_reversed;
|
||||
self.navigation_style = s.navigation_style;
|
||||
self.touchpad_pan = s.touchpad_pan;
|
||||
self.ui_scale_percent = crate::app::settings::clamp_ui_scale(s.ui_scale_percent);
|
||||
self.zoom_factor = crate::app::settings::clamp_zoom_factor(s.zoom_factor);
|
||||
|
||||
@@ -4482,6 +4482,25 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
// CATIA navigation: the left button pressed during a middle drag
|
||||
// turns the drag into an orbit instead of starting a pick.
|
||||
Message::ViewportLeftPressAt(_) | Message::ViewportLeftPress | Message::PanePress(_)
|
||||
if self.navigation_style == crate::app::settings::NavigationStyle::Catia
|
||||
&& self.tabs[self.active_tab]
|
||||
.scene
|
||||
.selection
|
||||
.borrow()
|
||||
.input
|
||||
.middle_down =>
|
||||
{
|
||||
self.nav_left_with_middle = true;
|
||||
Task::none()
|
||||
}
|
||||
Message::ViewportLeftRelease | Message::PaneRelease(_) if self.nav_left_with_middle => {
|
||||
self.nav_left_with_middle = false;
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::ViewportMove(p) => self.on_viewport_move(p),
|
||||
|
||||
Message::ViewportExit => self.on_viewport_exit(),
|
||||
@@ -4686,6 +4705,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
Message::ViewportMiddleRelease => {
|
||||
self.nav_left_with_middle = false;
|
||||
let i = self.active_tab;
|
||||
let mut sel = self.tabs[i].scene.selection.borrow_mut();
|
||||
sel.input.middle_down = false;
|
||||
@@ -9225,6 +9245,12 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::NavigationStyleChanged(style) => {
|
||||
self.navigation_style = style;
|
||||
self.persist_settings_if_changed();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::MenuBarChanged(shown) => {
|
||||
self.show_menu_bar = shown;
|
||||
self.persist_settings_if_changed();
|
||||
|
||||
@@ -1416,7 +1416,7 @@ impl OpenCADStudio {
|
||||
// — the requested Zoom=wheel / Pan=MMB / Rotate=Shift+MMB
|
||||
// scheme (#229). Floating viewports and paper keep the
|
||||
// plain MMB pan.
|
||||
if self.shift_down || self.tabs[i].orbit_mode {
|
||||
if self.middle_drag_orbits(i) {
|
||||
if self.tabs[i].scene.active_viewport.is_some() {
|
||||
// Orbit the floating viewport's own model view.
|
||||
drop(sel);
|
||||
@@ -3165,6 +3165,18 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// Whether a middle-button drag orbits rather than pans, by the
|
||||
/// navigation style (an armed orbit tool always orbits).
|
||||
fn middle_drag_orbits(&self, i: usize) -> bool {
|
||||
use crate::app::settings::NavigationStyle;
|
||||
self.tabs[i].orbit_mode
|
||||
|| match self.navigation_style {
|
||||
NavigationStyle::Cad => self.shift_down,
|
||||
NavigationStyle::Blender => !self.shift_down,
|
||||
NavigationStyle::Catia => self.nav_left_with_middle,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn on_viewport_exit(&mut self) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
self.tabs[i].measurement.live_visible = false;
|
||||
|
||||
@@ -312,6 +312,7 @@ impl OpenCADStudio {
|
||||
show_ribbon: !self.ribbon_closed,
|
||||
hatch_associative: self.hatch_associative,
|
||||
zoom_wheel_reversed: self.zoom_wheel_reversed,
|
||||
navigation_style: self.navigation_style,
|
||||
touchpad_pan: self.touchpad_pan,
|
||||
ui_scale_percent: self.ui_scale_percent,
|
||||
zoom_factor: self.zoom_factor,
|
||||
|
||||
@@ -5070,6 +5070,10 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
||||
"User Preferences" => Some(("common", "user-preferences")),
|
||||
"Zoom" => Some(("common", "zoom")),
|
||||
"Reverse mouse wheel zoom (ZOOMWHEEL)" => Some(("common", "reverse-mouse-wheel-zoom-zoomwheel")),
|
||||
"Navigation style" => Some(("common", "navigation-style")),
|
||||
"CAD: Shift + middle button orbits" => Some(("common", "nav-style-cad")),
|
||||
"Blender: middle button orbits, Shift pans" => Some(("common", "nav-style-blender")),
|
||||
"CATIA: middle + left button orbits" => Some(("common", "nav-style-catia")),
|
||||
"Touchpad: scroll pans, Ctrl+scroll or pinch zooms" => Some(("common", "touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms")),
|
||||
"Interface scale" => Some(("common", "interface-scale")),
|
||||
"Zoom factor" => Some(("common", "zoom-factor")),
|
||||
|
||||
@@ -63,6 +63,8 @@ pub struct AppPrefs {
|
||||
pub hatch_associative: bool,
|
||||
/// ZOOMWHEEL: reverse the mouse-wheel zoom direction.
|
||||
pub zoom_wheel_reversed: bool,
|
||||
/// Which mouse gestures pan and orbit the model view.
|
||||
pub navigation_style: crate::app::settings::NavigationStyle,
|
||||
/// Scroll pans and Ctrl+scroll zooms (touchpads).
|
||||
pub touchpad_pan: bool,
|
||||
/// Interface scale in percent.
|
||||
@@ -1274,6 +1276,16 @@ pub fn view_window<'a>(
|
||||
Space::new().height(10),
|
||||
spacemouse,
|
||||
Space::new().height(12),
|
||||
text(crate::t!("Navigation style")).size(15),
|
||||
Space::new().height(10),
|
||||
iced::widget::pick_list(
|
||||
Some(prefs.navigation_style),
|
||||
crate::app::settings::NavigationStyle::ALL,
|
||||
|style| style.to_string(),
|
||||
)
|
||||
.on_select(Message::NavigationStyleChanged)
|
||||
.text_size(12),
|
||||
Space::new().height(12),
|
||||
text(crate::t!("Zoom")).size(15),
|
||||
Space::new().height(10),
|
||||
row![
|
||||
|
||||
Reference in new issue
Block a user