Skip to content

Commit

Permalink
Add respecting OS setting for natural scroll direction
Browse files Browse the repository at this point in the history
  • Loading branch information
jtran committed Sep 21, 2024
1 parent b859081 commit 6b74d89
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 6 deletions.
1 change: 1 addition & 0 deletions interface.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface IElectronAPI {
kittycad: (access: string, args: any) => any
listMachines: () => Promise<MachinesListing>
getMachineApiIp: () => Promise<string | null>
readNaturalScrollDirection: () => Promise<boolean>
}

declare global {
Expand Down
27 changes: 23 additions & 4 deletions src/clientSideScene/CameraControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -33,6 +38,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'
Expand Down Expand Up @@ -513,15 +521,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',
Expand All @@ -542,8 +560,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)
Expand Down
4 changes: 2 additions & 2 deletions src/lib/cameraControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ interface MouseGuardZoomHandler {
description: string
dragCallback: (e: MouseEvent) => boolean
scrollCallback: (e: WheelEvent) => boolean
scrollReverseY?: boolean
scrollAllowInvertY?: boolean
lenientDragStartButton?: number
}

Expand Down Expand Up @@ -157,7 +157,7 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
!e.ctrlKey &&
!e.altKey &&
!e.metaKey,
scrollReverseY: true,
scrollAllowInvertY: true,
},
rotate: {
description: `${ALT} + Scroll`,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/desktop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,3 +565,7 @@ export const getUser = async (
}
return Promise.reject(new Error('unreachable'))
}

export async function readNaturalScrollDirection() {
return window.electron.readNaturalScrollDirection()
}
14 changes: 14 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -229,6 +230,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)
}
Expand Down
21 changes: 21 additions & 0 deletions src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args)
const save = (args: any) => ipcRenderer.invoke('dialog.showSaveDialog', args)
Expand Down Expand Up @@ -60,6 +61,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', {
login,
// Passing fs directly is not recommended since it gives a lot of power
Expand Down Expand Up @@ -120,4 +140,5 @@ contextBridge.exposeInMainWorld('electron', {
kittycad,
listMachines,
getMachineApiIp,
readNaturalScrollDirection,
})

0 comments on commit 6b74d89

Please sign in to comment.