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

improve batch list selection

https://github.com/mifi/lossless-cut/issues/89#issuecomment-1049831536
This commit is contained in:
Mikael Finstad 2022-02-24 20:57:35 +08:00
parent 7c28d66025
commit bdb050492f
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26
5 changed files with 42 additions and 12 deletions

View File

@ -52,6 +52,8 @@ const defaultKeyBindings = [
{ keys: 'command+down', action: 'timelineZoomOut' },
{ keys: 'shift+down', action: 'batchNextFile' },
{ keys: 'shift+enter', action: 'batchOpenSelectedFile' },
// https://github.com/mifi/lossless-cut/issues/610
{ keys: 'ctrl+z', action: 'undo' },
{ keys: 'command+z', action: 'undo' },

View File

@ -153,6 +153,7 @@ const App = memo(() => {
// Batch state / concat files
const [batchFiles, setBatchFiles] = useState([]);
const [selectedBatchFiles, setSelectedBatchFiles] = useState([]);
// Segment related state
const segCounterRef = useRef(0);
@ -1055,9 +1056,13 @@ const App = memo(() => {
// eslint-disable-next-line no-alert
if (askBeforeClose && !window.confirm(i18n.t('Are you sure you want to close the loaded batch of files?'))) return;
setBatchFiles([]);
setSelectedBatchFiles([]);
}, [askBeforeClose]);
const batchRemoveFile = useCallback((path) => setBatchFiles((existingBatch) => existingBatch.filter((existingFile) => existingFile.path !== path)), []);
const batchRemoveFile = useCallback((path) => {
setBatchFiles((existingBatch) => existingBatch.filter((existingFile) => existingFile.path !== path));
setSelectedBatchFiles([]);
}, []);
const cleanupFilesDialog = useCallback(async () => {
if (!isFileOpened) return;
@ -1565,12 +1570,28 @@ const App = memo(() => {
}, [userOpenSingleFile, setWorking, filePath]);
const batchFileJump = useCallback((direction) => {
const pathIndex = batchFiles.findIndex(({ path }) => path === filePath);
if (batchFiles.length === 0) return;
if (selectedBatchFiles.length === 0) {
setSelectedBatchFiles([batchFiles[0].path]);
return;
}
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;
batchOpenSingleFile(nextFile.path);
}, [filePath, batchFiles, batchOpenSingleFile]);
setSelectedBatchFiles([nextFile.path]);
}, [batchFiles, selectedBatchFiles]);
const batchOpenSelectedFile = useCallback(() => {
if (selectedBatchFiles.length === 0) return;
batchOpenSingleFile(selectedBatchFiles[0]);
}, [batchOpenSingleFile, selectedBatchFiles]);
const onBatchFileSelect = useCallback((path) => {
if (selectedBatchFiles.includes(path)) batchOpenSingleFile(path);
else setSelectedBatchFiles([path]);
}, [batchOpenSingleFile, selectedBatchFiles]);
const goToTimecode = useCallback(async () => {
if (!filePath) return;
@ -1727,6 +1748,7 @@ const App = memo(() => {
timelineZoomOut: () => { zoomRel(-1); return false; },
batchPreviousFile: () => batchFileJump(-1),
batchNextFile: () => batchFileJump(1),
batchOpenSelectedFile,
closeBatch,
removeCurrentSegment: () => removeCutSegment(currentSegIndexSafe),
undo: () => cutSegmentsHistory.back(),
@ -1796,7 +1818,7 @@ const App = memo(() => {
if (match) return bubble;
return true; // bubble the event
}, [addCutSegment, askSetStartTimeOffset, batchFileJump, captureSnapshot, changePlaybackRate, cleanupFilesDialog, clearSegments, closeBatch, closeExportConfirm, concatCurrentBatch, concatDialogVisible, convertFormatBatch, createFixedDurationSegments, createNumSegments, currentSegIndexSafe, cutSegmentsHistory, disableAllSegments, enableAllSegments, enableOnlyCurrentSegment, exportConfirmVisible, extractAllStreams, goToTimecode, increaseRotation, invertAllCutSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyboardShortcutsVisible, onExportConfirm, onExportPress, onLabelSegmentPress, pause, play, removeCutSegment, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, setCutEnd, setCutStart, shortStep, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleCurrentSegmentEnabled, toggleHelp, toggleKeyboardShortcuts, toggleKeyframeCut, togglePlay, toggleSegmentsList, toggleStreamsSelector, toggleStripAudio, userHtml5ifyCurrentFile, zoomRel]);
}, [addCutSegment, askSetStartTimeOffset, batchFileJump, batchOpenSelectedFile, captureSnapshot, changePlaybackRate, cleanupFilesDialog, clearSegments, closeBatch, closeExportConfirm, concatCurrentBatch, concatDialogVisible, convertFormatBatch, createFixedDurationSegments, createNumSegments, currentSegIndexSafe, cutSegmentsHistory, disableAllSegments, enableAllSegments, enableOnlyCurrentSegment, exportConfirmVisible, extractAllStreams, goToTimecode, increaseRotation, invertAllCutSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyboardShortcutsVisible, onExportConfirm, onExportPress, onLabelSegmentPress, pause, play, removeCutSegment, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, setCutEnd, setCutStart, shortStep, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleCurrentSegmentEnabled, toggleHelp, toggleKeyboardShortcuts, toggleKeyframeCut, togglePlay, toggleSegmentsList, toggleStreamsSelector, toggleStripAudio, userHtml5ifyCurrentFile, zoomRel]);
useKeyboard({ keyBindings, onKeyPress });
@ -1843,8 +1865,10 @@ const App = memo(() => {
const mapPathsToFiles = (paths) => paths.map((path) => ({ path, name: basename(path) }));
if (append) {
const newUniquePaths = newPaths.filter((newPath) => !existingFiles.some(({ path: existingPath }) => newPath === existingPath));
setSelectedBatchFiles([newUniquePaths[0]]);
return [...existingFiles, ...mapPathsToFiles(newUniquePaths)];
}
setSelectedBatchFiles([newPaths[0]]);
return mapPathsToFiles(newPaths);
});
}, []);
@ -2196,10 +2220,11 @@ const App = memo(() => {
<AnimatePresence>
{showLeftBar && (
<BatchFilesList
selectedBatchFiles={selectedBatchFiles}
filePath={filePath}
width={leftBarWidth}
batchFiles={batchFiles}
batchOpenSingleFile={batchOpenSingleFile}
onBatchFileSelect={onBatchFileSelect}
batchRemoveFile={batchRemoveFile}
closeBatch={closeBatch}
onMergeFilesClick={concatCurrentBatch}

View File

@ -5,7 +5,7 @@ import { FaAngleRight, FaFile } from 'react-icons/fa';
import useContextMenu from '../hooks/useContextMenu';
import { primaryTextColor } from '../colors';
const BatchFile = memo(({ path, filePath, name, onOpen, onDelete }) => {
const BatchFile = memo(({ path, isOpen, isSelected, name, onSelect, onDelete }) => {
const ref = useRef();
const { t } = useTranslation();
@ -14,15 +14,14 @@ const BatchFile = memo(({ path, filePath, name, onOpen, onDelete }) => {
], [t, onDelete, path]);
useContextMenu(ref, contextMenuTemplate);
const isCurrent = path === filePath;
return (
<div ref={ref} role="button" style={{ background: isCurrent ? 'rgba(255,255,255,0.15)' : undefined, fontSize: 13, padding: '3px 6px', display: 'flex', alignItems: 'center', alignContent: 'flex-start' }} title={path} onClick={() => onOpen(path)}>
<div ref={ref} role="button" style={{ background: isSelected ? 'rgba(255,255,255,0.15)' : undefined, fontSize: 13, padding: '3px 6px', display: 'flex', alignItems: 'center', alignContent: 'flex-start' }} title={path} onClick={() => onSelect(path)}>
<FaFile size={14} style={{ color: primaryTextColor, flexShrink: 0 }} />
<div style={{ flexBasis: 4, flexShrink: 0 }} />
<div style={{ whiteSpace: 'nowrap', cursor: 'pointer', overflow: 'hidden' }}>{name}</div>
<div style={{ flexGrow: 1 }} />
{isCurrent && <FaAngleRight size={14} style={{ color: 'white', marginRight: -5, flexShrink: 0 }} />}
{isOpen && <FaAngleRight size={14} style={{ color: 'white', marginRight: -5, flexShrink: 0 }} />}
</div>
);
});

View File

@ -16,7 +16,7 @@ const iconStyle = {
padding: '3px 5px',
};
const BatchFilesList = memo(({ filePath, width, batchFiles, batchOpenSingleFile, batchRemoveFile, closeBatch, onMergeFilesClick, onBatchConvertToSupportedFormatClick }) => {
const BatchFilesList = memo(({ selectedBatchFiles, filePath, width, batchFiles, onBatchFileSelect, batchRemoveFile, closeBatch, onMergeFilesClick, onBatchConvertToSupportedFormatClick }) => {
const { t } = useTranslation();
return (
@ -37,7 +37,7 @@ const BatchFilesList = memo(({ filePath, width, batchFiles, batchOpenSingleFile,
<div style={{ overflowX: 'hidden', overflowY: 'auto' }}>
{batchFiles.map(({ path, name }) => (
<BatchFile key={path} path={path} name={name} filePath={filePath} onOpen={batchOpenSingleFile} onDelete={batchRemoveFile} />
<BatchFile key={path} path={path} name={name} isSelected={selectedBatchFiles.includes(path)} isOpen={filePath === path} onSelect={onBatchFileSelect} onDelete={batchRemoveFile} />
))}
</div>
</motion.div>

View File

@ -365,6 +365,10 @@ const KeyboardShortcuts = memo(({
name: t('Next file'),
category: batchFilesCategory,
},
batchOpenSelectedFile: {
name: t('Open selected file'),
category: batchFilesCategory,
},
closeBatch: {
name: t('Close batch'),
category: batchFilesCategory,