refactor: refine timeline shift-range selection
This commit is contained in:
parent
e223348d47
commit
ae2b34e114
|
|
@ -379,10 +379,18 @@ export class ElementInteractionController {
|
||||||
|
|
||||||
let rangeSelection: readonly ElementRef[] | null = null;
|
let rangeSelection: readonly ElementRef[] | null = null;
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
|
const anchor =
|
||||||
|
this.elementSelectionAnchor?.trackId === ref.trackId
|
||||||
|
? this.elementSelectionAnchor
|
||||||
|
: null;
|
||||||
rangeSelection = this.deps.selection.selectRange({
|
rangeSelection = this.deps.selection.selectRange({
|
||||||
anchor: this.elementSelectionAnchor,
|
anchor: anchor ?? ref,
|
||||||
target: ref,
|
target: ref,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!anchor) {
|
||||||
|
this.elementSelectionAnchor = ref;
|
||||||
|
}
|
||||||
} else if (event.metaKey || event.ctrlKey) {
|
} else if (event.metaKey || event.ctrlKey) {
|
||||||
this.deps.selection.handleClick({ ...ref, isMultiKey: true });
|
this.deps.selection.handleClick({ ...ref, isMultiKey: true });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { useEditor } from "@/editor/use-editor";
|
import { useEditor } from "@/editor/use-editor";
|
||||||
|
import { findTrackInSceneTracks } from "@/timeline/track-element-update";
|
||||||
import type { ElementRef } from "@/timeline/types";
|
import type { ElementRef } from "@/timeline/types";
|
||||||
|
|
||||||
export function useElementSelection() {
|
export function useElementSelection() {
|
||||||
|
|
@ -102,8 +103,8 @@ export function useElementSelection() {
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects every element between the anchor and target on the same timeline row.
|
* Handles Shift-click range selection. Selects all clips between the anchor
|
||||||
* Falls back to selecting only the target when no same-row anchor exists.
|
* and target when both are on the same track; otherwise selects only target.
|
||||||
*/
|
*/
|
||||||
const selectElementRange = useCallback(
|
const selectElementRange = useCallback(
|
||||||
({
|
({
|
||||||
|
|
@ -113,58 +114,47 @@ export function useElementSelection() {
|
||||||
anchor?: ElementRef | null;
|
anchor?: ElementRef | null;
|
||||||
target: ElementRef;
|
target: ElementRef;
|
||||||
}) => {
|
}) => {
|
||||||
const tracks = editor.scenes.getActiveScene().tracks;
|
const setSelection = (elements: ElementRef[]) => {
|
||||||
const track = [...tracks.overlay, tracks.main, ...tracks.audio].find(
|
|
||||||
(candidate) => candidate.id === target.trackId,
|
|
||||||
);
|
|
||||||
const rangeAnchor =
|
|
||||||
anchor?.trackId === target.trackId
|
|
||||||
? anchor
|
|
||||||
: selectedElements
|
|
||||||
.slice()
|
|
||||||
.reverse()
|
|
||||||
.find((element) => element.trackId === target.trackId);
|
|
||||||
|
|
||||||
if (!track || !rangeAnchor) {
|
|
||||||
const elements = [target];
|
|
||||||
editor.selection.setSelectedElements({ elements });
|
editor.selection.setSelectedElements({ elements });
|
||||||
return elements;
|
return elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
const track = findTrackInSceneTracks({
|
||||||
|
tracks: editor.scenes.getActiveScene().tracks,
|
||||||
|
trackId: target.trackId,
|
||||||
|
});
|
||||||
|
if (!track || anchor?.trackId !== target.trackId) {
|
||||||
|
return setSelection([target]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderedElements = track.elements
|
const orderedElements = track.elements
|
||||||
.map((element, index) => ({ element, index }))
|
.map((element, index) => ({ element, index }))
|
||||||
.sort((a, b) => {
|
.sort((a, b) =>
|
||||||
if (a.element.startTime !== b.element.startTime) {
|
a.element.startTime === b.element.startTime
|
||||||
return a.element.startTime - b.element.startTime;
|
? a.index - b.index
|
||||||
}
|
: a.element.startTime - b.element.startTime,
|
||||||
return a.index - b.index;
|
);
|
||||||
});
|
|
||||||
const anchorIndex = orderedElements.findIndex(
|
const anchorIndex = orderedElements.findIndex(
|
||||||
({ element }) => element.id === rangeAnchor.elementId,
|
({ element }) => element.id === anchor.elementId,
|
||||||
);
|
);
|
||||||
const targetIndex = orderedElements.findIndex(
|
const targetIndex = orderedElements.findIndex(
|
||||||
({ element }) => element.id === target.elementId,
|
({ element }) => element.id === target.elementId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (anchorIndex === -1 || targetIndex === -1) {
|
if (anchorIndex === -1 || targetIndex === -1) {
|
||||||
const elements = [target];
|
return setSelection([target]);
|
||||||
editor.selection.setSelectedElements({ elements });
|
|
||||||
return elements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = Math.min(anchorIndex, targetIndex);
|
const start = Math.min(anchorIndex, targetIndex);
|
||||||
const end = Math.max(anchorIndex, targetIndex);
|
const end = Math.max(anchorIndex, targetIndex);
|
||||||
const elements = orderedElements
|
return setSelection(
|
||||||
.slice(start, end + 1)
|
orderedElements.slice(start, end + 1).map(({ element }) => ({
|
||||||
.map(({ element }) => ({
|
|
||||||
trackId: target.trackId,
|
trackId: target.trackId,
|
||||||
elementId: element.id,
|
elementId: element.id,
|
||||||
}));
|
})),
|
||||||
|
);
|
||||||
editor.selection.setSelectedElements({ elements });
|
|
||||||
return elements;
|
|
||||||
},
|
},
|
||||||
[selectedElements, editor],
|
[editor],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue