-
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #459 from charlielee/issue-419
Option to switch status bar time between frames and seconds
- Loading branch information
Showing
10 changed files
with
192 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
export interface UserPreferences { | ||
playCaptureSound: boolean; | ||
shortPlayLength: number; | ||
showTimestampInSeconds: boolean; | ||
workingDirectory: string | undefined; | ||
} | ||
|
||
export const defaultUserPreferences: UserPreferences = { | ||
playCaptureSound: true, | ||
shortPlayLength: 6, | ||
showTimestampInSeconds: false, | ||
workingDirectory: undefined, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { buildStartTimeCode, secondsToTimeCode } from "./timeUtils"; | ||
|
||
describe("secondsToTimeCode", () => { | ||
it("should convert to correct time codes when show decimal is true", () => { | ||
expect(secondsToTimeCode(0, true)).toBe("00:00.000"); | ||
expect(secondsToTimeCode(0.01, true)).toBe("00:00.010"); | ||
expect(secondsToTimeCode(0.999, true)).toBe("00:00.999"); | ||
expect(secondsToTimeCode(0.9999, true)).toBe("00:01.000"); | ||
expect(secondsToTimeCode(1, true)).toBe("00:01.000"); | ||
expect(secondsToTimeCode(1.1, true)).toBe("00:01.100"); | ||
expect(secondsToTimeCode(1.6664, true)).toBe("00:01.666"); | ||
expect(secondsToTimeCode(1.6665, true)).toBe("00:01.667"); | ||
expect(secondsToTimeCode(59, true)).toBe("00:59.000"); | ||
expect(secondsToTimeCode(60, true)).toBe("01:00.000"); | ||
expect(secondsToTimeCode(61, true)).toBe("01:01.000"); | ||
expect(secondsToTimeCode(600, true)).toBe("10:00.000"); | ||
expect(secondsToTimeCode(601, true)).toBe("10:01.000"); | ||
expect(secondsToTimeCode(601.789, true)).toBe("10:01.789"); | ||
expect(secondsToTimeCode(5999, true)).toBe("99:59.000"); | ||
expect(secondsToTimeCode(6000, true)).toBe("100:00.000"); | ||
}); | ||
|
||
it("should convert to correct time codes when show decimal is false", () => { | ||
expect(secondsToTimeCode(0, false)).toBe("00:00"); | ||
expect(secondsToTimeCode(0.01, false)).toBe("00:00"); | ||
expect(secondsToTimeCode(0.999, false)).toBe("00:01"); | ||
expect(secondsToTimeCode(0.9999, false)).toBe("00:01"); | ||
expect(secondsToTimeCode(1, false)).toBe("00:01"); | ||
expect(secondsToTimeCode(1.1, false)).toBe("00:01"); | ||
expect(secondsToTimeCode(1.6664, false)).toBe("00:02"); | ||
expect(secondsToTimeCode(1.6665, false)).toBe("00:02"); | ||
expect(secondsToTimeCode(59, false)).toBe("00:59"); | ||
expect(secondsToTimeCode(60, false)).toBe("01:00"); | ||
expect(secondsToTimeCode(61, false)).toBe("01:01"); | ||
expect(secondsToTimeCode(600, false)).toBe("10:00"); | ||
expect(secondsToTimeCode(601, false)).toBe("10:01"); | ||
expect(secondsToTimeCode(601.789, false)).toBe("10:02"); | ||
expect(secondsToTimeCode(5999, false)).toBe("99:59"); | ||
expect(secondsToTimeCode(6000, false)).toBe("100:00"); | ||
}); | ||
|
||
it("should convert to time code with decimal by default", () => { | ||
expect(secondsToTimeCode(60)).toBe("01:00.000"); | ||
}); | ||
}); | ||
|
||
describe("buildStartTimeCode", () => { | ||
it("should build correct time codes for frame positions", () => { | ||
// 15 FPS | ||
expect(buildStartTimeCode(0, 15, true)).toBe("00:00.000"); | ||
expect(buildStartTimeCode(1, 15, true)).toBe("00:00.067"); | ||
expect(buildStartTimeCode(14, 15, true)).toBe("00:00.933"); | ||
expect(buildStartTimeCode(15, 15, true)).toBe("00:01.000"); | ||
expect(buildStartTimeCode(16, 15, true)).toBe("00:01.067"); | ||
expect(buildStartTimeCode(999, 15, true)).toBe("01:06.600"); | ||
expect(buildStartTimeCode(9999, 15, true)).toBe("11:06.600"); | ||
// 12 FPS | ||
expect(buildStartTimeCode(0, 12, true)).toBe("00:00.000"); | ||
expect(buildStartTimeCode(1, 12, true)).toBe("00:00.083"); | ||
expect(buildStartTimeCode(11, 12, true)).toBe("00:00.917"); | ||
expect(buildStartTimeCode(12, 12, true)).toBe("00:01.000"); | ||
expect(buildStartTimeCode(13, 12, true)).toBe("00:01.083"); | ||
expect(buildStartTimeCode(999, 12, true)).toBe("01:23.250"); | ||
expect(buildStartTimeCode(9999, 12, true)).toBe("13:53.250"); | ||
}); | ||
|
||
it("should build time code for frame position when showDecimal is false", () => { | ||
expect(buildStartTimeCode(16, 15, false)).toBe("00:01"); | ||
}); | ||
|
||
it("should build time code for frame position with decimal by default", () => { | ||
expect(buildStartTimeCode(16, 15)).toBe("00:01.067"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { FrameRate, TimelineIndex } from "./Flavors"; | ||
import { zeroPad } from "./utils"; | ||
|
||
export const secondsToTimeCode = (seconds: number, showDecimal = true) => { | ||
const roundedSeconds = seconds.toFixed(showDecimal ? 3 : 0); | ||
const [fullSeconds, millisecondComponent] = roundedSeconds.split("."); | ||
|
||
const minuteComponent = Math.floor(parseInt(fullSeconds, 10) / 60); | ||
const secondComponent = parseInt(fullSeconds, 10) % 60; | ||
const paddedMinutesAndSeconds = `${zeroPad(minuteComponent, 2)}:${zeroPad(secondComponent, 2)}`; | ||
|
||
return showDecimal | ||
? `${paddedMinutesAndSeconds}.${millisecondComponent}` | ||
: paddedMinutesAndSeconds; | ||
}; | ||
|
||
export const buildStartTimeCode = ( | ||
framePosition: TimelineIndex, | ||
frameRate: FrameRate, | ||
showDecimal?: boolean | ||
) => { | ||
const timeInSeconds = framePosition / frameRate; | ||
return secondsToTimeCode(timeInSeconds, showDecimal); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 5 additions & 21 deletions
26
src/renderer/components/animator/StatusToolbar/StatusToolbar.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,19 @@ | ||
import { TimelineIndex } from "../../../../common/Flavors"; | ||
import { Take } from "../../../../common/project/Take"; | ||
import PlaybackContext from "../../../context/PlaybackContext/PlaybackContext"; | ||
import { getTrackLength } from "../../../services/project/projectCalculator"; | ||
import Toolbar from "../../common/Toolbar/Toolbar"; | ||
import ToolbarItem, { ToolbarItemAlign } from "../../common/ToolbarItem/ToolbarItem"; | ||
import "./StatusToolbar.css"; | ||
import StatusToolbarTimestampWithContext from "./StatusToolbarTimestamp/StatusToolbarTimestamp"; | ||
|
||
interface StatusToolbarWithContextProps { | ||
interface StatusToolbarProps { | ||
take: Take; | ||
} | ||
|
||
interface StatusToolbarProps extends StatusToolbarWithContextProps { | ||
timelineIndex: TimelineIndex | undefined; | ||
} | ||
|
||
const StatusToolbar = ({ take, timelineIndex }: StatusToolbarProps): JSX.Element => ( | ||
const StatusToolbar = ({ take }: StatusToolbarProps): JSX.Element => ( | ||
<Toolbar className="status-toolbar"> | ||
<ToolbarItem align={ToolbarItemAlign.CENTER}> | ||
<div> | ||
Frame{" "} | ||
{timelineIndex === undefined ? getTrackLength(take.frameTrack) + 1 : timelineIndex + 1} of{" "} | ||
{getTrackLength(take.frameTrack)} | ||
</div> | ||
<StatusToolbarTimestampWithContext take={take} /> | ||
</ToolbarItem> | ||
</Toolbar> | ||
); | ||
|
||
const StatusToolbarWithContext = (props: StatusToolbarWithContextProps) => ( | ||
<PlaybackContext.Consumer> | ||
{(value) => <StatusToolbar timelineIndex={value.timelineIndex} {...props} />} | ||
</PlaybackContext.Consumer> | ||
); | ||
|
||
export default StatusToolbarWithContext; | ||
export default StatusToolbar; |
3 changes: 3 additions & 0 deletions
3
...derer/components/animator/StatusToolbar/StatusToolbarTimestamp/StatusToolbarTimestamp.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.status-toolbar-timestamp { | ||
font-family: monospace; | ||
} |
58 changes: 58 additions & 0 deletions
58
...derer/components/animator/StatusToolbar/StatusToolbarTimestamp/StatusToolbarTimestamp.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { useState } from "react"; | ||
import { TimelineIndex } from "../../../../../common/Flavors"; | ||
import { Take } from "../../../../../common/project/Take"; | ||
import PlaybackContext from "../../../../context/PlaybackContext/PlaybackContext"; | ||
import { getTrackLength } from "../../../../services/project/projectCalculator"; | ||
import Button from "../../../common/Button/Button"; | ||
import { ButtonColor } from "../../../common/Button/ButtonColor"; | ||
import { buildStartTimeCode } from "../../../../../common/timeUtils"; | ||
import "./StatusToolbarTimestamp.css"; | ||
import { useSelector } from "react-redux"; | ||
import { RootState } from "../../../../redux/store"; | ||
|
||
interface StatusToolbarTimestampWithContextProps { | ||
take: Take; | ||
} | ||
|
||
interface StatusToolbarTimestampProps extends StatusToolbarTimestampWithContextProps { | ||
timelineIndex: TimelineIndex | undefined; | ||
} | ||
|
||
const StatusToolbarTimestamp = ({ | ||
take, | ||
timelineIndex, | ||
}: StatusToolbarTimestampProps): JSX.Element => { | ||
const { showTimestampInSeconds } = useSelector((state: RootState) => state.app.userPreferences); | ||
const [showInSeconds, setShowInSeconds] = useState(showTimestampInSeconds); | ||
|
||
const trackLength = getTrackLength(take.frameTrack); | ||
const secondsText = [ | ||
buildStartTimeCode(timelineIndex ?? (trackLength as TimelineIndex), take.frameRate), | ||
"/", | ||
buildStartTimeCode(trackLength as TimelineIndex, take.frameRate), | ||
].join(" "); | ||
const frameText = [ | ||
"Frame", | ||
timelineIndex === undefined ? trackLength + 1 : timelineIndex + 1, | ||
"of", | ||
trackLength, | ||
].join(" "); | ||
|
||
return ( | ||
<Button | ||
className="status-toolbar-timestamp" | ||
label={showInSeconds ? secondsText : frameText} | ||
title={showInSeconds ? "Show timestamp in frames" : "Show timestamp in seconds"} | ||
onClick={() => setShowInSeconds((prevState) => !prevState)} | ||
color={ButtonColor.TRANSPARENT} | ||
/> | ||
); | ||
}; | ||
|
||
const StatusToolbarTimestampWithContext = (props: StatusToolbarTimestampWithContextProps) => ( | ||
<PlaybackContext.Consumer> | ||
{(value) => <StatusToolbarTimestamp timelineIndex={value.timelineIndex} {...props} />} | ||
</PlaybackContext.Consumer> | ||
); | ||
|
||
export default StatusToolbarTimestampWithContext; |
18 changes: 7 additions & 11 deletions
18
src/renderer/components/animator/Timeline/TimelinePosition/TimelinePosition.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters