mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
Move timeline and preview gestures into dedicated controllers so hooks stay thin around editor state and DOM events. This also routes drag and playback synchronization through manager APIs and reuses persistent wasm surfaces so scrubbing, seeking, and preview rendering stay in sync. Made-with: Cursor
85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import { useEffect, useReducer, useRef } from "react";
|
|
import { usePreviewViewport } from "@/preview/components/preview-viewport";
|
|
import type { OnSnapLinesChange } from "@/preview/hooks/use-preview-interaction";
|
|
import { useEditor } from "@/editor/use-editor";
|
|
import { useShiftKey } from "@/hooks/use-shift-key";
|
|
import { registerCanceller } from "@/editor/cancel-interaction";
|
|
import {
|
|
TransformHandleController,
|
|
type TransformHandleDeps,
|
|
} from "@/preview/controllers/transform-handle-controller";
|
|
|
|
export function useTransformHandles({
|
|
onSnapLinesChange,
|
|
}: {
|
|
onSnapLinesChange?: OnSnapLinesChange;
|
|
}) {
|
|
const viewport = usePreviewViewport();
|
|
const editor = useEditor();
|
|
const isShiftHeldRef = useShiftKey();
|
|
const selectedElements = useEditor((e) => e.selection.getSelectedElements());
|
|
const tracks = useEditor(
|
|
(e) => e.timeline.getPreviewTracks() ?? e.scenes.getActiveScene().tracks,
|
|
);
|
|
const currentTime = useEditor((e) => e.playback.getCurrentTime());
|
|
const mediaAssets = useEditor((e) => e.media.getAssets());
|
|
const canvasSize = useEditor(
|
|
(e) => e.project.getActive().settings.canvasSize,
|
|
);
|
|
const deps: TransformHandleDeps = {
|
|
viewport,
|
|
input: {
|
|
isShiftHeld: () => isShiftHeldRef.current,
|
|
},
|
|
scene: {
|
|
getSelectedElements: () => selectedElements,
|
|
getTracks: () => tracks,
|
|
getCurrentTime: () => currentTime,
|
|
getMediaAssets: () => mediaAssets,
|
|
getCanvasSize: () => canvasSize,
|
|
},
|
|
timeline: {
|
|
previewElements: (updates) =>
|
|
editor.timeline.previewElements({ updates }),
|
|
commitPreview: () => editor.timeline.commitPreview(),
|
|
discardPreview: () => editor.timeline.discardPreview(),
|
|
},
|
|
preview: {
|
|
onSnapLinesChange,
|
|
},
|
|
};
|
|
|
|
const depsRef = useRef<TransformHandleDeps>(deps);
|
|
depsRef.current = deps;
|
|
|
|
const controllerRef = useRef<TransformHandleController | null>(null);
|
|
if (!controllerRef.current) {
|
|
controllerRef.current = new TransformHandleController({ depsRef });
|
|
}
|
|
const controller = controllerRef.current;
|
|
|
|
const [, rerender] = useReducer((n: number) => n + 1, 0);
|
|
useEffect(() => controller.subscribe(rerender), [controller]);
|
|
|
|
useEffect(() => {
|
|
if (!controller.isActive) return;
|
|
return registerCanceller({ fn: () => controller.cancel() });
|
|
}, [controller, controller.isActive]);
|
|
|
|
useEffect(() => () => controller.destroy(), [controller]);
|
|
|
|
const selectedWithBounds = controller.selectedWithBounds;
|
|
const hasVisualSelection = selectedWithBounds !== null;
|
|
|
|
return {
|
|
selectedWithBounds,
|
|
hasVisualSelection,
|
|
activeHandle: controller.activeHandle,
|
|
handleCornerPointerDown: controller.onCornerPointerDown,
|
|
handleEdgePointerDown: controller.onEdgePointerDown,
|
|
handleRotationPointerDown: controller.onRotationPointerDown,
|
|
handlePointerMove: controller.onPointerMove,
|
|
handlePointerUp: controller.onPointerUp,
|
|
};
|
|
}
|