FIX: cho phép copy/cut các notes được chọn trong PIANO ROLL TAB
This commit is contained in:
+24
-4
@@ -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 bắt đầu tại vị trí CON TRỎ PLAYHEAD (user 09:25) — bù offset
|
||||
// sao cho note đầu tiên của clipboard nằm đú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): CHỌN notes nằm trong vùng (user 09:15 — trước đây
|
||||
// chỉ vẽ highlight, không set selectedNoteIds → Ctrl+X báo "chọn 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);
|
||||
|
||||
Reference in New Issue
Block a user