fix: time selection show handles
This commit is contained in:
Binary file not shown.
+60
-46
@@ -200,6 +200,8 @@
|
|||||||
markers,
|
markers,
|
||||||
selectionMode,
|
selectionMode,
|
||||||
localSelectionTrackId,
|
localSelectionTrackId,
|
||||||
|
localSelectionStart,
|
||||||
|
currentTime,
|
||||||
localSelLeft,
|
localSelLeft,
|
||||||
localSelRight,
|
localSelRight,
|
||||||
onTrackLaneMouseDown,
|
onTrackLaneMouseDown,
|
||||||
@@ -294,7 +296,7 @@
|
|||||||
// 1. Draw Clip Layer Background & Border
|
// 1. Draw Clip Layer Background & Border
|
||||||
ctx.fillStyle = isClipSelected ? (track.color ? track.color + '44' : 'rgba(6, 182, 212, 0.30)') : (track.color ? track.color + '22' : 'rgba(6, 182, 212, 0.12)');
|
ctx.fillStyle = isClipSelected ? (track.color ? track.color + '44' : 'rgba(6, 182, 212, 0.30)') : (track.color ? track.color + '22' : 'rgba(6, 182, 212, 0.12)');
|
||||||
ctx.strokeStyle = isClipSelected ? '#fbbf24' : (track.color || '#06b6d4');
|
ctx.strokeStyle = isClipSelected ? '#fbbf24' : (track.color || '#06b6d4');
|
||||||
ctx.lineWidth = isClipSelected ? 3 : 1.5;
|
ctx.lineWidth = isClipSelected ? 1 : 1.5;
|
||||||
|
|
||||||
const clipTop = 8;
|
const clipTop = 8;
|
||||||
const clipHeight = height - 16;
|
const clipHeight = height - 16;
|
||||||
@@ -379,7 +381,7 @@
|
|||||||
ctx.fillStyle = 'rgba(245, 158, 11, 0.15)';
|
ctx.fillStyle = 'rgba(245, 158, 11, 0.15)';
|
||||||
ctx.fillRect(hlLeft, 0, hlWidth, height);
|
ctx.fillRect(hlLeft, 0, hlWidth, height);
|
||||||
ctx.strokeStyle = '#f59e0b';
|
ctx.strokeStyle = '#f59e0b';
|
||||||
ctx.lineWidth = 1.5;
|
ctx.lineWidth = 1;
|
||||||
ctx.strokeRect(hlLeft, 0, hlWidth, height);
|
ctx.strokeRect(hlLeft, 0, hlWidth, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,7 +539,7 @@
|
|||||||
onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime, e.ctrlKey);
|
onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime, e.ctrlKey);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onPlayheadSet(time, e.shiftKey);
|
onPlayheadSet(time);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -564,7 +566,22 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPlayheadSet(time, e.shiftKey);
|
if (e.shiftKey) {
|
||||||
|
// Shift+click on track: extend LOCAL selection from current start to clicked position
|
||||||
|
const prevStart = localSelectionStart !== null ? localSelectionStart : currentTime;
|
||||||
|
const selS = Math.min(prevStart, time);
|
||||||
|
const selE = Math.max(prevStart, time);
|
||||||
|
setSelectionMode('local');
|
||||||
|
setLocalSelectionTrackId(track.id);
|
||||||
|
setLocalSelectionStart(selS);
|
||||||
|
setLocalSelectionEnd(selE);
|
||||||
|
setSelectionStart(selS);
|
||||||
|
setSelectionEnd(selE);
|
||||||
|
setCurrentTime(time);
|
||||||
|
e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onPlayheadSet(time);
|
||||||
if (onTrackLaneMouseDown) {
|
if (onTrackLaneMouseDown) {
|
||||||
onTrackLaneMouseDown(track.id, time, e);
|
onTrackLaneMouseDown(track.id, time, e);
|
||||||
}
|
}
|
||||||
@@ -609,7 +626,7 @@
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TempoTrackLane = ({ bpm, zoom, timelineWidth, onPlayheadSet, snapValue }) => {
|
const TempoTrackLane = ({ bpm, zoom, timelineWidth, onPlayheadSet, snapValue, onRulerMouseDown }) => {
|
||||||
const canvasRef = useRef(null);
|
const canvasRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -705,7 +722,15 @@
|
|||||||
const rect = canvasRef.current.getBoundingClientRect();
|
const rect = canvasRef.current.getBoundingClientRect();
|
||||||
const x = e.clientX - rect.left + scrollLeft;
|
const x = e.clientX - rect.left + scrollLeft;
|
||||||
const time = Math.max(0, x / zoom);
|
const time = Math.max(0, x / zoom);
|
||||||
onPlayheadSet(time, e.shiftKey);
|
if (e.shiftKey) {
|
||||||
|
// Shift+click on tempo track: extend global selection from currentTime to clicked position
|
||||||
|
if (onRulerMouseDown) onRulerMouseDown({ ...e, shiftKey: true });
|
||||||
|
else onPlayheadSet(time);
|
||||||
|
} else {
|
||||||
|
// Click on tempo track: set playhead and start global selection point
|
||||||
|
if (onRulerMouseDown) onRulerMouseDown(e);
|
||||||
|
else onPlayheadSet(time);
|
||||||
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -4519,35 +4544,9 @@
|
|||||||
document.addEventListener('mouseup', handleMouseUp);
|
document.addEventListener('mouseup', handleMouseUp);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Playhead set with seek+play & shift+click selection ──
|
// ── Playhead set with seek+play ──
|
||||||
const handlePlayheadSet = (time, shiftKey) => {
|
const handlePlayheadSet = (time, shiftKey) => {
|
||||||
if (shiftKey) {
|
if (isPlaying) {
|
||||||
// Shift+click: extend selection from currentTime to clicked position
|
|
||||||
const selStart = Math.min(currentTime, time);
|
|
||||||
const selEnd = Math.max(currentTime, time);
|
|
||||||
setSelectionStart(selStart);
|
|
||||||
setSelectionEnd(selEnd);
|
|
||||||
setSelectionMode('global');
|
|
||||||
clearLocalSelection();
|
|
||||||
if (isLoopingSelection) {
|
|
||||||
// If looping, restart loop from new selection start
|
|
||||||
stopAllPlayback();
|
|
||||||
setCurrentTime(selStart);
|
|
||||||
if (isPlaying) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startOffsetTimeRef.current = selStart;
|
|
||||||
startAudioTimeRef.current = getAudioContext().currentTime;
|
|
||||||
startTrackPlayback(selStart);
|
|
||||||
setIsPlaying(true);
|
|
||||||
}, 50);
|
|
||||||
}
|
|
||||||
} else if (isPlaying) {
|
|
||||||
// Continue playing, just update the playhead visually
|
|
||||||
setCurrentTime(time);
|
|
||||||
} else {
|
|
||||||
setCurrentTime(time);
|
|
||||||
}
|
|
||||||
} else if (isPlaying) {
|
|
||||||
// Click during playback: seek to position and continue playing
|
// Click during playback: seek to position and continue playing
|
||||||
setCurrentTime(time);
|
setCurrentTime(time);
|
||||||
stopAllPlayback();
|
stopAllPlayback();
|
||||||
@@ -4582,12 +4581,18 @@
|
|||||||
setSelectionMode('global');
|
setSelectionMode('global');
|
||||||
rulerDragStartRef.current = time;
|
rulerDragStartRef.current = time;
|
||||||
isDraggingRulerRef.current = true;
|
isDraggingRulerRef.current = true;
|
||||||
setSelectionStart(time);
|
if (e.shiftKey) {
|
||||||
setSelectionEnd(time);
|
// Shift+click on ruler: extend global selection from currentTime to clicked position
|
||||||
handlePlayheadSet(time, e.shiftKey);
|
setSelectionStart(Math.min(currentTime, time));
|
||||||
|
setSelectionEnd(Math.max(currentTime, time));
|
||||||
|
} else {
|
||||||
|
setSelectionStart(time);
|
||||||
|
setSelectionEnd(time);
|
||||||
|
}
|
||||||
|
handlePlayheadSet(time);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global Ruler mousemove is tracked via document listener set up in useEffect
|
// Global Ruler mousemove is tracked via document listener set up in useEffect
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleMouseMove = (e) => {
|
const handleMouseMove = (e) => {
|
||||||
if (!isDraggingRulerRef.current) return;
|
if (!isDraggingRulerRef.current) return;
|
||||||
@@ -6912,10 +6917,17 @@
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Global selection overlay for main session */}
|
||||||
|
{selectionMode === 'global' && selLeft !== null && selRight !== null && selRight > selLeft && (
|
||||||
|
<div className="absolute inset-0 pointer-events-none z-20"
|
||||||
|
style={{ left: `${selLeft * zoom}px`, width: `${(selRight - selLeft) * zoom}px`, top: '40px' }}>
|
||||||
|
<div className="w-full h-full bg-amber-500/10" style={{ borderLeft: '1px solid #f59e0b', borderRight: '1px solid #f59e0b' }}></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="sticky top-10 z-35 flex h-[40px] border-b border-purple-500/30 bg-[#1a1a2e] shrink-0">
|
<div className="sticky top-10 z-35 flex h-[40px] border-b border-purple-500/30 bg-[#1a1a2e] shrink-0">
|
||||||
<div className="flex-1 relative h-full overflow-hidden bg-[#1a1a2e]">
|
<div className="flex-1 relative h-full overflow-hidden bg-[#1a1a2e]">
|
||||||
<TempoTrackLane bpm={parseInt(bpm) || 120} zoom={zoom} timelineWidth={timelineWidth}
|
<TempoTrackLane bpm={parseInt(bpm) || 120} zoom={zoom} timelineWidth={timelineWidth}
|
||||||
onPlayheadSet={handlePlayheadSet} snapValue={snapValue} />
|
onPlayheadSet={handlePlayheadSet} snapValue={snapValue} onRulerMouseDown={handleRulerMouseDown} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex flex-col divide-y divide-[#141414] relative bg-[#111111] min-h-full">
|
<div className="flex-1 flex flex-col divide-y divide-[#141414] relative bg-[#111111] min-h-full">
|
||||||
@@ -6941,9 +6953,11 @@
|
|||||||
onSplitTrackAtTime={handleSplitTrackAtTime}
|
onSplitTrackAtTime={handleSplitTrackAtTime}
|
||||||
onEditClipInSubTab={handleEditClipInSubTab}
|
onEditClipInSubTab={handleEditClipInSubTab}
|
||||||
snapValue={snapValue} bpm={bpm}
|
snapValue={snapValue} bpm={bpm}
|
||||||
selectionMode={selectionMode}
|
selectionMode={selectionMode}
|
||||||
localSelectionTrackId={localSelectionTrackId}
|
localSelectionTrackId={localSelectionTrackId}
|
||||||
localSelLeft={localSelectionStart !== null && localSelectionEnd !== null ? Math.min(localSelectionStart, localSelectionEnd) : null}
|
localSelectionStart={localSelectionStart}
|
||||||
|
currentTime={currentTime}
|
||||||
|
localSelLeft={localSelectionStart !== null && localSelectionEnd !== null ? Math.min(localSelectionStart, localSelectionEnd) : null}
|
||||||
localSelRight={localSelectionStart !== null && localSelectionEnd !== null ? Math.max(localSelectionStart, localSelectionEnd) : null} />
|
localSelRight={localSelectionStart !== null && localSelectionEnd !== null ? Math.max(localSelectionStart, localSelectionEnd) : null} />
|
||||||
{track.buffer && (
|
{track.buffer && (
|
||||||
<div className="absolute right-2 top-2 flex items-center gap-1 z-10 opacity-70 hover:opacity-100">
|
<div className="absolute right-2 top-2 flex items-center gap-1 z-10 opacity-70 hover:opacity-100">
|
||||||
@@ -6965,10 +6979,10 @@
|
|||||||
<div className="absolute top-0 bottom-0 border-l border-r border-amber-500 bg-amber-500/10 z-20 selection-interactive-box cursor-grab active:cursor-grabbing"
|
<div className="absolute top-0 bottom-0 border-l border-r border-amber-500 bg-amber-500/10 z-20 selection-interactive-box cursor-grab active:cursor-grabbing"
|
||||||
style={{ left: `${selLeft * zoom}px`, width: `${(selRight - selLeft) * zoom}px` }}
|
style={{ left: `${selLeft * zoom}px`, width: `${(selRight - selLeft) * zoom}px` }}
|
||||||
onMouseDown={handleSelectionBodyDragStart}>
|
onMouseDown={handleSelectionBodyDragStart}>
|
||||||
<div className="absolute -left-1.5 top-0 bottom-0 w-3 bg-amber-500 hover:bg-amber-400 cursor-ew-resize flex items-center justify-center z-30"
|
<div className="absolute -left-[1px] top-0 bottom-0 w-[1px] bg-amber-500 hover:bg-amber-400 cursor-ew-resize z-30"
|
||||||
onMouseDown={e => handleHandleDragStart(e, 'left')}><div className="w-[1.5px] h-4 bg-zinc-950/70 rounded"></div></div>
|
onMouseDown={e => handleHandleDragStart(e, 'left')}></div>
|
||||||
<div className="absolute -right-1.5 top-0 bottom-0 w-3 bg-amber-500 hover:bg-amber-400 cursor-ew-resize flex items-center justify-center z-30"
|
<div className="absolute -right-[1px] top-0 bottom-0 w-[1px] bg-amber-500 hover:bg-amber-400 cursor-ew-resize z-30"
|
||||||
onMouseDown={e => handleHandleDragStart(e, 'right')}><div className="w-[1.5px] h-4 bg-zinc-950/70 rounded"></div></div>
|
onMouseDown={e => handleHandleDragStart(e, 'right')}></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="absolute top-0 bottom-0 w-[2px] bg-[#ef4444] z-20 pointer-events-none" style={{ left: `${playheadLeftPos}px` }}>
|
<div className="absolute top-0 bottom-0 w-[2px] bg-[#ef4444] z-20 pointer-events-none" style={{ left: `${playheadLeftPos}px` }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user