Add respecting OS setting for natural scroll direction
This commit is contained in:
1
interface.d.ts
vendored
1
interface.d.ts
vendored
@ -69,6 +69,7 @@ export interface IElectronAPI {
|
||||
kittycad: (access: string, args: any) => any
|
||||
listMachines: () => Promise<MachinesListing>
|
||||
getMachineApiIp: () => Promise<string | null>
|
||||
readNaturalScrollDirection: () => Promise<boolean>
|
||||
onUpdateDownloaded: (
|
||||
callback: (value: string) => void
|
||||
) => Electron.IpcRenderer
|
||||
|
@ -22,7 +22,12 @@ import {
|
||||
UnreliableSubscription,
|
||||
} from 'lang/std/engineConnection'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { toSync, uuidv4 } from 'lib/utils'
|
||||
import {
|
||||
cachedNaturalScrollDirection,
|
||||
refreshNaturalScrollDirection,
|
||||
toSync,
|
||||
uuidv4,
|
||||
} from 'lib/utils'
|
||||
import { deg2Rad } from 'lib/utils2d'
|
||||
import { isReducedMotion, roundOff, throttle } from 'lib/utils'
|
||||
import * as TWEEN from '@tweenjs/tween.js'
|
||||
@ -34,6 +39,9 @@ const ORTHOGRAPHIC_CAMERA_SIZE = 20
|
||||
const FRAMES_TO_ANIMATE_IN = 30
|
||||
const ORTHOGRAPHIC_MAGIC_FOV = 4
|
||||
|
||||
// Load the setting from the OS.
|
||||
refreshNaturalScrollDirection().catch(reportRejection)
|
||||
|
||||
const tempQuaternion = new Quaternion() // just used for maths
|
||||
|
||||
type interactionType = 'pan' | 'rotate' | 'zoom'
|
||||
@ -522,15 +530,25 @@ export class CameraControls {
|
||||
}
|
||||
}
|
||||
|
||||
zoomDirection = (event: WheelEvent): 1 | -1 => {
|
||||
if (!this.interactionGuards.zoom.scrollAllowInvertY) return 1
|
||||
// Safari provides the updated user setting on every event, so it's more
|
||||
// accurate than our cached value.
|
||||
if ('webkitDirectionInvertedFromDevice' in event) {
|
||||
return event.webkitDirectionInvertedFromDevice ? -1 : 1
|
||||
}
|
||||
return cachedNaturalScrollDirection ? -1 : 1
|
||||
}
|
||||
|
||||
onMouseWheel = (event: WheelEvent) => {
|
||||
const interaction = this.getInteractionType(event)
|
||||
if (interaction === 'none') return
|
||||
event.preventDefault()
|
||||
|
||||
const zoomDirection = this.interactionGuards.zoom.scrollReverseY ? -1 : 1
|
||||
if (this.syncDirection === 'engineToClient') {
|
||||
if (interaction === 'zoom') {
|
||||
this.zoomDataFromLastFrame = event.deltaY * zoomDirection
|
||||
const zoomDir = this.zoomDirection(event)
|
||||
this.zoomDataFromLastFrame = event.deltaY * zoomDir
|
||||
} else {
|
||||
this.moveDataFromLastFrame = [
|
||||
'wheel',
|
||||
@ -551,8 +569,9 @@ export class CameraControls {
|
||||
|
||||
this.handleStart()
|
||||
if (interaction === 'zoom') {
|
||||
const zoomDir = this.zoomDirection(event)
|
||||
this.pendingZoom =
|
||||
1 + (event.deltaY / window.devicePixelRatio) * 0.001 * zoomDirection
|
||||
1 + (event.deltaY / window.devicePixelRatio) * 0.001 * zoomDir
|
||||
} else {
|
||||
this.isDragging = true
|
||||
this.mouseDownPosition.set(event.clientX, event.clientY)
|
||||
|
@ -66,7 +66,7 @@ interface MouseGuardZoomHandler {
|
||||
description: string
|
||||
dragCallback: (e: MouseEvent) => boolean
|
||||
scrollCallback: (e: WheelEvent) => boolean
|
||||
scrollReverseY?: boolean
|
||||
scrollAllowInvertY?: boolean
|
||||
lenientDragStartButton?: number
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
|
||||
!e.ctrlKey &&
|
||||
!e.altKey &&
|
||||
!e.metaKey,
|
||||
scrollReverseY: true,
|
||||
scrollAllowInvertY: true,
|
||||
},
|
||||
rotate: {
|
||||
description: `${ALT} + Scroll`,
|
||||
|
@ -565,3 +565,7 @@ export const getUser = async (
|
||||
}
|
||||
return Promise.reject(new Error('unreachable'))
|
||||
}
|
||||
|
||||
export async function readNaturalScrollDirection() {
|
||||
return window.electron.readNaturalScrollDirection()
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { v4 } from 'uuid'
|
||||
import { isDesktop } from './isDesktop'
|
||||
import { AnyMachineSnapshot } from 'xstate'
|
||||
import { AsyncFn } from './types'
|
||||
import { readNaturalScrollDirection } from './desktop'
|
||||
|
||||
export const uuidv4 = v4
|
||||
|
||||
@ -262,6 +263,19 @@ export function isReducedMotion(): boolean {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* True if Apple Trackpad scroll should move the content. I.e. if this is true,
|
||||
* and the user scrolls down, the viewport moves up relative to the content.
|
||||
*/
|
||||
export let cachedNaturalScrollDirection = platform() === 'macos'
|
||||
|
||||
export async function refreshNaturalScrollDirection() {
|
||||
if (!isDesktop()) return cachedNaturalScrollDirection
|
||||
const isNatural = await readNaturalScrollDirection()
|
||||
cachedNaturalScrollDirection = isNatural
|
||||
return isNatural
|
||||
}
|
||||
|
||||
export function XOR(bool1: boolean, bool2: boolean): boolean {
|
||||
return (bool1 || bool2) && !(bool1 && bool2)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import os from 'node:os'
|
||||
import fsSync from 'node:fs'
|
||||
import packageJson from '../package.json'
|
||||
import { MachinesListing } from 'lib/machineManager'
|
||||
import { exec } from 'child_process'
|
||||
import chokidar from 'chokidar'
|
||||
|
||||
const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args)
|
||||
@ -81,6 +82,25 @@ const listMachines = async (): Promise<MachinesListing> => {
|
||||
const getMachineApiIp = async (): Promise<String | null> =>
|
||||
ipcRenderer.invoke('find_machine_api')
|
||||
|
||||
async function readNaturalScrollDirection(): Promise<boolean> {
|
||||
if (os.platform() !== 'darwin') {
|
||||
// TODO: Detect this on other OS's.
|
||||
return false
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(
|
||||
'defaults read -globalDomain com.apple.swipescrolldirection',
|
||||
(err, stdout) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(stdout.trim() === '1')
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('electron', {
|
||||
startDeviceFlow,
|
||||
loginWithDeviceFlow,
|
||||
@ -144,6 +164,7 @@ contextBridge.exposeInMainWorld('electron', {
|
||||
kittycad,
|
||||
listMachines,
|
||||
getMachineApiIp,
|
||||
readNaturalScrollDirection,
|
||||
onUpdateDownloaded,
|
||||
appRestart,
|
||||
})
|
||||
|
Reference in New Issue
Block a user