diff --git a/src/App.tsx b/src/App.tsx index 902996d7e..490acb072 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -288,7 +288,7 @@ export function App() { const newCmdId = uuidv4() - if (buttonDownInStream) { + if (buttonDownInStream !== undefined) { const interactionGuards = cameraMouseDragGuards[cameraControls] let interaction: CameraDragInteractionType_type @@ -303,6 +303,7 @@ export function App() { } else { return } + debounceSocketSend({ type: 'modeling_cmd_req', cmd: { diff --git a/src/components/Stream.tsx b/src/components/Stream.tsx index b5b1af076..c59ed1960 100644 --- a/src/components/Stream.tsx +++ b/src/components/Stream.tsx @@ -66,11 +66,20 @@ export const Stream = ({ className = '' }) => { const interactionGuards = cameraMouseDragGuards[cameraControls] let interaction: CameraDragInteractionType_type - if (interactionGuards.pan.callback(e)) { + if ( + interactionGuards.pan.callback(e) || + interactionGuards.pan.lenientDragStartButton === e.button + ) { interaction = 'pan' - } else if (interactionGuards.rotate.callback(e)) { + } else if ( + interactionGuards.rotate.callback(e) || + interactionGuards.rotate.lenientDragStartButton === e.button + ) { interaction = 'rotate' - } else if (interactionGuards.zoom.dragCallback(e)) { + } else if ( + interactionGuards.zoom.dragCallback(e) || + interactionGuards.zoom.lenientDragStartButton === e.button + ) { interaction = 'zoom' } else { return @@ -93,7 +102,6 @@ export const Stream = ({ className = '' }) => { const handleScroll: WheelEventHandler = (e) => { if (!cameraMouseDragGuards[cameraControls].zoom.scrollCallback(e)) return - e.preventDefault() engineCommandManager?.sendSceneCommand({ type: 'modeling_cmd_req', cmd: { @@ -130,7 +138,7 @@ export const Stream = ({ className = '' }) => { cmd_id: newCmdId, }) - setButtonDownInStream(0) + setButtonDownInStream(undefined) if (!didDragInStream) { engineCommandManager?.sendSceneCommand({ type: 'modeling_cmd_req', diff --git a/src/lib/cameraControls.ts b/src/lib/cameraControls.ts index ca34fa460..ce33ac244 100644 --- a/src/lib/cameraControls.ts +++ b/src/lib/cameraControls.ts @@ -1,17 +1,19 @@ const noModifiersPressed = (e: React.MouseEvent) => !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey -export type CADProgram = +export type CameraSystem = | 'KittyCAD' | 'OnShape' + | 'Trackpad Friendly' | 'Solidworks' | 'NX' | 'Creo' | 'AutoCAD' -export const cadPrograms: CADProgram[] = [ +export const cameraSystems: CameraSystem[] = [ 'KittyCAD', 'OnShape', + 'Trackpad Friendly', 'Solidworks', 'NX', 'Creo', @@ -21,12 +23,14 @@ export const cadPrograms: CADProgram[] = [ interface MouseGuardHandler { description: string callback: (e: React.MouseEvent) => boolean + lenientDragStartButton?: number } interface MouseGuardZoomHandler { description: string dragCallback: (e: React.MouseEvent) => boolean scrollCallback: (e: React.MouseEvent) => boolean + lenientDragStartButton?: number } interface MouseGuard { @@ -35,12 +39,12 @@ interface MouseGuard { rotate: MouseGuardHandler } -export const cameraMouseDragGuards: Record = { +export const cameraMouseDragGuards: Record = { KittyCAD: { pan: { description: 'Right click + Shift + drag or middle click + drag', callback: (e) => - (e.button === 3 && noModifiersPressed(e)) || + (e.button === 1 && noModifiersPressed(e)) || (e.button === 2 && e.shiftKey), }, zoom: { @@ -58,7 +62,7 @@ export const cameraMouseDragGuards: Record = { description: 'Right click + Ctrl + drag or middle click + drag', callback: (e) => (e.button === 2 && e.ctrlKey) || - (e.button === 3 && noModifiersPressed(e)), + (e.button === 1 && noModifiersPressed(e)), }, zoom: { description: 'Scroll wheel', @@ -70,55 +74,74 @@ export const cameraMouseDragGuards: Record = { callback: (e) => e.button === 2 && noModifiersPressed(e), }, }, + 'Trackpad Friendly': { + pan: { + description: 'Left click + Alt + Shift + drag or middle click + drag', + callback: (e) => + (e.button === 0 && e.altKey && e.shiftKey && !e.metaKey) || + (e.button === 1 && noModifiersPressed(e)), + }, + zoom: { + description: 'Scroll wheel or Left click + Alt + OS + drag', + dragCallback: (e) => e.button === 0 && e.altKey && e.metaKey, + scrollCallback: () => true, + }, + rotate: { + description: 'Left click + Alt + drag', + callback: (e) => e.button === 0 && e.altKey && !e.shiftKey && !e.metaKey, + lenientDragStartButton: 0, + }, + }, Solidworks: { pan: { description: 'Right click + Ctrl + drag', callback: (e) => e.button === 2 && e.ctrlKey, + lenientDragStartButton: 2, }, zoom: { description: 'Scroll wheel or Middle click + Shift + drag', - dragCallback: (e) => e.button === 3 && e.shiftKey, + dragCallback: (e) => e.button === 1 && e.shiftKey, scrollCallback: () => true, }, rotate: { description: 'Middle click + drag', - callback: (e) => e.button === 3 && noModifiersPressed(e), + callback: (e) => e.button === 1 && noModifiersPressed(e), }, }, NX: { pan: { description: 'Middle click + Shift + drag', - callback: (e) => e.button === 3 && e.shiftKey, + callback: (e) => e.button === 1 && e.shiftKey, }, zoom: { description: 'Scroll wheel or Middle click + Ctrl + drag', - dragCallback: (e) => e.button === 3 && e.ctrlKey, + dragCallback: (e) => e.button === 1 && e.ctrlKey, scrollCallback: () => true, }, rotate: { description: 'Middle click + drag', - callback: (e) => e.button === 3 && noModifiersPressed(e), + callback: (e) => e.button === 1 && noModifiersPressed(e), }, }, Creo: { pan: { description: 'Middle click + Shift + drag', - callback: (e) => e.button === 3 && e.shiftKey, + callback: (e) => e.button === 1 && e.shiftKey, }, zoom: { description: 'Scroll wheel or Middle click + Ctrl + drag', - dragCallback: (e) => e.button === 3 && e.ctrlKey, + dragCallback: (e) => e.button === 1 && e.ctrlKey, scrollCallback: () => true, }, rotate: { description: 'Middle click + drag', - callback: (e) => e.button === 3 && noModifiersPressed(e), + callback: (e) => e.button === 1 && noModifiersPressed(e), }, }, AutoCAD: { pan: { description: 'Middle click + drag', - callback: (e) => e.button === 3 && noModifiersPressed(e), + callback: (e) => e.button === 1 && noModifiersPressed(e), }, zoom: { description: 'Scroll wheel', @@ -127,7 +150,7 @@ export const cameraMouseDragGuards: Record = { }, rotate: { description: 'Middle click + Shift + drag', - callback: (e) => e.button === 3 && e.shiftKey, + callback: (e) => e.button === 1 && e.shiftKey, }, }, } diff --git a/src/machines/settingsMachine.ts b/src/machines/settingsMachine.ts index 2f6c26c21..c0896963e 100644 --- a/src/machines/settingsMachine.ts +++ b/src/machines/settingsMachine.ts @@ -1,7 +1,7 @@ import { assign, createMachine } from 'xstate' import { CommandBarMeta } from '../lib/commands' import { Themes, getSystemTheme, setThemeClass } from '../lib/theme' -import { CADProgram, cadPrograms } from 'lib/cameraControls' +import { CameraSystem, cameraSystems } from 'lib/cameraControls' export const DEFAULT_PROJECT_NAME = 'project-$nnn' @@ -42,7 +42,7 @@ export const settingsCommandBarMeta: CommandBarMeta = { name: 'cameraControls', type: 'select', defaultValue: 'cameraControls', - options: Object.values(cadPrograms).map((v) => ({ name: v })), + options: Object.values(cameraSystems).map((v) => ({ name: v })), }, ], }, @@ -109,7 +109,7 @@ export const settingsMachine = createMachine( predictableActionArguments: true, context: { baseUnit: 'in' as BaseUnit, - cameraControls: 'KittyCAD' as CADProgram, + cameraControls: 'KittyCAD' as CameraSystem, defaultDirectory: '', defaultProjectName: DEFAULT_PROJECT_NAME, onboardingStatus: '', @@ -232,7 +232,10 @@ export const settingsMachine = createMachine( schema: { events: {} as | { type: 'Set Base Unit'; data: { baseUnit: BaseUnit } } - | { type: 'Set Camera Controls'; data: { cameraControls: CADProgram } } + | { + type: 'Set Camera Controls' + data: { cameraControls: CameraSystem } + } | { type: 'Set Default Directory'; data: { defaultDirectory: string } } | { type: 'Set Default Project Name' diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx index cabb32be4..7259806e2 100644 --- a/src/routes/Settings.tsx +++ b/src/routes/Settings.tsx @@ -18,8 +18,8 @@ import { IndexLoaderData, paths } from '../Router' import { Themes } from '../lib/theme' import { useGlobalStateContext } from 'hooks/useGlobalStateContext' import { - CADProgram, - cadPrograms, + CameraSystem, + cameraSystems, cameraMouseDragGuards, } from 'lib/cameraControls' import { UnitSystem } from 'machines/settingsMachine' @@ -103,11 +103,11 @@ export const Settings = () => { onChange={(e) => { send({ type: 'Set Camera Controls', - data: { cameraControls: e.target.value as CADProgram }, + data: { cameraControls: e.target.value as CameraSystem }, }) }} > - {cadPrograms.map((program) => ( + {cameraSystems.map((program) => ( diff --git a/src/useStore.ts b/src/useStore.ts index be4ad1858..24d250398 100644 --- a/src/useStore.ts +++ b/src/useStore.ts @@ -160,8 +160,8 @@ export interface StoreState { setIsStreamReady: (isStreamReady: boolean) => void isLSPServerReady: boolean setIsLSPServerReady: (isLSPServerReady: boolean) => void - buttonDownInStream: number - setButtonDownInStream: (buttonDownInStream: number) => void + buttonDownInStream: number | undefined + setButtonDownInStream: (buttonDownInStream: number | undefined) => void didDragInStream: boolean setDidDragInStream: (didDragInStream: boolean) => void fileId: string @@ -356,7 +356,7 @@ export const useStore = create()( setIsStreamReady: (isStreamReady) => set({ isStreamReady }), isLSPServerReady: false, setIsLSPServerReady: (isLSPServerReady) => set({ isLSPServerReady }), - buttonDownInStream: 0, + buttonDownInStream: undefined, setButtonDownInStream: (buttonDownInStream) => { set({ buttonDownInStream }) },