fix: Ctrl+Click on section/MIDI items toggled selection, locked item

Ctrl+Click handler toggled selection + set pending drag.
Once selected, drag system treated it as 'move selection'
instead of 'copy'. Removed selection toggle - Ctrl+Click
now only sets pending drag for copy operation.
This commit is contained in:
2026-07-28 08:07:49 +07:00
parent e4d6405b37
commit 447504ea67
3 changed files with 10 additions and 12 deletions
+2 -9
View File
@@ -1083,15 +1083,8 @@ const WaveformLane = ({
e.preventDefault();
e.stopPropagation();
if (e.ctrlKey) {
// Ctrl+Click: toggle selection only, never start drag immediately
var preToggleSnapshot = selectedItemIds ? new Set(selectedItemIds) : new Set();
if (selectedItemIds && selectedItemIds.has(hitItem.id)) {
if (onDeselectItem) onDeselectItem(hitItem.id);
} else if (onAddToSelection) {
onAddToSelection(hitItem.id);
}
// Store pre-toggle snapshot so drag copies the ORIGINAL group
if (onSetPendingDrag) onSetPendingDrag(track.id, hitItem.type, hitItem.id, time - hitItem.start, e.nativeEvent || e, preToggleSnapshot);
// Ctrl+Click: copy on drag, do NOT toggle selection
if (onSetPendingDrag) onSetPendingDrag(track.id, hitItem.type, hitItem.id, time - hitItem.start, e.nativeEvent || e, selectedItemIds ? new Set(selectedItemIds) : new Set());
} else {
// No modifier: start drag immediately (single or multi move)
if (onSectionItemDragStart) onSectionItemDragStart(track.id, hitItem.type, hitItem.id, time - hitItem.start, false);
+2 -3
View File
@@ -51,9 +51,8 @@ if(e.ctrlKey&&selectionMode){e.preventDefault();e.stopPropagation();if(onClearLo
if(e.shiftKey){e.preventDefault();e.stopPropagation();const currentAnchor=getLocalAnchor?getLocalAnchor():null;const anchor=currentAnchor!==null&&currentAnchor!==undefined?currentAnchor:localSelectionStart!==null?localSelectionStart:currentTime;const selS=Math.min(anchor,time);const selE=Math.max(anchor,time);if(onSetSelectionMode)onSetSelectionMode('local');if(onSetLocalSelectionTrackId)onSetLocalSelectionTrackId(track.id);if(onSetLocalSelectionStart)onSetLocalSelectionStart(selS);if(onSetLocalSelectionEnd)onSetLocalSelectionEnd(selE);if(onSetSelectionStart)onSetSelectionStart(selS);if(onSetSelectionEnd)onSetSelectionEnd(selE);if(onSetCurrentTime)onSetCurrentTime(time);if(onSelectTrack)onSelectTrack(track.id);return;}// Check if dragging selection boundaries (local mode)
const isLocal=selectionMode==='local'&&localSelectionTrackId===track.id;if(isLocal&&localSelLeft!==null&&localSelRight!==null){const leftPx=localSelLeft*zoom;const rightPx=localSelRight*zoom;const distToLeft=Math.abs(x-leftPx);const distToRight=Math.abs(x-rightPx);if(distToLeft<=5){e.preventDefault();e.stopPropagation();if(onSelectionEdgeDragStart)onSelectionEdgeDragStart(e,track.id,'left');return;}else if(distToRight<=5){e.preventDefault();e.stopPropagation();if(onSelectionEdgeDragStart)onSelectionEdgeDragStart(e,track.id,'right');return;}}// Check if time-stretching (Alt + Right Edge)
const toleranceSec=8/zoom;const rightEdgeClip=clips.find(c=>{const duration=c.buffer.duration/(c.speed||1.0);return Math.abs(time-(c.startTime+duration))<=toleranceSec;});if(rightEdgeClip&&e.altKey){e.preventDefault();e.stopPropagation();if(onClipStretchStart){onClipStretchStart(track.id,rightEdgeClip.id,time);}return;}// Check section/MIDI item edge for resize, then body for drag
const secItems=track.sections||[];const midiItems=track.midiItems||[];const secTol=8/zoom;let hitItem=null;let hitEdge=null;for(const sec of secItems){if(Math.abs(time-sec.start)<=secTol){hitItem={type:'section',id:sec.id,start:sec.start,dur:sec.duration};hitEdge='left';break;}if(Math.abs(time-(sec.start+sec.duration))<=secTol){hitItem={type:'section',id:sec.id,start:sec.start,dur:sec.duration};hitEdge='right';break;}}if(!hitItem){for(const midi of midiItems){if(Math.abs(time-midi.startTime)<=secTol){hitItem={type:'midiItem',id:midi.id,start:midi.startTime,dur:midi.duration};hitEdge='left';break;}if(Math.abs(time-(midi.startTime+midi.duration))<=secTol){hitItem={type:'midiItem',id:midi.id,start:midi.startTime,dur:midi.duration};hitEdge='right';break;}}}if(hitItem&&hitEdge){e.preventDefault();e.stopPropagation();if(onSectionItemResizeStart)onSectionItemResizeStart(track.id,hitItem.type,hitItem.id,hitEdge,time);return;}if(!hitItem){for(const sec of secItems){if(time>=sec.start&&time<sec.start+sec.duration){hitItem={type:'section',id:sec.id,start:sec.start};break;}}}if(!hitItem){for(const midi of midiItems){if(time>=midi.startTime&&time<midi.startTime+midi.duration){hitItem={type:'midiItem',id:midi.id,start:midi.startTime};break;}}}if(hitItem&&!e.altKey&&!e.shiftKey){e.preventDefault();e.stopPropagation();if(e.ctrlKey){// Ctrl+Click: toggle selection only, never start drag immediately
var preToggleSnapshot=selectedItemIds?new Set(selectedItemIds):new Set();if(selectedItemIds&&selectedItemIds.has(hitItem.id)){if(onDeselectItem)onDeselectItem(hitItem.id);}else if(onAddToSelection){onAddToSelection(hitItem.id);}// Store pre-toggle snapshot so drag copies the ORIGINAL group
if(onSetPendingDrag)onSetPendingDrag(track.id,hitItem.type,hitItem.id,time-hitItem.start,e.nativeEvent||e,preToggleSnapshot);}else{// No modifier: start drag immediately (single or multi move)
const secItems=track.sections||[];const midiItems=track.midiItems||[];const secTol=8/zoom;let hitItem=null;let hitEdge=null;for(const sec of secItems){if(Math.abs(time-sec.start)<=secTol){hitItem={type:'section',id:sec.id,start:sec.start,dur:sec.duration};hitEdge='left';break;}if(Math.abs(time-(sec.start+sec.duration))<=secTol){hitItem={type:'section',id:sec.id,start:sec.start,dur:sec.duration};hitEdge='right';break;}}if(!hitItem){for(const midi of midiItems){if(Math.abs(time-midi.startTime)<=secTol){hitItem={type:'midiItem',id:midi.id,start:midi.startTime,dur:midi.duration};hitEdge='left';break;}if(Math.abs(time-(midi.startTime+midi.duration))<=secTol){hitItem={type:'midiItem',id:midi.id,start:midi.startTime,dur:midi.duration};hitEdge='right';break;}}}if(hitItem&&hitEdge){e.preventDefault();e.stopPropagation();if(onSectionItemResizeStart)onSectionItemResizeStart(track.id,hitItem.type,hitItem.id,hitEdge,time);return;}if(!hitItem){for(const sec of secItems){if(time>=sec.start&&time<sec.start+sec.duration){hitItem={type:'section',id:sec.id,start:sec.start};break;}}}if(!hitItem){for(const midi of midiItems){if(time>=midi.startTime&&time<midi.startTime+midi.duration){hitItem={type:'midiItem',id:midi.id,start:midi.startTime};break;}}}if(hitItem&&!e.altKey&&!e.shiftKey){e.preventDefault();e.stopPropagation();if(e.ctrlKey){// Ctrl+Click: copy on drag, do NOT toggle selection
if(onSetPendingDrag)onSetPendingDrag(track.id,hitItem.type,hitItem.id,time-hitItem.start,e.nativeEvent||e,selectedItemIds?new Set(selectedItemIds):new Set());}else{// No modifier: start drag immediately (single or multi move)
if(onSectionItemDragStart)onSectionItemDragStart(track.id,hitItem.type,hitItem.id,time-hitItem.start,false);}return;}const clickedClip=clips.find(c=>time>=c.startTime&&time<c.startTime+c.buffer.duration/(c.speed||1.0));// Set selected clip ID
if(clickedClip){setSelectedClipId({trackId:track.id,clipId:clickedClip.id==='default'?'default_'+track.id:clickedClip.id});}else{setSelectedClipId(null);}if(activeTool==='pen'&&!e.ctrlKey){if(clickedClip){e.preventDefault();e.stopPropagation();if(onEditClipInSubTab){onEditClipInSubTab(track.id,clickedClip.id);}}return;}if(activeTool==='razor'&&!e.ctrlKey){if(clickedClip){e.preventDefault();e.stopPropagation();if(onSplitTrackAtTime){onSplitTrackAtTime(track.id,clickedClip.id,time);}}return;}if(activeTool==='grab'){if(onTrackLaneMouseDown){onTrackLaneMouseDown(track.id,time,e);}if(clickedClip){e.preventDefault();e.stopPropagation();if(onClipDragStart){onClipDragStart(track.id,clickedClip.id,time-clickedClip.startTime,e.ctrlKey);}}else{onPlayheadSet(time);}return;}// Check for click drag clip (Alt to move, Ctrl to duplicate)
if(clickedClip&&(e.altKey||e.ctrlKey)){e.preventDefault();e.stopPropagation();if(e.ctrlKey){// Ctrl+Click: toggle selection, store pending drag w/ pre-toggle snapshot