fix: lỗi alt-click drag nhân bản
This commit is contained in:
+50
-11
@@ -363,7 +363,7 @@
|
|||||||
canvasRef.current.style.cursor = isOverClip ? 'cell' : 'not-allowed';
|
canvasRef.current.style.cursor = isOverClip ? 'cell' : 'not-allowed';
|
||||||
} else {
|
} else {
|
||||||
// select tool
|
// select tool
|
||||||
canvasRef.current.style.cursor = (isOverClip && e.altKey) ? 'grab' : 'crosshair';
|
canvasRef.current.style.cursor = (isOverClip && (e.altKey || e.ctrlKey)) ? 'grab' : 'crosshair';
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onMouseDown={(e) => {
|
onMouseDown={(e) => {
|
||||||
@@ -401,7 +401,7 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (onClipDragStart) {
|
if (onClipDragStart) {
|
||||||
onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime);
|
onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime, e.ctrlKey);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onPlayheadSet(time);
|
onPlayheadSet(time);
|
||||||
@@ -409,12 +409,12 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for click drag clip (no Ctrl key required if select tool is in fallback ctrl-mode)
|
// Check for click drag clip (Alt to move, Ctrl to duplicate)
|
||||||
if (clickedClip && e.ctrlKey) {
|
if (clickedClip && (e.altKey || e.ctrlKey)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (onClipDragStart) {
|
if (onClipDragStart) {
|
||||||
onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime);
|
onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime, e.ctrlKey);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1710,25 +1710,64 @@
|
|||||||
const captureTrackSnapshotRef = useRef(null);
|
const captureTrackSnapshotRef = useRef(null);
|
||||||
captureTrackSnapshotRef.current = captureTrackSnapshot;
|
captureTrackSnapshotRef.current = captureTrackSnapshot;
|
||||||
|
|
||||||
const handleClipDragStart = (trackId, clipId, clickOffset) => {
|
const handleClipDragStart = (trackId, clipId, clickOffset, isDuplicate = false) => {
|
||||||
const track = tracks.find(t => t.id === trackId);
|
const track = tracks.find(t => t.id === trackId);
|
||||||
if (!track) return;
|
if (!track) return;
|
||||||
|
|
||||||
const clips = track.clips && track.clips.length > 0 ? track.clips : (track.buffer ? [{
|
const existingClips = track.clips && track.clips.length > 0 ? track.clips : (track.buffer ? [{
|
||||||
id: 'default',
|
id: 'default_' + track.id,
|
||||||
buffer: track.buffer,
|
buffer: track.buffer,
|
||||||
startTime: track.startTime || 0,
|
startTime: track.startTime || 0,
|
||||||
name: track.name
|
name: track.name
|
||||||
}] : []);
|
}] : []);
|
||||||
|
|
||||||
const clip = clips.find(c => c.id === clipId);
|
const clip = existingClips.find(c => c.id === clipId || (clipId === 'default' && c.id === 'default_' + track.id));
|
||||||
if (!clip) return;
|
if (!clip) return;
|
||||||
|
|
||||||
const beforeSnap = captureTrackSnapshot(trackId);
|
const beforeSnap = captureTrackSnapshot(trackId);
|
||||||
|
|
||||||
|
let targetClipId = clip.id;
|
||||||
|
|
||||||
|
if (isDuplicate) {
|
||||||
|
const cloneId = clip.id + '_copy_' + Date.now();
|
||||||
|
const clone = {
|
||||||
|
...clip,
|
||||||
|
id: cloneId,
|
||||||
|
name: clip.name + ' (Copy)'
|
||||||
|
};
|
||||||
|
|
||||||
|
setTracks(prev => prev.map(t => {
|
||||||
|
if (t.id === trackId) {
|
||||||
|
const newClips = [...existingClips, clone];
|
||||||
|
return {
|
||||||
|
...t,
|
||||||
|
clips: newClips,
|
||||||
|
buffer: newClips[0].buffer,
|
||||||
|
startTime: newClips[0].startTime,
|
||||||
|
name: newClips[0].name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}));
|
||||||
|
|
||||||
|
targetClipId = cloneId;
|
||||||
|
} else {
|
||||||
|
if (!track.clips || track.clips.length === 0) {
|
||||||
|
setTracks(prev => prev.map(t => {
|
||||||
|
if (t.id === trackId) {
|
||||||
|
return {
|
||||||
|
...t,
|
||||||
|
clips: existingClips
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setDraggedClip({
|
setDraggedClip({
|
||||||
trackId: trackId,
|
trackId: trackId,
|
||||||
clipId: clipId,
|
clipId: targetClipId,
|
||||||
clickOffset: clickOffset,
|
clickOffset: clickOffset,
|
||||||
buffer: clip.buffer,
|
buffer: clip.buffer,
|
||||||
name: clip.name,
|
name: clip.name,
|
||||||
@@ -2789,7 +2828,7 @@
|
|||||||
<button
|
<button
|
||||||
onClick={() => { setActiveTool('select'); showToast('Công cụ chọn (Select Tool) đã kích hoạt.', 'info'); }}
|
onClick={() => { setActiveTool('select'); showToast('Công cụ chọn (Select Tool) đã kích hoạt.', 'info'); }}
|
||||||
className={`p-1 rounded transition text-xs flex items-center justify-center ${activeTool === 'select' ? 'bg-cyan-950/80 text-cyan-400 border border-cyan-800/50 font-bold' : 'text-zinc-400 hover:bg-zinc-800 border border-transparent'}`}
|
className={`p-1 rounded transition text-xs flex items-center justify-center ${activeTool === 'select' ? 'bg-cyan-950/80 text-cyan-400 border border-cyan-800/50 font-bold' : 'text-zinc-400 hover:bg-zinc-800 border border-transparent'}`}
|
||||||
title="Select Tool: Chọn khoảng, Đặt playhead (Giữ Ctrl kéo để di chuyển nhanh clip)"
|
title="Select Tool: Chọn khoảng, Đặt playhead (Alt+Kéo để di chuyển clip, Ctrl+Kéo để nhân bản)"
|
||||||
>
|
>
|
||||||
<i data-lucide="mouse-pointer" className="w-3 h-3"></i>
|
<i data-lucide="mouse-pointer" className="w-3 h-3"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user