From 9283fbeb43a92830f47b32aeac248343fc0084c0 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Wed, 19 Oct 2022 19:25:41 +0200 Subject: [PATCH] add zoom modifier key setting #1287 also refactor/simplify user settings hook --- public/configStore.js | 1 + src/App.jsx | 12 +++++++----- src/Settings.jsx | 15 ++++++++++++++- src/hooks/useTimelineScroll.js | 14 +++++++++++--- src/hooks/useUserSettingsRoot.js | 4 ++++ 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/public/configStore.js b/public/configStore.js index e944844..8ce0ba7 100644 --- a/public/configStore.js +++ b/public/configStore.js @@ -112,6 +112,7 @@ const defaults = { customFfPath: undefined, storeProjectInWorkingDir: true, enableOverwriteOutput: true, + mouseWheelZoomModifierKey: 'ctrl', }; // For portable app: https://github.com/mifi/lossless-cut/issues/645 diff --git a/src/App.jsx b/src/App.jsx index bceadca..3c1b6c0 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -198,9 +198,11 @@ const App = memo(() => { const durationSafe = isDurationValid(duration) ? duration : 1; const zoomedDuration = isDurationValid(duration) ? duration / zoom : undefined; + const allUserSettings = useUserSettingsRoot(); + const { - captureFormat, setCaptureFormat, customOutDir, setCustomOutDir, keyframeCut, setKeyframeCut, preserveMovData, setPreserveMovData, movFastStart, setMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoMerge, setAutoMerge, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, setAutoExportExtraStreams, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, setAutoSaveProjectFile, wheelSensitivity, setWheelSensitivity, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, autoDeleteMergedSegments, setAutoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, simpleMode, setSimpleMode, outSegTemplate, setOutSegTemplate, keyboardSeekAccFactor, setKeyboardSeekAccFactor, keyboardNormalSeekSpeed, setKeyboardNormalSeekSpeed, enableTransferTimestamps, setEnableTransferTimestamps, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, enableAutoHtml5ify, setEnableAutoHtml5ify, segmentsToChaptersOnly, setSegmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, setEnableSmartCut, customFfPath, setCustomFfPath, storeProjectInWorkingDir, setStoreProjectInWorkingDir, enableOverwriteOutput, setEnableOverwriteOutput, - } = useUserSettingsRoot(); + captureFormat, setCaptureFormat, customOutDir, setCustomOutDir, keyframeCut, setKeyframeCut, preserveMovData, setPreserveMovData, movFastStart, setMovFastStart, avoidNegativeTs, autoMerge, timecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, askBeforeClose, enableAskForImportChapters, enableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, wheelSensitivity, invertTimelineScroll, language, ffmpegExperimental, hideNotifications, autoLoadTimecode, autoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, setSimpleMode, outSegTemplate, setOutSegTemplate, keyboardSeekAccFactor, keyboardNormalSeekSpeed, enableTransferTimestamps, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, enableAutoHtml5ify, segmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, customFfPath, storeProjectInWorkingDir, enableOverwriteOutput, mouseWheelZoomModifierKey, + } = allUserSettings; useEffect(() => { ffmpegSetCustomFfPath(customFfPath); @@ -341,7 +343,7 @@ const App = memo(() => { }); }, [comfortZoom]); - const onTimelineWheel = useTimelineScroll({ wheelSensitivity, invertTimelineScroll, zoomRel, seekRel }); + const onTimelineWheel = useTimelineScroll({ wheelSensitivity, mouseWheelZoomModifierKey, invertTimelineScroll, zoomRel, seekRel }); const getSegApparentEnd = useCallback((seg) => { const time = seg.end; @@ -742,8 +744,8 @@ const App = memo(() => { }, [autoDeleteMergedSegments, autoMerge, segmentsToChaptersOnly]); const userSettingsContext = useMemo(() => ({ - captureFormat, setCaptureFormat, toggleCaptureFormat, customOutDir, setCustomOutDir, changeOutDir, keyframeCut, setKeyframeCut, toggleKeyframeCut, preserveMovData, setPreserveMovData, togglePreserveMovData, movFastStart, setMovFastStart, toggleMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoMerge, setAutoMerge, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, setAutoExportExtraStreams, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, setAutoSaveProjectFile, wheelSensitivity, setWheelSensitivity, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, autoDeleteMergedSegments, setAutoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, toggleSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, togglePreserveMetadataOnMerge, simpleMode, setSimpleMode, toggleSimpleMode, outSegTemplate, setOutSegTemplate, keyboardSeekAccFactor, setKeyboardSeekAccFactor, keyboardNormalSeekSpeed, setKeyboardNormalSeekSpeed, enableTransferTimestamps, setEnableTransferTimestamps, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, toggleSafeOutputFileName, enableAutoHtml5ify, setEnableAutoHtml5ify, segmentsToChaptersOnly, setSegmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, setEnableSmartCut, customFfPath, setCustomFfPath, storeProjectInWorkingDir, setStoreProjectInWorkingDir, effectiveExportMode, enableOverwriteOutput, setEnableOverwriteOutput, - }), [askBeforeClose, autoDeleteMergedSegments, autoExportExtraStreams, autoLoadTimecode, autoMerge, autoSaveProjectFile, avoidNegativeTs, captureFormat, changeOutDir, customFfPath, customOutDir, effectiveExportMode, enableAskForFileOpenAction, enableAskForImportChapters, enableAutoHtml5ify, enableOverwriteOutput, enableSmartCut, enableTransferTimestamps, exportConfirmEnabled, ffmpegExperimental, hideNotifications, invertCutSegments, invertTimelineScroll, keyBindings, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyframeCut, language, movFastStart, outFormatLocked, outSegTemplate, playbackVolume, preserveMetadataOnMerge, preserveMovData, resetKeyBindings, safeOutputFileName, segmentsToChapters, segmentsToChaptersOnly, setAskBeforeClose, setAutoDeleteMergedSegments, setAutoExportExtraStreams, setAutoLoadTimecode, setAutoMerge, setAutoSaveProjectFile, setAvoidNegativeTs, setCaptureFormat, setCustomFfPath, setCustomOutDir, setEnableAskForFileOpenAction, setEnableAskForImportChapters, setEnableAutoHtml5ify, setEnableOverwriteOutput, setEnableSmartCut, setEnableTransferTimestamps, setExportConfirmEnabled, setFfmpegExperimental, setHideNotifications, setInvertCutSegments, setInvertTimelineScroll, setKeyBindings, setKeyboardNormalSeekSpeed, setKeyboardSeekAccFactor, setKeyframeCut, setLanguage, setMovFastStart, setOutFormatLocked, setOutSegTemplate, setPlaybackVolume, setPreserveMetadataOnMerge, setPreserveMovData, setSafeOutputFileName, setSegmentsToChapters, setSegmentsToChaptersOnly, setSimpleMode, setStoreProjectInWorkingDir, setTimecodeFormat, setWheelSensitivity, simpleMode, storeProjectInWorkingDir, timecodeFormat, toggleCaptureFormat, toggleExportConfirmEnabled, toggleKeyframeCut, toggleMovFastStart, togglePreserveMetadataOnMerge, togglePreserveMovData, toggleSafeOutputFileName, toggleSegmentsToChapters, toggleSimpleMode, wheelSensitivity]); + ...allUserSettings, toggleCaptureFormat, changeOutDir, toggleKeyframeCut, togglePreserveMovData, toggleMovFastStart, toggleExportConfirmEnabled, toggleSegmentsToChapters, togglePreserveMetadataOnMerge, toggleSimpleMode, toggleSafeOutputFileName, effectiveExportMode, + }), [allUserSettings, changeOutDir, effectiveExportMode, toggleCaptureFormat, toggleExportConfirmEnabled, toggleKeyframeCut, toggleMovFastStart, togglePreserveMetadataOnMerge, togglePreserveMovData, toggleSafeOutputFileName, toggleSegmentsToChapters, toggleSimpleMode]); const isCopyingStreamId = useCallback((path, streamId) => ( !!(copyStreamIdsByFile[path] || {})[streamId] diff --git a/src/Settings.jsx b/src/Settings.jsx index b2b5c44..b5a8dd0 100644 --- a/src/Settings.jsx +++ b/src/Settings.jsx @@ -9,6 +9,8 @@ import useUserSettings from './hooks/useUserSettings'; import { askForFfPath } from './dialogs'; import { isMasBuild } from './util'; +import { keyMap } from './hooks/useTimelineScroll'; + // https://www.electronjs.org/docs/api/locales // See i18n.js @@ -47,7 +49,7 @@ const Settings = memo(({ }) => { const { t } = useTranslation(); - const { customOutDir, changeOutDir, keyframeCut, toggleKeyframeCut, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, autoSaveProjectFile, setAutoSaveProjectFile, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, enableTransferTimestamps, setEnableTransferTimestamps, enableAutoHtml5ify, setEnableAutoHtml5ify, customFfPath, setCustomFfPath, storeProjectInWorkingDir, setStoreProjectInWorkingDir, enableOverwriteOutput, setEnableOverwriteOutput } = useUserSettings(); + const { customOutDir, changeOutDir, keyframeCut, toggleKeyframeCut, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, autoSaveProjectFile, setAutoSaveProjectFile, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, enableTransferTimestamps, setEnableTransferTimestamps, enableAutoHtml5ify, setEnableAutoHtml5ify, customFfPath, setCustomFfPath, storeProjectInWorkingDir, setStoreProjectInWorkingDir, enableOverwriteOutput, setEnableOverwriteOutput, mouseWheelZoomModifierKey, setMouseWheelZoomModifierKey } = useUserSettings(); const onLangChange = useCallback((e) => { const { value } = e.target; @@ -217,6 +219,17 @@ const Settings = memo(({ + + {t('Mouse wheel zoom modifier key')} + + + + + {t('Timeline trackpad/wheel sensitivity')} diff --git a/src/hooks/useTimelineScroll.js b/src/hooks/useTimelineScroll.js index 32ce093..443d55a 100644 --- a/src/hooks/useTimelineScroll.js +++ b/src/hooks/useTimelineScroll.js @@ -2,19 +2,27 @@ import { useCallback } from 'react'; import normalizeWheel from './normalizeWheel'; -function useTimelineScroll({ wheelSensitivity, invertTimelineScroll, zoomRel, seekRel }) { +export const keyMap = { + ctrl: 'ctrlKey', + shift: 'shiftKey', + alt: 'altKey', + meta: 'metaKey', +}; + +function useTimelineScroll({ wheelSensitivity, mouseWheelZoomModifierKey, invertTimelineScroll, zoomRel, seekRel }) { const onWheel = useCallback((e) => { const { pixelX, pixelY } = normalizeWheel(e); // console.log({ spinX, spinY, pixelX, pixelY }); const direction = invertTimelineScroll ? 1 : -1; - if (e.ctrlKey) { + const modifierKey = keyMap[mouseWheelZoomModifierKey]; + if (e[modifierKey]) { zoomRel(direction * (pixelY) * wheelSensitivity * 0.4); } else { seekRel(direction * (pixelX + pixelY) * wheelSensitivity * 0.2); } - }, [seekRel, zoomRel, wheelSensitivity, invertTimelineScroll]); + }, [invertTimelineScroll, mouseWheelZoomModifierKey, zoomRel, wheelSensitivity, seekRel]); return onWheel; } diff --git a/src/hooks/useUserSettingsRoot.js b/src/hooks/useUserSettingsRoot.js index 2396590..1edb7ac 100644 --- a/src/hooks/useUserSettingsRoot.js +++ b/src/hooks/useUserSettingsRoot.js @@ -115,6 +115,8 @@ export default () => { useEffect(() => safeSetConfig('storeProjectInWorkingDir', storeProjectInWorkingDir), [storeProjectInWorkingDir]); const [enableOverwriteOutput, setEnableOverwriteOutput] = useState(safeGetConfigInitial('enableOverwriteOutput')); useEffect(() => safeSetConfig('enableOverwriteOutput', enableOverwriteOutput), [enableOverwriteOutput]); + const [mouseWheelZoomModifierKey, setMouseWheelZoomModifierKey] = useState(safeGetConfigInitial('mouseWheelZoomModifierKey')); + useEffect(() => safeSetConfig('mouseWheelZoomModifierKey', mouseWheelZoomModifierKey), [mouseWheelZoomModifierKey]); const resetKeyBindings = useCallback(() => { configStore.reset('keyBindings'); @@ -209,5 +211,7 @@ export default () => { setStoreProjectInWorkingDir, enableOverwriteOutput, setEnableOverwriteOutput, + mouseWheelZoomModifierKey, + setMouseWheelZoomModifierKey, }; };