Preferences: reverse horizontal pan direction (#2007) (#2047)

Thanks @lasselian.
This commit is contained in:
Lasse Lian authored and GitHub committed 2026-10-18 03:12:23 -04:00
1 parent 8f768b65c8
commit b685b7301f
6 files changed
+34 -2

No files matched your search

+2
View File
@@ -1369,6 +1369,8 @@
"ui.reset_the_settings_on_this_page": "Reset the settings on this page",
"ui.restore": "Restore",
"ui.restore_defaults": "Restore Defaults",
"ui.reverse_horizontal_pan_direction": "Reverse horizontal pan direction",
"ui.reverse_horizontal_pan_tip": "Horizontal two-finger scrolling and the thumb wheel pan the other way",
"ui.reverse_zoom_direction": "Reverse zoom direction",
"ui.revolve": "Revolve",
"ui.right": "Right",
+2
View File
@@ -1369,6 +1369,8 @@
"ui.reset_the_settings_on_this_page": "このページの設定をリセット",
"ui.restore": "復元",
"ui.restore_defaults": "初期値に戻す",
"ui.reverse_horizontal_pan_direction": "水平パンの方向を反転",
"ui.reverse_horizontal_pan_tip": "水平方向の2本指スクロールとサムホイールで、反対方向にパンします",
"ui.reverse_zoom_direction": "ズーム方向を反転",
"ui.revolve": "回転",
"ui.right": "右側面",
+2
View File
@@ -1369,6 +1369,8 @@
"ui.reset_the_settings_on_this_page": "重置此页面上的设置",
"ui.restore": "还原",
"ui.restore_defaults": "恢复默认值",
"ui.reverse_horizontal_pan_direction": "反转水平平移方向",
"ui.reverse_horizontal_pan_tip": "水平双指滚动和拇指滚轮向相反方向平移",
"ui.reverse_zoom_direction": "反转缩放方向",
"ui.revolve": "旋转",
"ui.right": "右视",
+2 -1
View File
@@ -307,7 +307,8 @@ pub(crate) fn handle(app: &mut SolveApp, ui: &mut egui::Ui, rect: Rect, resp: &e
// (its End was lost); a stuck flag would pan future mouse input.
app.viewport.trackpad_gesture = false;
}
let pan = if trackpad_pan { raw.pan } else { egui::Vec2::ZERO } + raw.mouse_pan;
let mut pan = if trackpad_pan { raw.pan } else { egui::Vec2::ZERO } + raw.mouse_pan;
pan.x *= crate::prefs::pan_sign_x(app);
let scroll = if app.viewport.wheel_zoom_raw_until_idle || (trackpad_pan && app.viewport.trackpad_until_idle) {
raw.zoom + if trackpad_pan { 0.0 } else { raw.point_zoom }
} else {
+25
View File
@@ -321,6 +321,8 @@ pub struct Prefs {
pub smooth_wheel_zoom: bool,
/// Two-finger trackpad scroll pans (off: it zooms like the wheel); pinch always zooms.
pub trackpad_pan: bool,
/// Horizontal trackpad and thumb-wheel scrolling pans the view the other way (#2007).
pub pan_reverse_x: bool,
/// View changes (axis view, Look At, Home) animate; `look_at_speed` scales how fast.
pub look_at_anim: bool,
pub look_at_speed: f32,
@@ -432,6 +434,7 @@ impl Default for Prefs {
zoom_to_cursor: true,
smooth_wheel_zoom: true,
trackpad_pan: true,
pan_reverse_x: false,
look_at_anim: true,
look_at_speed: 1.0,
up_axis: "z".into(),
@@ -626,6 +629,7 @@ fn defaults(app: &mut SolveApp, page: usize) {
p.zoom_to_cursor = d.zoom_to_cursor;
p.smooth_wheel_zoom = d.smooth_wheel_zoom;
p.trackpad_pan = d.trackpad_pan;
p.pan_reverse_x = d.pan_reverse_x;
p.orbit_around = d.orbit_around;
p.orbit_cursor = d.orbit_cursor;
p.space_mouse_enabled = d.space_mouse_enabled;
@@ -729,6 +733,11 @@ pub fn nav_mode(app: &SolveApp, button: NavButton, mods: Modifiers) -> Option<Na
nav_preset(&app.preferences.nav).mode(button, mods)
}
/// +1, or −1 when horizontal scrolling pans the other way (#2007).
pub fn pan_sign_x(app: &SolveApp) -> f32 {
if app.preferences.pan_reverse_x { -1.0 } else { 1.0 }
}
/// +1, or −1 when the wheel zooms the other way.
pub fn zoom_sign(app: &SolveApp) -> f64 {
if app.preferences.zoom_reverse { -1.0 } else { 1.0 }
@@ -1158,6 +1167,10 @@ fn page(app: &mut SolveApp, ui: &mut egui::Ui, t: &Tokens) {
crate::i18n::tr_label("Trackpad gestures pan (off: they zoom like the wheel); mouse wheel zooms, thumb wheel pans sideways, pinch always zooms")
});
ui.end_row();
ui.label(crate::i18n::tr_label("Reverse horizontal pan direction"));
ui.checkbox(&mut app.preferences.pan_reverse_x, "")
.on_hover_text(crate::i18n::tr_label("Horizontal two-finger scrolling and the thumb wheel pan the other way"));
ui.end_row();
ui.label(crate::i18n::tr_label("Orbit around"));
// Wrapped: three choices, and German or Russian labels are long.
ui.horizontal_wrapped(|ui| {
@@ -1838,6 +1851,18 @@ mod tests {
assert_eq!(other.preferences.nav, OPENSCAD_NAV);
}
#[test]
fn pan_reverse_x_defaults_off_persists_and_flips_the_sign() {
assert!(!serde_json::from_value::<Prefs>(json!({})).unwrap().pan_reverse_x);
let mut app = SolveApp::new(solidcraft_engine::Session::default(), Default::default());
assert_eq!(pan_sign_x(&app), 1.0);
set(&mut app, &json!({"pan_reverse_x": true})).unwrap();
assert_eq!(pan_sign_x(&app), -1.0);
let mut other = SolveApp::new(solidcraft_engine::Session::default(), Default::default());
other.load_prefs(&app.prefs());
assert!(other.preferences.pan_reverse_x);
}
#[test]
fn trackpad_pan_defaults_on_persists_and_follows_dialog_buttons() {
assert!(serde_json::from_value::<Prefs>(json!({})).unwrap().trackpad_pan);
+1 -1
View File
@@ -152,7 +152,7 @@ labels; `prefs:measure_axis_distances` identifies the checkbox.
| `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`, `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_around` (`view_centre` (default: what Pan moves and Fit frames), `origin` (the world origin stays still on screen), `cursor` (the model point under the pointer when the drag began); the older `orbit_cursor` boolean still sets `cursor` when true), `look_at_anim`, `look_at_speed`, `middle_double_click` (`home` (default), `fit`, `off`), `msaa`, `show_frame_rate` (default false: frames per second in the view's bottom-right corner; the view redraws every frame while it is on), `length_decimals`, `angle_decimals`, `theme` (`dark`, `light`, `system`), `language` (`en`, `ja`, `de`, `fr`, `ko`, `ru`, `uk`, `zh`; see [localization](localization.md)), `ui_scale`, `ui_font` (an installed font family or a font file for the interface; empty for the built-in font), `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, AI) opens the Preferences dialog there; `button`: `ok`, `cancel`, `apply`, `defaults`; `stand_up: true` stands up a just-opened Y-up file, `stand_up: false` leaves it as it is ("Leave it": the offer closes and `offer_stand_up` stays on); the offer's buttons are the handles `stand_up:stand`, `stand_up:leave` and `stand_up:never` | 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`, `pan_reverse_x` (default false: horizontal scrolling pans the other way), `zoom_to_cursor`, `smooth_wheel_zoom` (default true), `trackpad_pan` (default true: two-finger scroll pans, except Touchpad always zooms; pinch zooms), `orbit_around` (`view_centre` (default: what Pan moves and Fit frames), `origin` (the world origin stays still on screen), `cursor` (the model point under the pointer when the drag began); the older `orbit_cursor` boolean still sets `cursor` when true), `look_at_anim`, `look_at_speed`, `middle_double_click` (`home` (default), `fit`, `off`), `msaa`, `show_frame_rate` (default false: frames per second in the view's bottom-right corner; the view redraws every frame while it is on), `length_decimals`, `angle_decimals`, `theme` (`dark`, `light`, `system`), `language` (`en`, `ja`, `de`, `fr`, `ko`, `ru`, `uk`, `zh`; see [localization](localization.md)), `ui_scale`, `ui_font` (an installed font family or a font file for the interface; empty for the built-in font), `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, AI) opens the Preferences dialog there; `button`: `ok`, `cancel`, `apply`, `defaults`; `stand_up: true` stands up a just-opened Y-up file, `stand_up: false` leaves it as it is ("Leave it": the offer closes and `offer_stand_up` stays on); the offer's buttons are the handles `stand_up:stand`, `stand_up:leave` and `stand_up:never` | 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 |