kopia lustrzana https://github.com/mifi/lossless-cut
add shortcut shift+j for toggling keyboard shortcut dialog #654
also allow resetting key config #726pull/982/head
rodzic
1a59be3e75
commit
c2cb1f5501
|
@ -62,6 +62,7 @@ const defaultKeyBindings = [
|
||||||
|
|
||||||
{ keys: 'e', action: 'export' },
|
{ keys: 'e', action: 'export' },
|
||||||
{ keys: 'h', action: 'toggleHelp' },
|
{ keys: 'h', action: 'toggleHelp' },
|
||||||
|
{ keys: 'shift+/', action: 'toggleKeyboardShortcuts' },
|
||||||
{ keys: 'escape', action: 'closeActiveScreen' },
|
{ keys: 'escape', action: 'closeActiveScreen' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
13
src/App.jsx
13
src/App.jsx
|
@ -197,7 +197,7 @@ const App = memo(() => {
|
||||||
const isCustomFormatSelected = fileFormat !== detectedFileFormat;
|
const isCustomFormatSelected = fileFormat !== detectedFileFormat;
|
||||||
|
|
||||||
const {
|
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,
|
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,
|
||||||
} = useUserPreferences();
|
} = useUserPreferences();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -1626,6 +1626,8 @@ const App = memo(() => {
|
||||||
setStartTimeOffset(newStartTimeOffset);
|
setStartTimeOffset(newStartTimeOffset);
|
||||||
}, [startTimeOffset]);
|
}, [startTimeOffset]);
|
||||||
|
|
||||||
|
const toggleKeyboardShortcuts = useCallback(() => setKeyboardShortcutsVisible((v) => !v), []);
|
||||||
|
|
||||||
const onKeyPress = useCallback(({ action, keyup }) => {
|
const onKeyPress = useCallback(({ action, keyup }) => {
|
||||||
function seekReset() {
|
function seekReset() {
|
||||||
seekAccelerationRef.current = 1;
|
seekAccelerationRef.current = 1;
|
||||||
|
@ -1723,6 +1725,11 @@ const App = memo(() => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action === 'toggleKeyboardShortcuts') {
|
||||||
|
toggleKeyboardShortcuts();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (concatDialogVisible || keyboardShortcutsVisible) {
|
if (concatDialogVisible || keyboardShortcutsVisible) {
|
||||||
return true; // don't allow any further hotkeys
|
return true; // don't allow any further hotkeys
|
||||||
}
|
}
|
||||||
|
@ -1740,7 +1747,7 @@ const App = memo(() => {
|
||||||
if (match) return bubble;
|
if (match) return bubble;
|
||||||
|
|
||||||
return true; // bubble the event
|
return true; // bubble the event
|
||||||
}, [addCutSegment, askSetStartTimeOffset, batchFileJump, captureSnapshot, changePlaybackRate, cleanupFilesDialog, clearSegments, closeBatch, closeExportConfirm, concatCurrentBatch, concatDialogVisible, convertFormatBatch, createFixedDurationSegments, createNumSegments, currentSegIndexSafe, cutSegmentsHistory, exportConfirmVisible, extractAllStreams, goToTimecode, increaseRotation, invertAllCutSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyboardShortcutsVisible, onExportConfirm, onExportPress, onLabelSegmentPress, removeCutSegment, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, setCutEnd, setCutStart, shortStep, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleHelp, toggleKeyframeCut, togglePlay, toggleSegmentsList, toggleStreamsSelector, toggleStripAudio, userHtml5ifyCurrentFile, zoomRel]);
|
}, [addCutSegment, askSetStartTimeOffset, batchFileJump, captureSnapshot, changePlaybackRate, cleanupFilesDialog, clearSegments, closeBatch, closeExportConfirm, concatCurrentBatch, concatDialogVisible, convertFormatBatch, createFixedDurationSegments, createNumSegments, currentSegIndexSafe, cutSegmentsHistory, exportConfirmVisible, extractAllStreams, goToTimecode, increaseRotation, invertAllCutSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyboardShortcutsVisible, onExportConfirm, onExportPress, onLabelSegmentPress, removeCutSegment, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, setCutEnd, setCutStart, shortStep, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleHelp, toggleKeyboardShortcuts, toggleKeyframeCut, togglePlay, toggleSegmentsList, toggleStreamsSelector, toggleStripAudio, userHtml5ifyCurrentFile, zoomRel]);
|
||||||
|
|
||||||
useKeyboard({ keyBindings, onKeyPress });
|
useKeyboard({ keyBindings, onKeyPress });
|
||||||
|
|
||||||
|
@ -2481,7 +2488,7 @@ const App = memo(() => {
|
||||||
|
|
||||||
<ConcatDialog isShown={batchFiles.length > 0 && concatDialogVisible} onHide={() => setConcatDialogVisible(false)} initialPaths={batchFilePaths} onConcat={mergeFiles} segmentsToChapters={segmentsToChapters} setSegmentsToChapters={setSegmentsToChapters} setAlwaysConcatMultipleFiles={setAlwaysConcatMultipleFiles} alwaysConcatMultipleFiles={alwaysConcatMultipleFiles} preserveMetadataOnMerge={preserveMetadataOnMerge} setPreserveMetadataOnMerge={setPreserveMetadataOnMerge} preserveMovData={preserveMovData} setPreserveMovData={setPreserveMovData} />
|
<ConcatDialog isShown={batchFiles.length > 0 && concatDialogVisible} onHide={() => setConcatDialogVisible(false)} initialPaths={batchFilePaths} onConcat={mergeFiles} segmentsToChapters={segmentsToChapters} setSegmentsToChapters={setSegmentsToChapters} setAlwaysConcatMultipleFiles={setAlwaysConcatMultipleFiles} alwaysConcatMultipleFiles={alwaysConcatMultipleFiles} preserveMetadataOnMerge={preserveMetadataOnMerge} setPreserveMetadataOnMerge={setPreserveMetadataOnMerge} preserveMovData={preserveMovData} setPreserveMovData={setPreserveMovData} />
|
||||||
|
|
||||||
<KeyboardShortcuts isShown={keyboardShortcutsVisible} onHide={() => setKeyboardShortcutsVisible(false)} keyBindings={keyBindings} setKeyBindings={setKeyBindings} currentCutSeg={currentCutSeg} />
|
<KeyboardShortcuts isShown={keyboardShortcutsVisible} onHide={() => setKeyboardShortcutsVisible(false)} keyBindings={keyBindings} setKeyBindings={setKeyBindings} currentCutSeg={currentCutSeg} resetKeyBindings={resetKeyBindings} />
|
||||||
</div>
|
</div>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -100,7 +100,7 @@ const CreateBinding = memo(({
|
||||||
const rowStyle = { display: 'flex', alignItems: 'center', margin: '6px 0' };
|
const rowStyle = { display: 'flex', alignItems: 'center', margin: '6px 0' };
|
||||||
|
|
||||||
const KeyboardShortcuts = memo(({
|
const KeyboardShortcuts = memo(({
|
||||||
keyBindings, setKeyBindings, currentCutSeg,
|
keyBindings, setKeyBindings, resetKeyBindings, currentCutSeg,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
@ -136,7 +136,11 @@ const KeyboardShortcuts = memo(({
|
||||||
toggleHelp: {
|
toggleHelp: {
|
||||||
name: t('Show/hide help screen'),
|
name: t('Show/hide help screen'),
|
||||||
},
|
},
|
||||||
|
toggleKeyboardShortcuts: {
|
||||||
|
name: t('Keyboard & mouse shortcuts'),
|
||||||
|
},
|
||||||
|
|
||||||
|
// playbackCategory
|
||||||
togglePlayResetSpeed: {
|
togglePlayResetSpeed: {
|
||||||
name: t('Play/pause'),
|
name: t('Play/pause'),
|
||||||
category: playbackCategory,
|
category: playbackCategory,
|
||||||
|
@ -162,6 +166,7 @@ const KeyboardShortcuts = memo(({
|
||||||
category: playbackCategory,
|
category: playbackCategory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// seekingCategory
|
||||||
seekPreviousFrame: {
|
seekPreviousFrame: {
|
||||||
name: t('Step backward 1 frame'),
|
name: t('Step backward 1 frame'),
|
||||||
category: seekingCategory,
|
category: seekingCategory,
|
||||||
|
@ -209,6 +214,7 @@ const KeyboardShortcuts = memo(({
|
||||||
category: seekingCategory,
|
category: seekingCategory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// segmentsAndCutpointsCategory
|
||||||
addSegment: {
|
addSegment: {
|
||||||
name: t('Add cut segment'),
|
name: t('Add cut segment'),
|
||||||
category: segmentsAndCutpointsCategory,
|
category: segmentsAndCutpointsCategory,
|
||||||
|
@ -272,6 +278,7 @@ const KeyboardShortcuts = memo(({
|
||||||
category: segmentsAndCutpointsCategory,
|
category: segmentsAndCutpointsCategory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// streamsCategory
|
||||||
toggleStreamsSelector: {
|
toggleStreamsSelector: {
|
||||||
name: t('Edit tracks / metadata tags'),
|
name: t('Edit tracks / metadata tags'),
|
||||||
category: streamsCategory,
|
category: streamsCategory,
|
||||||
|
@ -281,6 +288,7 @@ const KeyboardShortcuts = memo(({
|
||||||
category: streamsCategory,
|
category: streamsCategory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// zoomOperationsCategory
|
||||||
timelineZoomIn: {
|
timelineZoomIn: {
|
||||||
name: t('Zoom in timeline'),
|
name: t('Zoom in timeline'),
|
||||||
category: zoomOperationsCategory,
|
category: zoomOperationsCategory,
|
||||||
|
@ -294,6 +302,7 @@ const KeyboardShortcuts = memo(({
|
||||||
category: zoomOperationsCategory,
|
category: zoomOperationsCategory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// outputCategory
|
||||||
export: {
|
export: {
|
||||||
name: t('Export segment(s)'),
|
name: t('Export segment(s)'),
|
||||||
category: outputCategory,
|
category: outputCategory,
|
||||||
|
@ -315,6 +324,7 @@ const KeyboardShortcuts = memo(({
|
||||||
category: outputCategory,
|
category: outputCategory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// batchFilesCategory
|
||||||
batchPreviousFile: {
|
batchPreviousFile: {
|
||||||
name: t('Previous file'),
|
name: t('Previous file'),
|
||||||
category: batchFilesCategory,
|
category: batchFilesCategory,
|
||||||
|
@ -332,6 +342,7 @@ const KeyboardShortcuts = memo(({
|
||||||
category: batchFilesCategory,
|
category: batchFilesCategory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// otherCategory
|
||||||
toggleKeyframeCutMode: {
|
toggleKeyframeCutMode: {
|
||||||
name: t('Cut mode'),
|
name: t('Cut mode'),
|
||||||
category: otherCategory,
|
category: otherCategory,
|
||||||
|
@ -380,6 +391,14 @@ const KeyboardShortcuts = memo(({
|
||||||
setKeyBindings((existingBindings) => existingBindings.filter((existingBinding) => !(existingBinding.keys === keys && existingBinding.action === action)));
|
setKeyBindings((existingBindings) => existingBindings.filter((existingBinding) => !(existingBinding.keys === keys && existingBinding.action === action)));
|
||||||
}, [setKeyBindings, t]);
|
}, [setKeyBindings, t]);
|
||||||
|
|
||||||
|
|
||||||
|
const onResetClick = useCallback(() => {
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
|
if (!window.confirm(t('Are you sure?'))) return;
|
||||||
|
|
||||||
|
resetKeyBindings();
|
||||||
|
}, [resetKeyBindings, t]);
|
||||||
|
|
||||||
const onAddBindingClick = useCallback((action) => {
|
const onAddBindingClick = useCallback((action) => {
|
||||||
setCreatingBinding(action);
|
setCreatingBinding(action);
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -448,13 +467,15 @@ const KeyboardShortcuts = memo(({
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Button intent="danger" onClick={onResetClick}>{t('Reset')}</Button>
|
||||||
|
|
||||||
<CreateBinding actionsMap={actionsMap} action={creatingBinding} setCreatingBinding={setCreatingBinding} onNewKeyBindingConfirmed={onNewKeyBindingConfirmed} />
|
<CreateBinding actionsMap={actionsMap} action={creatingBinding} setCreatingBinding={setCreatingBinding} onNewKeyBindingConfirmed={onNewKeyBindingConfirmed} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const KeyboardShortcutsDialog = memo(({
|
const KeyboardShortcutsDialog = memo(({
|
||||||
isShown, onHide, keyBindings, setKeyBindings, currentCutSeg,
|
isShown, onHide, keyBindings, setKeyBindings, resetKeyBindings, currentCutSeg,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
@ -468,7 +489,7 @@ const KeyboardShortcutsDialog = memo(({
|
||||||
onConfirm={onHide}
|
onConfirm={onHide}
|
||||||
topOffset="3vh"
|
topOffset="3vh"
|
||||||
>
|
>
|
||||||
{isShown ? <KeyboardShortcuts keyBindings={keyBindings} setKeyBindings={setKeyBindings} currentCutSeg={currentCutSeg} /> : <div />}
|
{isShown ? <KeyboardShortcuts keyBindings={keyBindings} setKeyBindings={setKeyBindings} currentCutSeg={currentCutSeg} resetKeyBindings={resetKeyBindings} /> : <div />}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef, useCallback } from 'react';
|
||||||
import i18n from 'i18next';
|
import i18n from 'i18next';
|
||||||
|
|
||||||
import { errorToast } from '../util';
|
import { errorToast } from '../util';
|
||||||
|
@ -102,6 +102,10 @@ export default () => {
|
||||||
useEffect(() => safeSetConfig('segmentsToChaptersOnly', segmentsToChaptersOnly), [segmentsToChaptersOnly]);
|
useEffect(() => safeSetConfig('segmentsToChaptersOnly', segmentsToChaptersOnly), [segmentsToChaptersOnly]);
|
||||||
const [keyBindings, setKeyBindings] = useState(safeGetConfig('keyBindings'));
|
const [keyBindings, setKeyBindings] = useState(safeGetConfig('keyBindings'));
|
||||||
useEffect(() => safeSetConfig('keyBindings', keyBindings), [keyBindings]);
|
useEffect(() => safeSetConfig('keyBindings', keyBindings), [keyBindings]);
|
||||||
|
const resetKeyBindings = useCallback(() => {
|
||||||
|
configStore.reset('keyBindings');
|
||||||
|
setKeyBindings(safeGetConfig('keyBindings'));
|
||||||
|
}, []);
|
||||||
|
|
||||||
// NOTE! This useEffect must be placed after all usages of firstUpdateRef.current (safeSetConfig)
|
// NOTE! This useEffect must be placed after all usages of firstUpdateRef.current (safeSetConfig)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -182,5 +186,6 @@ export default () => {
|
||||||
setSegmentsToChaptersOnly,
|
setSegmentsToChaptersOnly,
|
||||||
keyBindings,
|
keyBindings,
|
||||||
setKeyBindings,
|
setKeyBindings,
|
||||||
|
resetKeyBindings,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Ładowanie…
Reference in New Issue