Navigation: a Blender preset (#1356)

Middle drag orbits, Shift+middle pans, Ctrl+middle zooms; right drag leaves the camera alone. Opt-in preset; the default is unchanged. Fixes #1090.
This commit is contained in:
DuBearte authored and GitHub committed 2026-10-15 08:22:46 -04:00
1 parent 56622e4be8
commit d05fe3d4de
3 files changed
+34 -4

No files matched your search

+32 -2
View File
@@ -94,7 +94,7 @@ use NavButton::{Left, Middle, Right};
use NavMode::{Orbit, Pan, Zoom};
/// The mouse mappings. The first is the default.
pub const NAV_PRESETS: [NavPreset; 7] = [
pub const NAV_PRESETS: [NavPreset; 8] = [
NavPreset {
id: "fusion",
label: "Fusion",
@@ -140,6 +140,13 @@ pub const NAV_PRESETS: [NavPreset; 7] = [
bind(Right, NavKeys::NONE, Orbit),
],
},
NavPreset {
id: "blender",
label: "Blender",
help: "Middle drag orbits, Shift+middle pans, Ctrl+middle zooms; right drag leaves the camera alone",
reverse_zoom: false,
bindings: &[bind(Middle, NavKeys::SHIFT, Pan), bind(Middle, NavKeys::CTRL, Zoom), bind(Middle, NavKeys::NONE, Orbit)],
},
NavPreset {
id: "tinkercad",
label: "Tinkercad",
@@ -1659,6 +1666,29 @@ mod tests {
assert!(!app.preferences.trackpad_pan);
}
#[test]
fn blender_preset_moves_the_camera_with_the_middle_button_only() {
// #1090: orbit, pan and zoom all on the middle button; no left or right drag binding, so
// those stay selection and the context menu (Ctrl+left on empty space orbits in every
// preset, outside the bindings).
let mut app = SolveApp::new(solidcraft_engine::Session::default(), Default::default());
set(&mut app, &json!({"nav": "blender"})).unwrap();
let none = Modifiers::default();
let shift = Modifiers { shift: true, ..Default::default() };
let ctrl = Modifiers { ctrl: true, ..Default::default() };
assert_eq!(nav_mode(&app, NavButton::Middle, none), Some(NavMode::Orbit));
assert_eq!(nav_mode(&app, NavButton::Middle, shift), Some(NavMode::Pan));
assert_eq!(nav_mode(&app, NavButton::Middle, ctrl), Some(NavMode::Zoom));
for mods in [none, shift, ctrl] {
assert_eq!(nav_mode(&app, NavButton::Left, mods), None);
assert_eq!(nav_mode(&app, NavButton::Right, mods), None);
}
assert_eq!(zoom_sign(&app), 1.0, "the wheel turns the usual way");
let mut other = SolveApp::new(solidcraft_engine::Session::default(), Default::default());
other.load_prefs(&app.prefs());
assert_eq!(other.preferences.nav, "blender");
}
#[test]
fn presets_map_the_mouse_and_prefs_round_trip() {
let mut app = SolveApp::new(solidcraft_engine::Session::default(), Default::default());
@@ -1684,7 +1714,7 @@ mod tests {
assert_eq!(nav_mode(&app, NavButton::Left, none), None, "a plain left drag still selects");
assert_eq!(nav_mode(&app, NavButton::Left, shift), None);
assert_eq!(nav_mode(&app, NavButton::Right, none), Some(NavMode::Orbit));
assert!(set(&mut app, &json!({"nav": "blender"})).is_err());
assert!(set(&mut app, &json!({"nav": "no-such-preset"})).is_err());
assert!(set(&mut app, &json!({"default_units": "furlong"})).is_err());
assert!(set(&mut app, &json!({"nope": 1})).is_err());
// Kept between runs.
+1 -1
View File
@@ -25,7 +25,7 @@
Esc or a click elsewhere closes the list. Clicking again on the same spot, with nothing changed
in between, selects the next thing in that list. The
wheel zooms at the cursor, the middle button pans and right-drag orbits freely (the mouse
preset in Preferences switches to SolidWorks, Inventor, Alias, Tinkercad, OpenSCAD or Touchpad style). On a
preset in Preferences switches to SolidWorks, Inventor, Alias, Blender, Tinkercad, OpenSCAD or Touchpad style). On a
trackpad, pinch zooms and two-finger scroll pans (Navigation › Two-finger scroll pans;
off: scroll zooms like the wheel); mouse wheels zoom and the thumb wheel pans sideways.
Shift+middle orbits freely
+1 -1
View File
@@ -132,7 +132,7 @@ unit regardless of the design, pass an expression with one: `"value": "50 mm"` o
| `ui.home` | `open?`: bool; `sample?`: index | the start page: show or hide it, open a built-in sample (once built); returns `open` and the recent designs |
| `ui.shortcut` | `command`, `key?`, `replace?` | a command's key, or rebinds it (`""` clears; a key another command uses is refused unless `replace`) |
| `ui.help` | `item?`: `about`, `shortcuts` (read only), `report` (opens a report draft for review), `crashes` (the Crash Reports window) | the version, commit, build date and diagnostic text |
| `ui.prefs` | any of `default_units`, `nav` (`fusion`, `solidworks`, `inventor`, `alias`, `tinkercad`, `openscad`, `touchpad`), `zoom_reverse`, `zoom_to_cursor`, `smooth_wheel_zoom` (default true), `trackpad_pan` (default true: two-finger scroll pans, except Touchpad always zooms; pinch zooms), `orbit_cursor`, `look_at_anim`, `look_at_speed`, `middle_double_click` (`home` (default), `fit`, `off`), `msaa`, `length_decimals`, `angle_decimals`, `theme` (`dark`, `light`, `system`), `language` (`en`, `ja`, `de`, `fr`, `ko`, `ru`, `uk`, `zh`; see [localization](localization.md)), `ui_scale`, `up_axis` (`z`, `y`), `offer_stand_up`, `color_vision` (`off` (default), `protan`, `deutan`, `tritan`: colour vision modes, #778), `check_updates` (default true; [updates](book/src/guide/updates.md)); `open` (a page: General, Navigation, Keyboard Shortcuts, Design, Graphics, Sketch, Selection) opens the Preferences dialog there; `button`: `ok`, `cancel`, `apply`, `defaults`; `stand_up: true` stands up a just-opened Y-up file | sets preferences (kept between runs), returns them all plus `dialog` (the open page or null), `dark` and `stand_up_offer` |
| `ui.prefs` | any of `default_units`, `nav` (`fusion`, `solidworks`, `inventor`, `alias`, `blender`, `tinkercad`, `openscad`, `touchpad`), `zoom_reverse`, `zoom_to_cursor`, `smooth_wheel_zoom` (default true), `trackpad_pan` (default true: two-finger scroll pans, except Touchpad always zooms; pinch zooms), `orbit_cursor`, `look_at_anim`, `look_at_speed`, `middle_double_click` (`home` (default), `fit`, `off`), `msaa`, `length_decimals`, `angle_decimals`, `theme` (`dark`, `light`, `system`), `language` (`en`, `ja`, `de`, `fr`, `ko`, `ru`, `uk`, `zh`; see [localization](localization.md)), `ui_scale`, `up_axis` (`z`, `y`), `offer_stand_up`, `color_vision` (`off` (default), `protan`, `deutan`, `tritan`: colour vision modes, #778), `check_updates` (default true; [updates](book/src/guide/updates.md)); `open` (a page: General, Navigation, Keyboard Shortcuts, Design, Graphics, Sketch, Selection) opens the Preferences dialog there; `button`: `ok`, `cancel`, `apply`, `defaults`; `stand_up: true` stands up a just-opened Y-up file | sets preferences (kept between runs), returns them all plus `dialog` (the open page or null), `dark` and `stand_up_offer` |
| `ui.capture` | `action?`: `capture`, `revert`, `cancel` | answers the Capture Position question (asked when a command starts or the design is saved while components were moved without capturing); without `action`, what waits |
| `ui.window` | `action`: minimize, maximize, restore, toggle, close | what the title bar's caption buttons do (on Windows and Linux the application bar is the title bar); close asks about unsaved designs first |
| `ui.agent_cursor` | `show` (bool), `speed`: a number from 1 to 5 (times the normal speed) or `"instant"` (no animation: the command runs at once and the pointer jumps to where it acted), `follow_camera` (bool); all optional | the settings. While the cursor is shown, `engine.execute` first glides to the command's toolbar button and to each pick, then runs and replies; later requests wait their turn. `"animate": false` on a call skips it |