Skip to content

Commit 0d933a7

Browse files
Fix trimming behavior (#1273)
* Basic visual feedback working * Added trimming feedback * fixing copilot error * fixing change from code rabbit * fixing change from code rabbit * fix: clean up code formatting and remove unused imports * fixed code rabbit review * Final improvements - Conslidated drag states into one 'startHandleDrag' signal, focusing on deriving states like isDragging instead of using independent signals. - Limited offset to >=0, so dragging to the left uses previous behaviour --------- Co-authored-by: Brendan Allan <brendonovich@outlook.com>
1 parent 8d76a11 commit 0d933a7

File tree

1 file changed

+90
-33
lines changed

1 file changed

+90
-33
lines changed

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

Lines changed: 90 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import {
88
createEffect,
99
createMemo,
1010
createRoot,
11+
createSignal,
1112
For,
1213
Match,
13-
mergeProps,
14+
onCleanup,
1415
Show,
1516
Switch,
1617
} from "solid-js";
@@ -204,18 +205,40 @@ export function ClipTrack(
204205
>
205206
<For each={segments()}>
206207
{(segment, i) => {
207-
const prevDuration = () =>
208-
segments()
209-
.slice(0, i())
210-
.reduce((t, s) => t + (s.end - s.start) / s.timescale, 0);
211-
212-
const relativeSegment = mergeProps(segment, () => ({
213-
start: prevDuration(),
214-
end: segment.end - segment.start + prevDuration(),
215-
}));
208+
const [startHandleDrag, setStartHandleDrag] = createSignal<null | {
209+
offset: number;
210+
initialStart: number;
211+
}>(null);
212+
213+
const prefixOffsets = createMemo(() => {
214+
const segs = segments();
215+
const out: number[] = new Array(segs.length);
216+
let sum = 0;
217+
for (let k = 0; k < segs.length; k++) {
218+
out[k] = sum;
219+
sum += (segs[k].end - segs[k].start) / segs[k].timescale;
220+
}
221+
return out;
222+
});
223+
const prevDuration = createMemo(() => prefixOffsets()[i()] ?? 0);
224+
225+
const relativeSegment = createMemo(() => {
226+
const ds = startHandleDrag();
227+
const offset = ds ? ds.offset / segment.timescale : 0;
228+
229+
return {
230+
start: Math.max(prevDuration() + offset, 0),
231+
end:
232+
prevDuration() +
233+
offset +
234+
(segment.end - segment.start) / segment.timescale,
235+
timescale: segment.timescale,
236+
recordingSegment: segment.recordingSegment,
237+
};
238+
});
216239

217-
const segmentX = useSegmentTranslateX(() => relativeSegment);
218-
const segmentWidth = useSegmentWidth(() => relativeSegment);
240+
const segmentX = useSegmentTranslateX(relativeSegment);
241+
const segmentWidth = useSegmentWidth(relativeSegment);
219242

220243
const segmentRecording = (s = i()) =>
221244
editorInstance.recordings.segments[
@@ -360,7 +383,7 @@ export function ClipTrack(
360383
: "border-transparent",
361384
)}
362385
innerClass="ring-blue-9"
363-
segment={relativeSegment}
386+
segment={relativeSegment()}
364387
onMouseDown={(e) => {
365388
e.stopPropagation();
366389

@@ -465,9 +488,14 @@ export function ClipTrack(
465488
position="start"
466489
class="opacity-0 group-hover:opacity-100"
467490
onMouseDown={(downEvent) => {
468-
const start = segment.start;
469-
470491
if (split()) return;
492+
493+
const initialStart = segment.start;
494+
setStartHandleDrag({
495+
offset: 0,
496+
initialStart,
497+
});
498+
471499
const maxSegmentDuration =
472500
editorInstance.recordings.segments[
473501
segment.recordingSegment ?? 0
@@ -498,35 +526,51 @@ export function ClipTrack(
498526

499527
function update(event: MouseEvent) {
500528
const newStart =
501-
start +
502-
(event.clientX - downEvent.clientX) * secsPerPixel();
529+
initialStart +
530+
(event.clientX - downEvent.clientX) *
531+
secsPerPixel() *
532+
segment.timescale;
533+
534+
const clampedStart = Math.min(
535+
Math.max(
536+
newStart,
537+
prevSegmentIsSameClip ? prevSegment.end : 0,
538+
segment.end - maxDuration,
539+
),
540+
segment.end - 1,
541+
);
542+
543+
setStartHandleDrag({
544+
offset: clampedStart - initialStart,
545+
initialStart,
546+
});
503547

504548
setProject(
505549
"timeline",
506550
"segments",
507551
i(),
508552
"start",
509-
Math.min(
510-
Math.max(
511-
newStart,
512-
prevSegmentIsSameClip ? prevSegment.end : 0,
513-
segment.end - maxDuration,
514-
),
515-
segment.end - 1,
516-
),
553+
clampedStart,
517554
);
518555
}
519556

520557
const resumeHistory = projectHistory.pause();
521558
createRoot((dispose) => {
559+
onCleanup(() => {
560+
resumeHistory();
561+
console.log("NUL");
562+
setStartHandleDrag(null);
563+
onHandleReleased();
564+
});
565+
522566
createEventListenerMap(window, {
523567
mousemove: update,
524568
mouseup: (e) => {
525-
dispose();
526-
resumeHistory();
527569
update(e);
528-
onHandleReleased();
570+
dispose();
529571
},
572+
blur: () => dispose(),
573+
mouseleave: () => dispose(),
530574
});
531575
});
532576
}}
@@ -583,9 +627,11 @@ export function ClipTrack(
583627
: false;
584628

585629
function update(event: MouseEvent) {
586-
const newEnd =
587-
end +
588-
(event.clientX - downEvent.clientX) * secsPerPixel();
630+
const deltaRecorded =
631+
(event.clientX - downEvent.clientX) *
632+
secsPerPixel() *
633+
segment.timescale;
634+
const newEnd = end + deltaRecorded;
589635

590636
setProject(
591637
"timeline",
@@ -595,7 +641,8 @@ export function ClipTrack(
595641
Math.max(
596642
Math.min(
597643
newEnd,
598-
segment.end + availableTimelineDuration,
644+
// availableTimelineDuration is in timeline seconds; convert to recorded seconds
645+
end + availableTimelineDuration * segment.timescale,
599646
nextSegmentIsSameClip
600647
? nextSegment.start
601648
: maxSegmentDuration,
@@ -615,6 +662,16 @@ export function ClipTrack(
615662
update(e);
616663
onHandleReleased();
617664
},
665+
blur: () => {
666+
dispose();
667+
resumeHistory();
668+
onHandleReleased();
669+
},
670+
mouseleave: () => {
671+
dispose();
672+
resumeHistory();
673+
onHandleReleased();
674+
},
618675
});
619676
});
620677
}}
@@ -716,7 +773,7 @@ function CutOffsetButton(props: {
716773
{props.value === 0 ? (
717774
<IconCapScissors class="size-3.5" />
718775
) : (
719-
<>{formatTime(props.value)}</>
776+
formatTime(props.value)
720777
)}
721778
</button>
722779
);

0 commit comments

Comments
 (0)