FIX: cho phép copy/cut các notes được chọn trong PIANO ROLL TAB

This commit is contained in:
2026-08-07 21:39:18 +07:00
parent 652e745d0a
commit 688a7a57e2
4 changed files with 58 additions and 8 deletions
+24 -4
View File
@@ -7141,8 +7141,15 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
e.preventDefault();
if (!clipboardNotes || !clipboardNotes.length) { showToast('Clipboard trống — bấm Ctrl+C trước.', 'warning'); return; }
pushToUndo(notes);
setNotes(prev => [...(prev || []), ...clipboardNotes.map(function(n) { return { ...n, id: 'note_cp_' + Date.now() + '_' + Math.floor(Math.random() * 100000) }; })]);
showToast('Đã paste ' + clipboardNotes.length + ' nốt.', 'success');
// Dán bt đu ti v trí CON TR PLAYHEAD (user 09:25) bù offset
// sao cho note đu tiên ca clipboard nm đúng playhead (beat).
const pasteBeat = (st.currentTime || 0) / (60.0 / (parseInt(bpm) || 120));
const minStart = Math.min(...clipboardNotes.map(n => n.start_beat));
const offset = pasteBeat - minStart;
setNotes(prev => [...(prev || []), ...clipboardNotes.map(function(n) {
return { ...n, id: 'note_cp_' + Date.now() + '_' + Math.floor(Math.random() * 100000), start_beat: Math.max(0, n.start_beat + offset) };
})]);
showToast('Đã paste ' + clipboardNotes.length + ' nốt tại playhead.', 'success');
return;
}
}
@@ -7158,7 +7165,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
};
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, [notes, setSelectedNoteIds, clipboardNotes]);
}, [notes, setSelectedNoteIds, clipboardNotes, selectedNoteIds]);
// Undo/redo stacks
const undoStackRef = React.useRef([]);
@@ -8242,6 +8249,19 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
}
}
setDraggedNote(null);
// Marquee (Ctrl+drag): CHN notes nm trong vùng (user 09:15 trưc đây
// ch v highlight, không set selectedNoteIds Ctrl+X báo "chn notes")
if (selectionMarquee) {
const minBeat = Math.min(selectionMarquee.startBeat, selectionMarquee.currentBeat);
const maxBeat = Math.max(selectionMarquee.startBeat, selectionMarquee.currentBeat);
const minPitch = Math.min(selectionMarquee.startPitch, selectionMarquee.currentPitch);
const maxPitch = Math.max(selectionMarquee.startPitch, selectionMarquee.currentPitch);
const inMarquee = notes.filter(n => {
const center = n.start_beat + n.duration_beats / 2;
return center >= minBeat && center <= maxBeat && n.pitch >= minPitch && n.pitch <= maxPitch;
});
setSelectedNoteIds(inMarquee.map(n => n.id));
}
setSelectionMarquee(null);
rightClickDragRef.current = { active: false, startX: 0, startY: 0 };
if (brushAutoScrollRef.current) {
@@ -16574,7 +16594,7 @@ const App = () => {
handleSubTabLoop(curTabId, 4);
return;
}
if (e.key === 'v' || e.key === 'V') {
if (!ctrl && !e.metaKey && !e.altKey && (e.key === 'v' || e.key === 'V')) {
e.preventDefault();
const val = prompt("Nhập Gain điều chỉnh (dB):", "0");
if (val) handleSubTabGain(curTabId, parseFloat(val) || 0);