1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-22 02:12:30 +01:00

add keyboard actions

- Open previous file `batchOpenPreviousFile`
- Open next `filebatchOpenNextFile`

closes #1926
This commit is contained in:
Mikael Finstad 2024-03-17 23:04:11 +08:00
parent 55a915b2f7
commit ef59dbda84
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26
4 changed files with 499 additions and 474 deletions

View File

@ -51,12 +51,14 @@ const defaultKeyBindings = [
{ keys: 'ctrl+up', action: 'timelineZoomIn' },
{ keys: 'command+up', action: 'timelineZoomIn' },
{ keys: 'shift+up', action: 'batchPreviousFile' },
{ keys: 'ctrl+shift+up', action: 'batchOpenPreviousFile' },
{ keys: 'pagedown', action: 'jumpLastSegment' },
{ keys: 'down', action: 'jumpNextSegment' },
{ keys: 'ctrl+down', action: 'timelineZoomOut' },
{ keys: 'command+down', action: 'timelineZoomOut' },
{ keys: 'shift+down', action: 'batchNextFile' },
{ keys: 'ctrl+shift+down', action: 'batchOpenNextFile' },
{ keys: 'shift+enter', action: 'batchOpenSelectedFile' },

View File

@ -1653,19 +1653,24 @@ function App() {
}
}, [userOpenSingleFile, setWorking, filePath]);
const batchFileJump = useCallback((direction) => {
const batchFileJump = useCallback((direction: number, alsoOpen: boolean) => {
if (batchFiles.length === 0) return;
let newSelectedBatchFiles: string[];
if (selectedBatchFiles.length === 0) {
setSelectedBatchFiles([batchFiles[0]!.path]);
return;
}
newSelectedBatchFiles = [batchFiles[0]!.path];
} else {
const selectedFilePath = selectedBatchFiles[direction > 0 ? selectedBatchFiles.length - 1 : 0];
const pathIndex = batchFiles.findIndex(({ path }) => path === selectedFilePath);
if (pathIndex === -1) return;
const nextFile = batchFiles[pathIndex + direction];
if (!nextFile) return;
setSelectedBatchFiles([nextFile.path]);
}, [batchFiles, selectedBatchFiles]);
newSelectedBatchFiles = [nextFile.path];
}
setSelectedBatchFiles(newSelectedBatchFiles);
if (alsoOpen) batchOpenSingleFile(newSelectedBatchFiles[0]);
}, [batchFiles, batchOpenSingleFile, selectedBatchFiles]);
const batchOpenSelectedFile = useCallback(() => {
if (selectedBatchFiles.length === 0) return;
@ -2032,7 +2037,7 @@ function App() {
type MainKeyboardAction = Exclude<KeyboardAction, 'closeActiveScreen' | 'toggleKeyboardShortcuts'>;
const mainActions = useMemo<Record<MainKeyboardAction, ((a: { keyup?: boolean | undefined }) => boolean) | ((a: { keyup?: boolean | undefined }) => void)>>(() => {
const mainActions = useMemo(() => {
async function exportYouTube() {
if (!checkFileOpened()) return;
@ -2043,7 +2048,7 @@ function App() {
seekAccelerationRef.current = 1;
}
return {
const ret: Record<MainKeyboardAction, ((a: { keyup?: boolean | undefined }) => boolean) | ((a: { keyup?: boolean | undefined }) => void)> = {
// NOTE: Do not change these keys because users have bound keys by these names in their config files
// For actions, see also KeyboardShortcuts.jsx
togglePlayNoResetSpeed: () => togglePlay(),
@ -2099,8 +2104,10 @@ function App() {
jumpTimelineEnd,
timelineZoomIn: () => { zoomRel(1); return false; },
timelineZoomOut: () => { zoomRel(-1); return false; },
batchPreviousFile: () => batchFileJump(-1),
batchNextFile: () => batchFileJump(1),
batchPreviousFile: () => batchFileJump(-1, false),
batchNextFile: () => batchFileJump(1, false),
batchOpenPreviousFile: () => batchFileJump(-1, true),
batchOpenNextFile: () => batchFileJump(1, true),
batchOpenSelectedFile,
closeBatch,
removeCurrentSegment: () => removeCutSegment(currentSegIndexSafe),
@ -2172,6 +2179,8 @@ function App() {
showIncludeExternalStreamsDialog,
toggleFullscreenVideo,
};
return ret;
}, [addSegment, alignSegmentTimesToKeyframes, apparentCutSegments, askStartTimeOffset, batchFileJump, batchOpenSelectedFile, captureSnapshot, captureSnapshotAsCoverArt, changePlaybackRate, checkFileOpened, cleanupFilesDialog, clearSegments, closeBatch, closeFileWithConfirm, combineOverlappingSegments, combineSelectedSegments, concatBatch, convertFormatBatch, copySegmentsToClipboard, createFixedDurationSegments, createNumSegments, createRandomSegments, createSegmentsFromKeyframes, currentSegIndexSafe, cutSegments.length, cutSegmentsHistory, deselectAllSegments, detectBlackScenes, detectSceneChanges, detectSilentScenes, duplicateCurrentSegment, editCurrentSegmentTags, extractAllStreams, extractCurrentSegmentFramesAsImages, extractSelectedSegmentsFramesAsImages, fillSegmentsGaps, goToTimecode, handleShowStreamsSelectorClick, increaseRotation, invertAllSegments, invertSelectedSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, onExportPress, onLabelSegment, openFilesDialog, openSendReportDialogWithState, pause, play, removeCutSegment, removeSelectedSegments, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, selectAllSegments, selectOnlyCurrentSegment, setCurrentSegIndex, setCutEnd, setCutStart, setPlaybackVolume, shiftAllSegmentTimes, shortStep, showIncludeExternalStreamsDialog, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleCurrentSegmentSelected, toggleFullscreenVideo, toggleKeyframeCut, toggleLastCommands, toggleLoopSelectedSegments, togglePlay, toggleSegmentsList, toggleSettings, toggleShowKeyframes, toggleShowThumbnails, toggleStreamsSelector, toggleStripAudio, toggleStripThumbnail, toggleWaveformMode, tryFixInvalidDuration, userHtml5ifyCurrentFile, zoomRel]);
const getKeyboardAction = useCallback((action: MainKeyboardAction) => mainActions[action], [mainActions]);

View File

@ -126,7 +126,7 @@ const KeyboardShortcuts = memo(({
const { mouseWheelZoomModifierKey } = useUserSettings();
const { actionsMap, extraLinesPerCategory } = useMemo<{ actionsMap: ActionsMap, extraLinesPerCategory: Record<Category, ReactNode> }>(() => {
const { actionsMap, extraLinesPerCategory } = useMemo(() => {
const playbackCategory = t('Playback');
const selectivePlaybackCategory = t('Playback/preview segments only');
const seekingCategory = t('Seeking');
@ -137,26 +137,8 @@ const KeyboardShortcuts = memo(({
const otherCategory = t('Other operations');
const streamsCategory = t('Tracks');
return {
extraLinesPerCategory: {
[zoomOperationsCategory]: [
<div key="1" style={{ ...rowStyle, alignItems: 'center' }}>
<Text>{t('Zoom in/out timeline')}</Text>
<div style={{ flexGrow: 1 }} />
<FaMouse style={{ marginRight: 3 }} />
<Text>{t('Mouse scroll/wheel up/down')}</Text>
</div>,
<div key="2" style={{ ...rowStyle, alignItems: 'center' }}>
<Text>{t('Pan timeline')}</Text>
<div style={{ flexGrow: 1 }} />
{getModifier(mouseWheelZoomModifierKey).map((v) => <kbd key={v} style={{ marginRight: '.7em' }}>{v}</kbd>)}
<FaMouse style={{ marginRight: 3 }} />
<Text>{t('Mouse scroll/wheel up/down')}</Text>
</div>,
],
},
actionsMap: {
// eslint-disable-next-line no-shadow
const actionsMap: ActionsMap = {
toggleLastCommands: {
name: t('Last ffmpeg commands'),
},
@ -506,10 +488,18 @@ const KeyboardShortcuts = memo(({
name: t('Previous file'),
category: batchFilesCategory,
},
batchOpenPreviousFile: {
name: t('Open previous file'),
category: batchFilesCategory,
},
batchNextFile: {
name: t('Next file'),
category: batchFilesCategory,
},
batchOpenNextFile: {
name: t('Open next file'),
category: batchFilesCategory,
},
batchOpenSelectedFile: {
name: t('Open selected file'),
category: batchFilesCategory,
@ -604,7 +594,31 @@ const KeyboardShortcuts = memo(({
name: t('Quit LosslessCut'),
category: otherCategory,
},
} satisfies ActionsMap,
};
// eslint-disable-next-line no-shadow
const extraLinesPerCategory: Record<Category, ReactNode> = {
[zoomOperationsCategory]: [
<div key="1" style={{ ...rowStyle, alignItems: 'center' }}>
<Text>{t('Zoom in/out timeline')}</Text>
<div style={{ flexGrow: 1 }} />
<FaMouse style={{ marginRight: 3 }} />
<Text>{t('Mouse scroll/wheel up/down')}</Text>
</div>,
<div key="2" style={{ ...rowStyle, alignItems: 'center' }}>
<Text>{t('Pan timeline')}</Text>
<div style={{ flexGrow: 1 }} />
{getModifier(mouseWheelZoomModifierKey).map((v) => <kbd key={v} style={{ marginRight: '.7em' }}>{v}</kbd>)}
<FaMouse style={{ marginRight: 3 }} />
<Text>{t('Mouse scroll/wheel up/down')}</Text>
</div>,
],
};
return {
extraLinesPerCategory,
actionsMap,
};
}, [currentCutSeg, mouseWheelZoomModifierKey, t]);

View File

@ -1,4 +1,4 @@
export type KeyboardAction = 'addSegment' | 'togglePlayResetSpeed' | 'togglePlayNoResetSpeed' | 'reducePlaybackRate' | 'reducePlaybackRateMore' | 'increasePlaybackRate' | 'increasePlaybackRateMore' | 'timelineToggleComfortZoom' | 'seekPreviousFrame' | 'seekNextFrame' | 'captureSnapshot' | 'setCutStart' | 'setCutEnd' | 'removeCurrentSegment' | 'cleanupFilesDialog' | 'splitCurrentSegment' | 'increaseRotation' | 'goToTimecode' | 'seekBackwards' | 'seekBackwardsPercent' | 'seekBackwardsPercent' | 'seekBackwardsKeyframe' | 'jumpCutStart' | 'seekForwards' | 'seekForwardsPercent' | 'seekForwardsPercent' | 'seekForwardsKeyframe' | 'jumpCutEnd' | 'jumpTimelineStart' | 'jumpTimelineEnd' | 'jumpFirstSegment' | 'jumpPrevSegment' | 'timelineZoomIn' | 'timelineZoomIn' | 'batchPreviousFile' | 'jumpLastSegment' | 'jumpNextSegment' | 'timelineZoomOut' | 'timelineZoomOut' | 'batchNextFile' | 'batchOpenSelectedFile' | 'undo' | 'undo' | 'redo' | 'redo' | 'copySegmentsToClipboard' | 'copySegmentsToClipboard' | 'toggleFullscreenVideo' | 'labelCurrentSegment' | 'export' | 'toggleKeyboardShortcuts' | 'closeActiveScreen' | 'increaseVolume' | 'decreaseVolume' | 'detectBlackScenes' | 'detectSilentScenes' | 'detectSceneChanges' | 'toggleLastCommands' | 'play' | 'pause' | 'reloadFile' | 'html5ify' | 'togglePlayOnlyCurrentSegment' | 'toggleLoopOnlyCurrentSegment' | 'toggleLoopStartEndOnlyCurrentSegment' | 'toggleLoopSelectedSegments' | 'editCurrentSegmentTags' | 'duplicateCurrentSegment' | 'reorderSegsByStartTime' | 'invertAllSegments' | 'fillSegmentsGaps' | 'shiftAllSegmentTimes' | 'alignSegmentTimesToKeyframes' | 'createSegmentsFromKeyframes' | 'createFixedDurationSegments' | 'createNumSegments' | 'createRandomSegments' | 'shuffleSegments' | 'combineOverlappingSegments' | 'combineSelectedSegments' | 'clearSegments' | 'toggleSegmentsList' | 'selectOnlyCurrentSegment' | 'deselectAllSegments' | 'selectAllSegments' | 'toggleCurrentSegmentSelected' | 'invertSelectedSegments' | 'removeSelectedSegments' | 'toggleStreamsSelector' | 'extractAllStreams' | 'showStreamsSelector' | 'showIncludeExternalStreamsDialog' | 'captureSnapshotAsCoverArt' | 'extractCurrentSegmentFramesAsImages' | 'extractSelectedSegmentsFramesAsImages' | 'convertFormatBatch' | 'convertFormatCurrentFile' | 'fixInvalidDuration' | 'closeBatch' | 'concatBatch' | 'toggleKeyframeCutMode' | 'toggleCaptureFormat' | 'toggleStripAudio' | 'toggleStripThumbnail' | 'setStartTimeOffset' | 'toggleWaveformMode' | 'toggleShowThumbnails' | 'toggleShowKeyframes' | 'toggleSettings' | 'openSendReportDialog' | 'openFilesDialog' | 'exportYouTube' | 'closeCurrentFile' | 'quit';
export type KeyboardAction = 'addSegment' | 'togglePlayResetSpeed' | 'togglePlayNoResetSpeed' | 'reducePlaybackRate' | 'reducePlaybackRateMore' | 'increasePlaybackRate' | 'increasePlaybackRateMore' | 'timelineToggleComfortZoom' | 'seekPreviousFrame' | 'seekNextFrame' | 'captureSnapshot' | 'setCutStart' | 'setCutEnd' | 'removeCurrentSegment' | 'cleanupFilesDialog' | 'splitCurrentSegment' | 'increaseRotation' | 'goToTimecode' | 'seekBackwards' | 'seekBackwardsPercent' | 'seekBackwardsPercent' | 'seekBackwardsKeyframe' | 'jumpCutStart' | 'seekForwards' | 'seekForwardsPercent' | 'seekForwardsPercent' | 'seekForwardsKeyframe' | 'jumpCutEnd' | 'jumpTimelineStart' | 'jumpTimelineEnd' | 'jumpFirstSegment' | 'jumpPrevSegment' | 'timelineZoomIn' | 'timelineZoomIn' | 'batchPreviousFile' | 'jumpLastSegment' | 'jumpNextSegment' | 'timelineZoomOut' | 'timelineZoomOut' | 'batchNextFile' | 'batchOpenSelectedFile' | 'batchOpenPreviousFile' | 'batchOpenNextFile' | 'undo' | 'undo' | 'redo' | 'redo' | 'copySegmentsToClipboard' | 'copySegmentsToClipboard' | 'toggleFullscreenVideo' | 'labelCurrentSegment' | 'export' | 'toggleKeyboardShortcuts' | 'closeActiveScreen' | 'increaseVolume' | 'decreaseVolume' | 'detectBlackScenes' | 'detectSilentScenes' | 'detectSceneChanges' | 'toggleLastCommands' | 'play' | 'pause' | 'reloadFile' | 'html5ify' | 'togglePlayOnlyCurrentSegment' | 'toggleLoopOnlyCurrentSegment' | 'toggleLoopStartEndOnlyCurrentSegment' | 'toggleLoopSelectedSegments' | 'editCurrentSegmentTags' | 'duplicateCurrentSegment' | 'reorderSegsByStartTime' | 'invertAllSegments' | 'fillSegmentsGaps' | 'shiftAllSegmentTimes' | 'alignSegmentTimesToKeyframes' | 'createSegmentsFromKeyframes' | 'createFixedDurationSegments' | 'createNumSegments' | 'createRandomSegments' | 'shuffleSegments' | 'combineOverlappingSegments' | 'combineSelectedSegments' | 'clearSegments' | 'toggleSegmentsList' | 'selectOnlyCurrentSegment' | 'deselectAllSegments' | 'selectAllSegments' | 'toggleCurrentSegmentSelected' | 'invertSelectedSegments' | 'removeSelectedSegments' | 'toggleStreamsSelector' | 'extractAllStreams' | 'showStreamsSelector' | 'showIncludeExternalStreamsDialog' | 'captureSnapshotAsCoverArt' | 'extractCurrentSegmentFramesAsImages' | 'extractSelectedSegmentsFramesAsImages' | 'convertFormatBatch' | 'convertFormatCurrentFile' | 'fixInvalidDuration' | 'closeBatch' | 'concatBatch' | 'toggleKeyframeCutMode' | 'toggleCaptureFormat' | 'toggleStripAudio' | 'toggleStripThumbnail' | 'setStartTimeOffset' | 'toggleWaveformMode' | 'toggleShowThumbnails' | 'toggleShowKeyframes' | 'toggleSettings' | 'openSendReportDialog' | 'openFilesDialog' | 'exportYouTube' | 'closeCurrentFile' | 'quit';
export interface KeyBinding {
keys: string,