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

add preference for color intensity

https://github.com/mifi/lossless-cut/discussions/1507
This commit is contained in:
Mikael Finstad 2023-04-03 09:27:30 +09:00
parent 10f8092d15
commit 0afa76a165
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26
12 changed files with 316 additions and 283 deletions

View File

@ -123,6 +123,7 @@ const defaults = {
},
allowMultipleInstances: false,
darkMode: true,
preferStrongColors: false,
};
// For portable app: https://github.com/mifi/lossless-cut/issues/645

View File

@ -26,7 +26,7 @@ import useFrameCapture from './hooks/useFrameCapture';
import useSegments from './hooks/useSegments';
import useDirectoryAccess, { DirectoryAccessDeclinedError } from './hooks/useDirectoryAccess';
import UserSettingsContext from './contexts/UserSettingsContext';
import { UserSettingsContext, SegColorsContext } from './contexts';
import NoFileLoaded from './NoFileLoaded';
import Canvas from './Canvas';
@ -50,6 +50,7 @@ import OutputFormatSelect from './components/OutputFormatSelect';
import { loadMifiLink, runStartupCheck } from './mifi';
import { controlsBackground, darkModeTransition } from './colors';
import { getSegColor } from './util/colors';
import {
getStreamFps, isCuttingStart, isCuttingEnd,
readFileMeta, getSmarterOutFormat, renderThumbnails as ffmpegRenderThumbnails,
@ -175,7 +176,7 @@ const App = memo(() => {
const allUserSettings = useUserSettingsRoot();
const {
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, setStoreProjectInWorkingDir, enableOverwriteOutput, mouseWheelZoomModifierKey, captureFrameMethod, captureFrameQuality, captureFrameFileNameFormat, enableNativeHevc, cleanupChoices, setCleanupChoices, darkMode, setDarkMode,
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, setStoreProjectInWorkingDir, enableOverwriteOutput, mouseWheelZoomModifierKey, captureFrameMethod, captureFrameQuality, captureFrameFileNameFormat, enableNativeHevc, cleanupChoices, setCleanupChoices, darkMode, setDarkMode, preferStrongColors,
} = allUserSettings;
useEffect(() => {
@ -510,6 +511,13 @@ const App = memo(() => {
...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 segColorsContext = useMemo(() => ({
getSegColor: (seg) => {
const color = getSegColor(seg);
return preferStrongColors ? color.desaturate(0.2) : color.desaturate(0.6);
},
}), [preferStrongColors]);
const isCopyingStreamId = useCallback((path, streamId) => (
!!(copyStreamIdsByFile[path] || {})[streamId]
), [copyStreamIdsByFile]);
@ -2153,6 +2161,7 @@ const App = memo(() => {
// throw new Error('Test error boundary');
return (
<SegColorsContext.Provider value={segColorsContext}>
<UserSettingsContext.Provider value={userSettingsContext}>
<ThemeProvider value={theme}>
<div className={darkMode ? 'dark-theme' : undefined} style={{ display: 'flex', flexDirection: 'column', height: '100vh', color: 'var(--gray12)', background: 'var(--gray1)', transition: darkModeTransition }}>
@ -2416,6 +2425,7 @@ const App = memo(() => {
</div>
</ThemeProvider>
</UserSettingsContext.Provider>
</SegColorsContext.Provider>
);
});

View File

@ -18,7 +18,8 @@ import Select from './components/Select';
import SimpleModeButton from './components/SimpleModeButton';
import { withBlur, mirrorTransform, checkAppPath } from './util';
import { toast } from './swal';
import { getSegColor } from './util/colors';
import { getSegColor as getSegColorRaw } from './util/colors';
import { useSegColors } from './contexts';
import { formatDuration, parseDuration, isExactDurationMatch } from './util/duration';
import useUserSettings from './hooks/useUserSettings';
@ -31,6 +32,7 @@ const leftRightWidth = 100;
const CutTimeInput = memo(({ darkMode, cutTime, setCutTime, startTimeOffset, seekAbs, currentCutSeg, currentApparentCutSeg, isStart }) => {
const { t } = useTranslation();
const { getSegColor } = useSegColors();
const [cutTimeManual, setCutTimeManual] = useState();
@ -41,8 +43,10 @@ const CutTimeInput = memo(({ darkMode, cutTime, setCutTime, startTimeOffset, see
const isCutTimeManualSet = () => cutTimeManual !== undefined;
const border = useMemo(() => {
const segColor = getSegColor(currentCutSeg);
const border = `.1em solid ${darkMode ? segColor.desaturate(0.9).lightness(50).string() : segColor.desaturate(0.7).lightness(60).string()}`;
return `.1em solid ${darkMode ? segColor.desaturate(0.4).lightness(50).string() : segColor.desaturate(0.2).lightness(60).string()}`;
}, [currentCutSeg, darkMode, getSegColor]);
const cutTimeInputStyle = {
border, borderRadius: 5, backgroundColor: 'var(--gray5)', transition: darkModeTransition, fontSize: 13, textAlign: 'center', padding: '1px 5px', marginTop: 0, marginBottom: 0, marginLeft: isStart ? 0 : 5, marginRight: isStart ? 5 : 0, boxSizing: 'border-box', fontFamily: 'inherit', width: 90, outline: 'none',
@ -142,6 +146,7 @@ const BottomBar = memo(({
toggleEnableThumbnails, toggleWaveformMode, waveformMode, showThumbnails,
}) => {
const { t } = useTranslation();
const { getSegColor } = useSegColors();
// ok this is a bit over-engineered but what the hell!
const loopSelectedSegmentsButtonStyle = useMemo(() => {
@ -149,7 +154,7 @@ const BottomBar = memo(({
const selectedSegmentsSafe = (selectedSegments.length > 1 ? selectedSegments : [selectedSegments[0], selectedSegments[0]]).slice(0, 10);
const gradientColors = selectedSegmentsSafe.map((seg, i) => {
const segColor = getSegColor(seg);
const segColor = getSegColorRaw(seg);
// make colors stronger, the more segments
return `${segColor.alpha(Math.max(0.4, Math.min(0.8, selectedSegmentsSafe.length / 3))).string()} ${((i / (selectedSegmentsSafe.length - 1)) * 100).toFixed(1)}%`;
}).join(', ');
@ -191,7 +196,7 @@ const BottomBar = memo(({
const newIndex = currentSegIndexSafe + direction;
const seg = cutSegments[newIndex];
const backgroundColor = seg && getSegColor(seg).desaturate(0.9).lightness(darkMode ? 35 : 55).string();
const backgroundColor = seg && getSegColor(seg).desaturate(0.6).lightness(darkMode ? 35 : 55).string();
const opacity = seg ? undefined : 0.5;
const text = seg ? `${newIndex + 1}` : '-';
const wide = text.length > 1;

View File

@ -12,7 +12,7 @@ import Swal from './swal';
import useContextMenu from './hooks/useContextMenu';
import useUserSettings from './hooks/useUserSettings';
import { saveColor, controlsBackground, primaryTextColor, darkModeTransition } from './colors';
import { getSegColor } from './util/colors';
import { useSegColors } from './contexts';
import { mySpring } from './animations';
const buttonBaseStyle = {
@ -24,6 +24,7 @@ const neutralButtonColor = 'var(--gray8)';
const Segment = memo(({ darkMode, seg, index, currentSegIndex, formatTimecode, getFrameCount, updateOrder, invertCutSegments, onClick, onRemovePress, onRemoveSelected, onLabelSelectedSegments, onReorderPress, onLabelPress, selected, onSelectSingleSegment, onToggleSegmentSelected, onDeselectAllSegments, onSelectSegmentsByLabel, onSelectAllSegments, jumpSegStart, jumpSegEnd, addSegment, onViewSegmentTags, onExtractSegmentFramesAsImages, onInvertSelectedSegments }) => {
const { t } = useTranslation();
const { getSegColor } = useSegColors();
const ref = useRef();
@ -81,7 +82,7 @@ const Segment = memo(({ darkMode, seg, index, currentSegIndex, formatTimecode, g
const segColor = getSegColor(seg);
const color = segColor.desaturate(0.75).lightness(darkMode ? 35 : 55);
const color = segColor.desaturate(0.25).lightness(darkMode ? 35 : 55);
const borderColor = darkMode ? color.lighten(0.5) : color.darken(0.3);
return <b style={{ cursor: 'grab', color: 'white', padding: '0 4px', marginRight: 3, marginLeft: -3, background: color.string(), border: `1px solid ${isActive ? borderColor.string() : 'transparent'}`, borderRadius: 10, fontSize: 12 }}>{index + 1}</b>;
@ -154,6 +155,7 @@ const SegmentList = memo(({
jumpSegStart, jumpSegEnd, onViewSegmentTags,
}) => {
const { t } = useTranslation();
const { getSegColor } = useSegColors();
const { invertCutSegments, simpleMode, darkMode } = useUserSettings();
@ -198,7 +200,7 @@ const SegmentList = memo(({
}
function renderFooter() {
const getButtonColor = (seg) => getSegColor(seg).desaturate(0.8).lightness(darkMode ? 45 : 55).string();
const getButtonColor = (seg) => getSegColor(seg).desaturate(0.3).lightness(darkMode ? 45 : 55).string();
const currentSegColor = getButtonColor(currentCutSeg);
const segAtCursorColor = getButtonColor(segmentAtCursor);

View File

@ -12,8 +12,6 @@ import useUserSettings from './hooks/useUserSettings';
import { timelineBackground, darkModeTransition } from './colors';
import { getSegColor } from './util/colors';
const currentTimeWidth = 1;
const Waveform = memo(({ waveform, calculateTimelinePercent, durationSafe }) => {
@ -295,8 +293,6 @@ const Timeline = memo(({
)}
{apparentCutSegments.map((seg, i) => {
const segColor = getSegColor(seg);
if (seg.start === 0 && seg.end === 0) return null; // No video loaded
const selected = invertCutSegments || isSegmentSelected({ segId: seg.segId });
@ -304,14 +300,11 @@ const Timeline = memo(({
return (
<TimelineSeg
key={seg.segId}
seg={seg}
segNum={i}
segColor={segColor}
onSegClick={setCurrentSegIndex}
isActive={i === currentSegIndexSafe}
duration={durationSafe}
name={seg.name}
cutStart={seg.start}
cutEnd={seg.end}
invertCutSegments={invertCutSegments}
formatTimecode={formatTimecode}
selected={selected}

View File

@ -4,13 +4,18 @@ import { FaTrashAlt } from 'react-icons/fa';
import { mySpring } from './animations';
import useUserSettings from './hooks/useUserSettings';
import { useSegColors } from './contexts';
const TimelineSeg = memo(({
duration, cutStart, cutEnd, isActive, segNum, name,
onSegClick, invertCutSegments, segColor, formatTimecode, selected,
seg, duration, isActive, segNum, onSegClick, invertCutSegments, formatTimecode, selected,
}) => {
const { darkMode } = useUserSettings();
const { getSegColor } = useSegColors();
const segColor = useMemo(() => getSegColor(seg), [getSegColor, seg]);
const { name, start: cutStart, end: cutEnd } = seg;
const cutSectionWidth = `${((cutEnd - cutStart) / duration) * 100}%`;
@ -18,13 +23,13 @@ const TimelineSeg = memo(({
const markerBorder = useMemo(() => {
if (!isActive) return '2px solid transparent';
return `2px solid ${(darkMode ? segColor.desaturate(0.6).lightness(70) : segColor.desaturate(0.9).lightness(20)).string()}`;
return `2px solid ${darkMode ? segColor.desaturate(0.1).lightness(70).string() : segColor.desaturate(0.2).lightness(40).string()}`;
}, [darkMode, isActive, segColor]);
const backgroundColor = useMemo(() => {
if (invertCutSegments || !selected) return darkMode ? segColor.desaturate(0.9).lightness(25).string() : segColor.desaturate(0.9).lightness(80).string();
if (isActive) return darkMode ? segColor.desaturate(0.7).lightness(50).string() : segColor.desaturate(0.7).lightness(40).string();
return darkMode ? segColor.desaturate(0.7).lightness(40).string() : segColor.desaturate(0.9).lightness(55).string();
if (invertCutSegments || !selected) return darkMode ? segColor.desaturate(0.4).lightness(25).string() : segColor.desaturate(0.4).lightness(83).string();
if (isActive) return darkMode ? segColor.desaturate(0.2).lightness(50).string() : segColor.desaturate(0.2).lightness(60).string();
return darkMode ? segColor.desaturate(0.2).lightness(40).string() : segColor.desaturate(0.5).lightness(65).string();
}, [darkMode, invertCutSegments, isActive, segColor, selected]);
const markerBorderRadius = 5;

View File

@ -1,15 +1,16 @@
import React from 'react';
import React, { useMemo } from 'react';
import { getSegColor } from '../util/colors';
import { useSegColors } from '../contexts';
import useUserSettings from '../hooks/useUserSettings';
const SegmentCutpointButton = ({ currentCutSeg, side, Icon, onClick, title, style }) => {
const { darkMode } = useUserSettings();
const segColor = getSegColor(currentCutSeg);
const { getSegColor } = useSegColors();
const segColor = useMemo(() => getSegColor(currentCutSeg), [currentCutSeg, getSegColor]);
const start = side === 'start';
const border = `3px solid ${segColor.desaturate(0.9).lightness(darkMode ? 45 : 35).string()}`;
const backgroundColor = segColor.desaturate(0.9).lightness(darkMode ? 35 : 55).string();
const border = `3px solid ${segColor.desaturate(0.6).lightness(darkMode ? 45 : 35).string()}`;
const backgroundColor = segColor.desaturate(0.6).lightness(darkMode ? 35 : 55).string();
return (
<Icon

View File

@ -38,7 +38,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, enableOverwriteOutput, setEnableOverwriteOutput, mouseWheelZoomModifierKey, setMouseWheelZoomModifierKey, captureFrameMethod, setCaptureFrameMethod, captureFrameQuality, setCaptureFrameQuality, captureFrameFileNameFormat, setCaptureFrameFileNameFormat, enableNativeHevc, setEnableNativeHevc, enableUpdateCheck, setEnableUpdateCheck, allowMultipleInstances, setAllowMultipleInstances } = 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, enableOverwriteOutput, setEnableOverwriteOutput, mouseWheelZoomModifierKey, setMouseWheelZoomModifierKey, captureFrameMethod, setCaptureFrameMethod, captureFrameQuality, setCaptureFrameQuality, captureFrameFileNameFormat, setCaptureFrameFileNameFormat, enableNativeHevc, setEnableNativeHevc, enableUpdateCheck, setEnableUpdateCheck, allowMultipleInstances, setAllowMultipleInstances, preferStrongColors, setPreferStrongColors } = useUserSettings();
const onLangChange = useCallback((e) => {
const { value } = e.target;
@ -311,6 +311,15 @@ const Settings = memo(({
</td>
</Row>
<Header title={t('User interface')} />
<Row>
<KeyCell>{t('Prefer strong colors')}</KeyCell>
<td>
<Switch checked={preferStrongColors} onCheckedChange={setPreferStrongColors} />
</td>
</Row>
<Header title={t('Advanced options')} />
{!isMasBuild && (

6
src/contexts.js Normal file
View File

@ -0,0 +1,6 @@
import React, { useContext } from 'react';
export const UserSettingsContext = React.createContext();
export const SegColorsContext = React.createContext();
export const useSegColors = () => useContext(SegColorsContext);

View File

@ -1,3 +0,0 @@
import React from 'react';
export default React.createContext();

View File

@ -1,5 +1,5 @@
import { useContext } from 'react';
import UserSettingsContext from '../contexts/UserSettingsContext';
import { UserSettingsContext } from '../contexts';
export default () => useContext(UserSettingsContext);

View File

@ -135,6 +135,8 @@ export default () => {
useEffect(() => safeSetConfig({ allowMultipleInstances }), [allowMultipleInstances]);
const [darkMode, setDarkMode] = useState(safeGetConfigInitial('darkMode'));
useEffect(() => safeSetConfig({ darkMode }), [darkMode]);
const [preferStrongColors, setPreferStrongColors] = useState(safeGetConfigInitial('preferStrongColors'));
useEffect(() => safeSetConfig({ preferStrongColors }), [preferStrongColors]);
const resetKeyBindings = useCallback(() => {
@ -248,5 +250,7 @@ export default () => {
setAllowMultipleInstances,
darkMode,
setDarkMode,
preferStrongColors,
setPreferStrongColors,
};
};