Skip to content

Commit fbb2016

Browse files
committed
remove isFreeForm and fix clip segment positioning
1 parent 979b186 commit fbb2016

File tree

2 files changed

+164
-159
lines changed

2 files changed

+164
-159
lines changed

apps/desktop/src/routes/editor/Timeline.tsx

Lines changed: 163 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Setter,
88
Show,
99
batch,
10+
createEffect,
1011
createRoot,
1112
createSignal,
1213
on,
@@ -300,101 +301,111 @@ function ClipTrack(props: Pick<ComponentProps<"div">, "ref">) {
300301
return (
301302
<TrackRoot ref={props.ref}>
302303
<For each={segments()}>
303-
{(segment, i) => (
304-
<SegmentRoot
305-
class="border-blue-300"
306-
innerClass="ring-blue-300"
307-
segment={segment}
308-
onMouseDown={(e) => {
309-
if (!split()) return;
310-
e.stopPropagation();
304+
{(segment, i) => {
305+
const prevDuration = () =>
306+
segments()
307+
.slice(0, i())
308+
.reduce((t, s) => t + (s.end - s.start) / s.timescale, 0);
311309

312-
const rect = e.currentTarget.getBoundingClientRect();
313-
const fraction = (e.clientX - rect.left) / rect.width;
314-
315-
const splitTime =
316-
segment.start + fraction * (segment.end - segment.start);
317-
318-
setProject(
319-
"timeline",
320-
"segments",
321-
produce((segments) => {
322-
segments.splice(i() + 1, 0, {
323-
start: splitTime,
324-
end: segment.end,
325-
timescale: 1,
326-
recordingSegment: segment.recordingSegment,
327-
});
328-
segments[i()].end = splitTime;
329-
})
330-
);
331-
}}
332-
>
333-
<SegmentHandle
334-
class="bg-blue-300"
335-
onMouseDown={(downEvent) => {
336-
const start = segment.start;
337-
338-
const maxSegmentDuration =
339-
editorInstance.recordings.segments[
340-
segment.recordingSegment ?? 0
341-
].display.duration;
342-
343-
const availableTimelineDuration =
344-
editorInstance.recordingDuration -
345-
segments().reduce(
346-
(acc, segment, segmentI) =>
347-
segmentI === i()
348-
? acc
349-
: acc +
350-
(segment.end - segment.start) / segment.timescale,
351-
0
352-
);
310+
return (
311+
<SegmentRoot
312+
class="border-blue-300"
313+
innerClass="ring-blue-300"
314+
segment={{
315+
...segment,
316+
start: prevDuration(),
317+
end: segment.end - segment.start + prevDuration(),
318+
}}
319+
onMouseDown={(e) => {
320+
if (!split()) return;
321+
e.stopPropagation();
353322

354-
const maxDuration = Math.min(
355-
maxSegmentDuration,
356-
availableTimelineDuration
323+
const rect = e.currentTarget.getBoundingClientRect();
324+
const fraction = (e.clientX - rect.left) / rect.width;
325+
326+
const splitTime =
327+
segment.start + fraction * (segment.end - segment.start);
328+
329+
setProject(
330+
"timeline",
331+
"segments",
332+
produce((segments) => {
333+
segments.splice(i() + 1, 0, {
334+
start: splitTime,
335+
end: segment.end,
336+
timescale: 1,
337+
recordingSegment: segment.recordingSegment,
338+
});
339+
segments[i()].end = splitTime;
340+
})
357341
);
342+
}}
343+
>
344+
<SegmentHandle
345+
class="bg-blue-300"
346+
onMouseDown={(downEvent) => {
347+
const start = segment.start;
348+
349+
const maxSegmentDuration =
350+
editorInstance.recordings.segments[
351+
segment.recordingSegment ?? 0
352+
].display.duration;
353+
354+
const availableTimelineDuration =
355+
editorInstance.recordingDuration -
356+
segments().reduce(
357+
(acc, segment, segmentI) =>
358+
segmentI === i()
359+
? acc
360+
: acc +
361+
(segment.end - segment.start) / segment.timescale,
362+
0
363+
);
358364

359-
function update(event: MouseEvent) {
360-
const newStart =
361-
start +
362-
(event.clientX - downEvent.clientX) * secsPerPixel();
363-
364-
setProject(
365-
"timeline",
366-
"segments",
367-
i(),
368-
"start",
369-
Math.min(
370-
Math.max(
371-
Math.max(newStart, 0),
372-
segment.end - maxDuration
373-
),
374-
segment.end - 1
375-
)
365+
const maxDuration = Math.min(
366+
maxSegmentDuration,
367+
availableTimelineDuration
376368
);
377-
}
378369

379-
const resumeHistory = history.pause();
380-
createRoot((dispose) => {
381-
createEventListenerMap(window, {
382-
mousemove: update,
383-
mouseup: (e) => {
384-
dispose();
385-
resumeHistory();
386-
update(e);
387-
onHandleReleased();
388-
},
370+
function update(event: MouseEvent) {
371+
const newStart =
372+
start +
373+
(event.clientX - downEvent.clientX) * secsPerPixel();
374+
375+
setProject(
376+
"timeline",
377+
"segments",
378+
i(),
379+
"start",
380+
Math.min(
381+
Math.max(
382+
Math.max(newStart, 0),
383+
segment.end - maxDuration
384+
),
385+
segment.end - 1
386+
)
387+
);
388+
}
389+
390+
const resumeHistory = history.pause();
391+
createRoot((dispose) => {
392+
createEventListenerMap(window, {
393+
mousemove: update,
394+
mouseup: (e) => {
395+
dispose();
396+
resumeHistory();
397+
update(e);
398+
onHandleReleased();
399+
},
400+
});
389401
});
390-
});
391-
}}
392-
/>
393-
<SegmentContent class="bg-blue-50 justify-between">
394-
<span class="text-black-transparent-60 text-[0.625rem] mt-auto">
395-
{formatTime(segment.start)}
396-
</span>
397-
{/* <Show when={segments().length > 1}>
402+
}}
403+
/>
404+
<SegmentContent class="bg-blue-50 justify-between">
405+
<span class="text-black-transparent-60 text-[0.625rem] mt-auto">
406+
{formatTime(segment.start)}
407+
</span>
408+
{/* <Show when={segments().length > 1}>
398409
<button
399410
onClick={() => {
400411
setProject(
@@ -410,67 +421,69 @@ function ClipTrack(props: Pick<ComponentProps<"div">, "ref">) {
410421
<IconCapTrash class="size-4 text-gray-400 group-hover/button:text-gray-500 transition-colors" />
411422
</button>
412423
</Show> */}
413-
<span class="text-black-transparent-60 text-[0.625rem] mt-auto">
414-
{formatTime(segment.end)}
415-
</span>
416-
</SegmentContent>
417-
<SegmentHandle
418-
class="bg-blue-300"
419-
onMouseDown={(downEvent) => {
420-
const end = segment.end;
421-
422-
const maxSegmentDuration =
423-
editorInstance.recordings.segments[
424-
segment.recordingSegment ?? 0
425-
].display.duration;
426-
427-
const availableTimelineDuration =
428-
editorInstance.recordingDuration -
429-
segments().reduce(
430-
(acc, segment, segmentI) =>
431-
segmentI === i()
432-
? acc
433-
: acc +
434-
(segment.end - segment.start) / segment.timescale,
435-
0
436-
);
424+
<span class="text-black-transparent-60 text-[0.625rem] mt-auto">
425+
{formatTime(segment.end)}
426+
</span>
427+
</SegmentContent>
428+
<SegmentHandle
429+
class="bg-blue-300"
430+
onMouseDown={(downEvent) => {
431+
const end = segment.end;
432+
433+
const maxSegmentDuration =
434+
editorInstance.recordings.segments[
435+
segment.recordingSegment ?? 0
436+
].display.duration;
437+
438+
const availableTimelineDuration =
439+
editorInstance.recordingDuration -
440+
segments().reduce(
441+
(acc, segment, segmentI) =>
442+
segmentI === i()
443+
? acc
444+
: acc +
445+
(segment.end - segment.start) / segment.timescale,
446+
0
447+
);
437448

438-
function update(event: MouseEvent) {
439-
const newEnd =
440-
end + (event.clientX - downEvent.clientX) * secsPerPixel();
441-
442-
setProject(
443-
"timeline",
444-
"segments",
445-
i(),
446-
"end",
447-
Math.max(
448-
Math.min(
449-
newEnd,
450-
maxSegmentDuration,
451-
availableTimelineDuration
452-
),
453-
segment.start + 1
454-
)
455-
);
456-
}
449+
function update(event: MouseEvent) {
450+
const newEnd =
451+
end +
452+
(event.clientX - downEvent.clientX) * secsPerPixel();
457453

458-
const resumeHistory = history.pause();
459-
createRoot((dispose) => {
460-
createEventListenerMap(window, {
461-
mousemove: update,
462-
mouseup: (e) => {
463-
dispose();
464-
resumeHistory();
465-
update(e);
466-
onHandleReleased();
467-
},
454+
setProject(
455+
"timeline",
456+
"segments",
457+
i(),
458+
"end",
459+
Math.max(
460+
Math.min(
461+
newEnd,
462+
maxSegmentDuration,
463+
availableTimelineDuration
464+
),
465+
segment.start + 1
466+
)
467+
);
468+
}
469+
470+
const resumeHistory = history.pause();
471+
createRoot((dispose) => {
472+
createEventListenerMap(window, {
473+
mousemove: update,
474+
mouseup: (e) => {
475+
dispose();
476+
resumeHistory();
477+
update(e);
478+
onHandleReleased();
479+
},
480+
});
468481
});
469-
});
470-
}}
471-
/>
472-
</SegmentRoot>
473-
)}
482+
}}
483+
/>
484+
</SegmentRoot>
485+
);
486+
}}
474487
</For>
475488
</TrackRoot>
476489
);
@@ -492,7 +505,6 @@ function ZoomTrack(props: {
492505
<div class="h-2 w-full" />
493506

494507
<TrackRoot
495-
isFreeForm
496508
onMouseMove={(e) => {
497509
if (hoveringSegment()) {
498510
setHoveredTime(undefined);
@@ -823,14 +835,11 @@ function ZoomTrack(props: {
823835
);
824836
}
825837

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

829841
return (
830-
<TrackContextProvider
831-
ref={ref}
832-
isFreeForm={() => props.isFreeForm ?? false}
833-
>
842+
<TrackContextProvider ref={ref}>
834843
<div
835844
{...props}
836845
ref={mergeRefs(setRef, props.ref)}
@@ -851,7 +860,7 @@ function SegmentRoot(
851860
) => void;
852861
}
853862
) {
854-
const { isFreeForm, secsPerPixel } = useTrackContext();
863+
const { secsPerPixel } = useTrackContext();
855864
const { state, project } = useEditorContext();
856865

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

871-
const delta = !isFreeForm() ? 0 : props.segment.start;
880+
const delta = props.segment.start;
872881

873882
return (delta - base) / secsPerPixel();
874883
});

apps/desktop/src/routes/editor/context.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,7 @@ export const [TimelineContextProvider, useTimelineContext] =
303303
);
304304

305305
export const [TrackContextProvider, useTrackContext] = createContextProvider(
306-
(props: {
307-
ref: Accessor<Element | undefined>;
308-
isFreeForm: Accessor<boolean>;
309-
}) => {
306+
(props: { ref: Accessor<Element | undefined> }) => {
310307
const { state } = useEditorContext();
311308

312309
const [trackState, setTrackState] = createStore({
@@ -320,7 +317,6 @@ export const [TrackContextProvider, useTrackContext] = createContextProvider(
320317
return {
321318
secsPerPixel,
322319
trackBounds: bounds,
323-
isFreeForm: () => props.isFreeForm(),
324320
trackState,
325321
setTrackState,
326322
};

0 commit comments

Comments
 (0)