mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
feat: centralize selection management
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
import type { ElementRef } from "@/lib/timeline/types";
|
||||
|
||||
export interface CommandResult {
|
||||
select?: ElementRef[];
|
||||
}
|
||||
|
||||
export abstract class Command {
|
||||
abstract execute(): void;
|
||||
abstract execute(): CommandResult | undefined;
|
||||
|
||||
undo(): void {
|
||||
throw new Error("Undo not implemented for this command");
|
||||
}
|
||||
|
||||
redo(): void {
|
||||
this.execute();
|
||||
redo(): CommandResult | undefined {
|
||||
return this.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import { Command } from "./base-command";
|
||||
import { Command, type CommandResult } from "./base-command";
|
||||
|
||||
export class BatchCommand extends Command {
|
||||
constructor(private commands: Command[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
execute(): void {
|
||||
execute(): CommandResult | undefined {
|
||||
let latestSelectionResult: CommandResult | undefined;
|
||||
|
||||
for (const command of this.commands) {
|
||||
command.execute();
|
||||
const result = command.execute();
|
||||
if (result?.select !== undefined) {
|
||||
latestSelectionResult = result;
|
||||
}
|
||||
}
|
||||
|
||||
return latestSelectionResult;
|
||||
}
|
||||
|
||||
undo(): void {
|
||||
@@ -17,9 +24,16 @@ export class BatchCommand extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
redo(): void {
|
||||
redo(): CommandResult | undefined {
|
||||
let latestSelectionResult: CommandResult | undefined;
|
||||
|
||||
for (const command of this.commands) {
|
||||
command.execute();
|
||||
const result = command.redo();
|
||||
if (result?.select !== undefined) {
|
||||
latestSelectionResult = result;
|
||||
}
|
||||
}
|
||||
|
||||
return latestSelectionResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export { Command } from "./base-command";
|
||||
export type { CommandResult } from "./base-command";
|
||||
export { BatchCommand } from "./batch-command";
|
||||
|
||||
export * from "./timeline";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Command } from "@/lib/commands/base-command";
|
||||
import { Command, type CommandResult } from "@/lib/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type {
|
||||
TimelineTrack,
|
||||
@@ -17,21 +17,26 @@ import { cloneAnimations } from "@/lib/animation";
|
||||
export class PasteCommand extends Command {
|
||||
private savedState: TimelineTrack[] | null = null;
|
||||
private pastedElements: { trackId: string; elementId: string }[] = [];
|
||||
private previousSelection: { trackId: string; elementId: string }[] = [];
|
||||
private readonly time: number;
|
||||
private readonly clipboardItems: ClipboardItem[];
|
||||
|
||||
constructor(
|
||||
private time: number,
|
||||
private clipboardItems: ClipboardItem[],
|
||||
) {
|
||||
constructor({
|
||||
time,
|
||||
clipboardItems,
|
||||
}: {
|
||||
time: number;
|
||||
clipboardItems: ClipboardItem[];
|
||||
}) {
|
||||
super();
|
||||
this.time = time;
|
||||
this.clipboardItems = clipboardItems;
|
||||
}
|
||||
|
||||
execute(): void {
|
||||
execute(): CommandResult | undefined {
|
||||
if (this.clipboardItems.length === 0) return;
|
||||
|
||||
const editor = EditorCore.getInstance();
|
||||
this.savedState = editor.timeline.getTracks();
|
||||
this.previousSelection = editor.selection.getSelectedElements();
|
||||
this.pastedElements = [];
|
||||
|
||||
const minStart = Math.min(
|
||||
@@ -116,7 +121,7 @@ export class PasteCommand extends Command {
|
||||
editor.timeline.updateTracks(updatedTracks);
|
||||
|
||||
if (this.pastedElements.length > 0) {
|
||||
editor.selection.setSelectedElements({ elements: this.pastedElements });
|
||||
return { select: this.pastedElements };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,9 +129,6 @@ export class PasteCommand extends Command {
|
||||
if (this.savedState) {
|
||||
const editor = EditorCore.getInstance();
|
||||
editor.timeline.updateTracks(this.savedState);
|
||||
editor.selection.setSelectedElements({
|
||||
elements: this.previousSelection,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Command } from "@/lib/commands/base-command";
|
||||
import { Command, type CommandResult } from "@/lib/commands/base-command";
|
||||
import type { TimelineTrack } from "@/lib/timeline";
|
||||
import { EditorCore } from "@/core";
|
||||
import { rippleShiftElements } from "@/lib/timeline";
|
||||
@@ -21,7 +21,7 @@ export class DeleteElementsCommand extends Command {
|
||||
this.rippleEnabled = rippleEnabled;
|
||||
}
|
||||
|
||||
execute(): void {
|
||||
execute(): CommandResult {
|
||||
const editor = EditorCore.getInstance();
|
||||
this.savedState = editor.timeline.getTracks();
|
||||
|
||||
@@ -69,6 +69,10 @@ export class DeleteElementsCommand extends Command {
|
||||
.filter((track) => track.elements.length > 0 || isMainTrack(track));
|
||||
|
||||
editor.timeline.updateTracks(updatedTracks);
|
||||
|
||||
return {
|
||||
select: [],
|
||||
};
|
||||
}
|
||||
|
||||
undo(): void {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Command } from "@/lib/commands/base-command";
|
||||
import { Command, type CommandResult } from "@/lib/commands/base-command";
|
||||
import type { TimelineElement, TimelineTrack } from "@/lib/timeline";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
import { EditorCore } from "@/core";
|
||||
@@ -12,7 +12,6 @@ interface DuplicateElementsParams {
|
||||
export class DuplicateElementsCommand extends Command {
|
||||
private duplicatedElements: { trackId: string; elementId: string }[] = [];
|
||||
private savedState: TimelineTrack[] | null = null;
|
||||
private previousSelection: { trackId: string; elementId: string }[] = [];
|
||||
private elements: DuplicateElementsParams["elements"];
|
||||
|
||||
constructor({ elements }: DuplicateElementsParams) {
|
||||
@@ -20,10 +19,9 @@ export class DuplicateElementsCommand extends Command {
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
execute(): void {
|
||||
execute(): CommandResult | undefined {
|
||||
const editor = EditorCore.getInstance();
|
||||
this.savedState = editor.timeline.getTracks();
|
||||
this.previousSelection = editor.selection.getSelectedElements();
|
||||
this.duplicatedElements = [];
|
||||
|
||||
let updatedTracks = [...this.savedState];
|
||||
@@ -89,9 +87,9 @@ export class DuplicateElementsCommand extends Command {
|
||||
editor.timeline.updateTracks(updatedTracks);
|
||||
|
||||
if (this.duplicatedElements.length > 0) {
|
||||
editor.selection.setSelectedElements({
|
||||
elements: this.duplicatedElements,
|
||||
});
|
||||
return {
|
||||
select: this.duplicatedElements,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +97,6 @@ export class DuplicateElementsCommand extends Command {
|
||||
if (this.savedState) {
|
||||
const editor = EditorCore.getInstance();
|
||||
editor.timeline.updateTracks(this.savedState);
|
||||
editor.selection.setSelectedElements({
|
||||
elements: this.previousSelection,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Command } from "@/lib/commands/base-command";
|
||||
import { Command, type CommandResult } from "@/lib/commands/base-command";
|
||||
import type { TimelineTrack } from "@/lib/timeline";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
import { EditorCore } from "@/core";
|
||||
@@ -9,7 +9,6 @@ import { getSourceSpanAtClipTime } from "@/lib/retime";
|
||||
export class SplitElementsCommand extends Command {
|
||||
private savedState: TimelineTrack[] | null = null;
|
||||
private rightSideElements: { trackId: string; elementId: string }[] = [];
|
||||
private previousSelection: { trackId: string; elementId: string }[] = [];
|
||||
private readonly elements: { trackId: string; elementId: string }[];
|
||||
private readonly splitTime: number;
|
||||
private readonly retainSide: "both" | "left" | "right";
|
||||
@@ -37,10 +36,9 @@ export class SplitElementsCommand extends Command {
|
||||
return this.rightSideElements;
|
||||
}
|
||||
|
||||
execute(): void {
|
||||
execute(): CommandResult | undefined {
|
||||
const editor = EditorCore.getInstance();
|
||||
this.savedState = editor.timeline.getTracks();
|
||||
this.previousSelection = editor.selection.getSelectedElements();
|
||||
this.rightSideElements = [];
|
||||
|
||||
const updatedTracks = this.savedState.map((track) => {
|
||||
@@ -173,9 +171,9 @@ export class SplitElementsCommand extends Command {
|
||||
editor.timeline.updateTracks(updatedTracks);
|
||||
|
||||
if (this.rightSideElements.length > 0) {
|
||||
editor.selection.setSelectedElements({
|
||||
elements: this.rightSideElements,
|
||||
});
|
||||
return {
|
||||
select: this.rightSideElements,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,9 +181,6 @@ export class SplitElementsCommand extends Command {
|
||||
if (this.savedState) {
|
||||
const editor = EditorCore.getInstance();
|
||||
editor.timeline.updateTracks(this.savedState);
|
||||
editor.selection.setSelectedElements({
|
||||
elements: this.previousSelection,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user