Skip to content

Commit

Permalink
remove isFreeForm and fix clip segment positioning
Browse files Browse the repository at this point in the history
  • Loading branch information
Brendonovich committed Feb 13, 2025
1 parent 979b186 commit fbb2016
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 159 deletions.
317 changes: 163 additions & 154 deletions apps/desktop/src/routes/editor/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Setter,
Show,
batch,
createEffect,
createRoot,
createSignal,
on,
Expand Down Expand Up @@ -300,101 +301,111 @@ function ClipTrack(props: Pick<ComponentProps<"div">, "ref">) {
return (
<TrackRoot ref={props.ref}>
<For each={segments()}>
{(segment, i) => (
<SegmentRoot
class="border-blue-300"
innerClass="ring-blue-300"
segment={segment}
onMouseDown={(e) => {
if (!split()) return;
e.stopPropagation();
{(segment, i) => {
const prevDuration = () =>
segments()
.slice(0, i())
.reduce((t, s) => t + (s.end - s.start) / s.timescale, 0);

const rect = e.currentTarget.getBoundingClientRect();
const fraction = (e.clientX - rect.left) / rect.width;

const splitTime =
segment.start + fraction * (segment.end - segment.start);

setProject(
"timeline",
"segments",
produce((segments) => {
segments.splice(i() + 1, 0, {
start: splitTime,
end: segment.end,
timescale: 1,
recordingSegment: segment.recordingSegment,
});
segments[i()].end = splitTime;
})
);
}}
>
<SegmentHandle
class="bg-blue-300"
onMouseDown={(downEvent) => {
const start = segment.start;

const maxSegmentDuration =
editorInstance.recordings.segments[
segment.recordingSegment ?? 0
].display.duration;

const availableTimelineDuration =
editorInstance.recordingDuration -
segments().reduce(
(acc, segment, segmentI) =>
segmentI === i()
? acc
: acc +
(segment.end - segment.start) / segment.timescale,
0
);
return (
<SegmentRoot
class="border-blue-300"
innerClass="ring-blue-300"
segment={{
...segment,
start: prevDuration(),
end: segment.end - segment.start + prevDuration(),
}}
onMouseDown={(e) => {
if (!split()) return;
e.stopPropagation();

const maxDuration = Math.min(
maxSegmentDuration,
availableTimelineDuration
const rect = e.currentTarget.getBoundingClientRect();
const fraction = (e.clientX - rect.left) / rect.width;

const splitTime =
segment.start + fraction * (segment.end - segment.start);

setProject(
"timeline",
"segments",
produce((segments) => {
segments.splice(i() + 1, 0, {
start: splitTime,
end: segment.end,
timescale: 1,
recordingSegment: segment.recordingSegment,
});
segments[i()].end = splitTime;
})
);
}}
>
<SegmentHandle
class="bg-blue-300"
onMouseDown={(downEvent) => {
const start = segment.start;

const maxSegmentDuration =
editorInstance.recordings.segments[
segment.recordingSegment ?? 0
].display.duration;

const availableTimelineDuration =
editorInstance.recordingDuration -
segments().reduce(
(acc, segment, segmentI) =>
segmentI === i()
? acc
: acc +
(segment.end - segment.start) / segment.timescale,
0
);

function update(event: MouseEvent) {
const newStart =
start +
(event.clientX - downEvent.clientX) * secsPerPixel();

setProject(
"timeline",
"segments",
i(),
"start",
Math.min(
Math.max(
Math.max(newStart, 0),
segment.end - maxDuration
),
segment.end - 1
)
const maxDuration = Math.min(
maxSegmentDuration,
availableTimelineDuration
);
}

const resumeHistory = history.pause();
createRoot((dispose) => {
createEventListenerMap(window, {
mousemove: update,
mouseup: (e) => {
dispose();
resumeHistory();
update(e);
onHandleReleased();
},
function update(event: MouseEvent) {
const newStart =
start +
(event.clientX - downEvent.clientX) * secsPerPixel();

setProject(
"timeline",
"segments",
i(),
"start",
Math.min(
Math.max(
Math.max(newStart, 0),
segment.end - maxDuration
),
segment.end - 1
)
);
}

const resumeHistory = history.pause();
createRoot((dispose) => {
createEventListenerMap(window, {
mousemove: update,
mouseup: (e) => {
dispose();
resumeHistory();
update(e);
onHandleReleased();
},
});
});
});
}}
/>
<SegmentContent class="bg-blue-50 justify-between">
<span class="text-black-transparent-60 text-[0.625rem] mt-auto">
{formatTime(segment.start)}
</span>
{/* <Show when={segments().length > 1}>
}}
/>
<SegmentContent class="bg-blue-50 justify-between">
<span class="text-black-transparent-60 text-[0.625rem] mt-auto">
{formatTime(segment.start)}
</span>
{/* <Show when={segments().length > 1}>
<button
onClick={() => {
setProject(
Expand All @@ -410,67 +421,69 @@ function ClipTrack(props: Pick<ComponentProps<"div">, "ref">) {
<IconCapTrash class="size-4 text-gray-400 group-hover/button:text-gray-500 transition-colors" />
</button>
</Show> */}
<span class="text-black-transparent-60 text-[0.625rem] mt-auto">
{formatTime(segment.end)}
</span>
</SegmentContent>
<SegmentHandle
class="bg-blue-300"
onMouseDown={(downEvent) => {
const end = segment.end;

const maxSegmentDuration =
editorInstance.recordings.segments[
segment.recordingSegment ?? 0
].display.duration;

const availableTimelineDuration =
editorInstance.recordingDuration -
segments().reduce(
(acc, segment, segmentI) =>
segmentI === i()
? acc
: acc +
(segment.end - segment.start) / segment.timescale,
0
);
<span class="text-black-transparent-60 text-[0.625rem] mt-auto">
{formatTime(segment.end)}
</span>
</SegmentContent>
<SegmentHandle
class="bg-blue-300"
onMouseDown={(downEvent) => {
const end = segment.end;

const maxSegmentDuration =
editorInstance.recordings.segments[
segment.recordingSegment ?? 0
].display.duration;

const availableTimelineDuration =
editorInstance.recordingDuration -
segments().reduce(
(acc, segment, segmentI) =>
segmentI === i()
? acc
: acc +
(segment.end - segment.start) / segment.timescale,
0
);

function update(event: MouseEvent) {
const newEnd =
end + (event.clientX - downEvent.clientX) * secsPerPixel();

setProject(
"timeline",
"segments",
i(),
"end",
Math.max(
Math.min(
newEnd,
maxSegmentDuration,
availableTimelineDuration
),
segment.start + 1
)
);
}
function update(event: MouseEvent) {
const newEnd =
end +
(event.clientX - downEvent.clientX) * secsPerPixel();

const resumeHistory = history.pause();
createRoot((dispose) => {
createEventListenerMap(window, {
mousemove: update,
mouseup: (e) => {
dispose();
resumeHistory();
update(e);
onHandleReleased();
},
setProject(
"timeline",
"segments",
i(),
"end",
Math.max(
Math.min(
newEnd,
maxSegmentDuration,
availableTimelineDuration
),
segment.start + 1
)
);
}

const resumeHistory = history.pause();
createRoot((dispose) => {
createEventListenerMap(window, {
mousemove: update,
mouseup: (e) => {
dispose();
resumeHistory();
update(e);
onHandleReleased();
},
});
});
});
}}
/>
</SegmentRoot>
)}
}}
/>
</SegmentRoot>
);
}}
</For>
</TrackRoot>
);
Expand All @@ -492,7 +505,6 @@ function ZoomTrack(props: {
<div class="h-2 w-full" />

<TrackRoot
isFreeForm
onMouseMove={(e) => {
if (hoveringSegment()) {
setHoveredTime(undefined);
Expand Down Expand Up @@ -823,14 +835,11 @@ function ZoomTrack(props: {
);
}

function TrackRoot(props: ComponentProps<"div"> & { isFreeForm?: boolean }) {
function TrackRoot(props: ComponentProps<"div">) {
const [ref, setRef] = createSignal<HTMLDivElement>();

return (
<TrackContextProvider
ref={ref}
isFreeForm={() => props.isFreeForm ?? false}
>
<TrackContextProvider ref={ref}>
<div
{...props}
ref={mergeRefs(setRef, props.ref)}
Expand All @@ -851,7 +860,7 @@ function SegmentRoot(
) => void;
}
) {
const { isFreeForm, secsPerPixel } = useTrackContext();
const { secsPerPixel } = useTrackContext();
const { state, project } = useEditorContext();

const isSelected = createMemo(() => {
Expand All @@ -868,7 +877,7 @@ function SegmentRoot(
const translateX = createMemo(() => {
const base = state.timelineTransform.position;

const delta = !isFreeForm() ? 0 : props.segment.start;
const delta = props.segment.start;

return (delta - base) / secsPerPixel();
});
Expand Down
6 changes: 1 addition & 5 deletions apps/desktop/src/routes/editor/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,7 @@ export const [TimelineContextProvider, useTimelineContext] =
);

export const [TrackContextProvider, useTrackContext] = createContextProvider(
(props: {
ref: Accessor<Element | undefined>;
isFreeForm: Accessor<boolean>;
}) => {
(props: { ref: Accessor<Element | undefined> }) => {
const { state } = useEditorContext();

const [trackState, setTrackState] = createStore({
Expand All @@ -320,7 +317,6 @@ export const [TrackContextProvider, useTrackContext] = createContextProvider(
return {
secsPerPixel,
trackBounds: bounds,
isFreeForm: () => props.isFreeForm(),
trackState,
setTrackState,
};
Expand Down

1 comment on commit fbb2016

@vercel
Copy link

@vercel vercel bot commented on fbb2016 Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.