fix: Chỉnh sửa các hành động của vẽ các note midi trong piano roll tab
This commit is contained in:
+54
-17
@@ -4467,11 +4467,19 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
} else if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
|
} else if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleRedo();
|
handleRedo();
|
||||||
|
} else if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||||
|
if (selectedNoteIds.length > 0 && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
|
||||||
|
e.preventDefault();
|
||||||
|
pushToUndo(notes);
|
||||||
|
setNotes(prev => prev.filter(n => !selectedNoteIds.includes(n.id)));
|
||||||
|
setSelectedNoteIds([]);
|
||||||
|
showToast(`Đã xóa ${selectedNoteIds.length} nốt!`, 'info');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener('keydown', handler);
|
window.addEventListener('keydown', handler);
|
||||||
return () => window.removeEventListener('keydown', handler);
|
return () => window.removeEventListener('keydown', handler);
|
||||||
}, [handleUndo, handleRedo]);
|
}, [handleUndo, handleRedo, notes, selectedNoteIds]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
onUpdateNotes(st.id, notes);
|
onUpdateNotes(st.id, notes);
|
||||||
@@ -4739,7 +4747,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
const beat = x / pixelsPerBeat;
|
const beat = x / pixelsPerBeat;
|
||||||
const pitch = 127 - Math.floor(y / NoteHeight);
|
const pitch = 127 - Math.floor(y / NoteHeight);
|
||||||
|
|
||||||
// Right click -> Quick delete note!
|
// Right click -> Quick delete note or start erase sweep
|
||||||
if (e.button === 2) {
|
if (e.button === 2) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const clickedNote = notes.find(n => {
|
const clickedNote = notes.find(n => {
|
||||||
@@ -4749,8 +4757,11 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
pushToUndo(notes);
|
pushToUndo(notes);
|
||||||
setNotes(prev => prev.filter(n => n.id !== clickedNote.id));
|
setNotes(prev => prev.filter(n => n.id !== clickedNote.id));
|
||||||
setSelectedNoteIds(prev => prev.filter(id => id !== clickedNote.id));
|
setSelectedNoteIds(prev => prev.filter(id => id !== clickedNote.id));
|
||||||
showToast('Đã xóa nốt nhanh!', 'info');
|
showToast('Đã xóa nốt!', 'info');
|
||||||
}
|
}
|
||||||
|
// Start erase sweep
|
||||||
|
notesBeforeDragRef.current = JSON.parse(JSON.stringify(notes));
|
||||||
|
setDraggedNote({ mode: 'erase_sweep', visitedPitches: [pitch] });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4769,12 +4780,32 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ctrl+click: duplicate or selection
|
// Ctrl+click: toggle selection (multi-select)
|
||||||
if (e.ctrlKey && !e.altKey) {
|
if (e.ctrlKey && !e.altKey && !e.shiftKey) {
|
||||||
|
if (clickedNoteIdx !== -1) {
|
||||||
|
const clickedNote = notes[clickedNoteIdx];
|
||||||
|
if (selectedNoteIds.includes(clickedNote.id)) {
|
||||||
|
setSelectedNoteIds(prev => prev.filter(id => id !== clickedNote.id));
|
||||||
|
} else {
|
||||||
|
setSelectedNoteIds(prev => [...prev, clickedNote.id]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Ctrl+click on empty space: start selection marquee
|
||||||
|
setSelectedNoteIds([]);
|
||||||
|
setSelectionMarquee({
|
||||||
|
startBeat: beat, startPitch: pitch,
|
||||||
|
currentBeat: beat, currentPitch: pitch
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl+Shift+click: duplicate selected + clicked notes
|
||||||
|
if (e.ctrlKey && e.shiftKey) {
|
||||||
if (clickedNoteIdx !== -1) {
|
if (clickedNoteIdx !== -1) {
|
||||||
pushToUndo(notes);
|
pushToUndo(notes);
|
||||||
const clickedNote = notes[clickedNoteIdx];
|
const clickedNote = notes[clickedNoteIdx];
|
||||||
// Duplicate: clone all selected notes + clicked note
|
|
||||||
const idsToClone = [...new Set([...selectedNoteIds, clickedNote.id])];
|
const idsToClone = [...new Set([...selectedNoteIds, clickedNote.id])];
|
||||||
const clones = notes.filter(n => idsToClone.includes(n.id)).map(n => ({
|
const clones = notes.filter(n => idsToClone.includes(n.id)).map(n => ({
|
||||||
...JSON.parse(JSON.stringify(n)),
|
...JSON.parse(JSON.stringify(n)),
|
||||||
@@ -4792,13 +4823,6 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
startOffsetPitch: pitch,
|
startOffsetPitch: pitch,
|
||||||
selectedNotesOffset: cloneOffsets
|
selectedNotesOffset: cloneOffsets
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// Ctrl+click on empty space: start selection marquee
|
|
||||||
setSelectedNoteIds([]);
|
|
||||||
setSelectionMarquee({
|
|
||||||
startBeat: beat, startPitch: pitch,
|
|
||||||
currentBeat: beat, currentPitch: pitch
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -4985,9 +5009,19 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (draggedNote.mode === 'erase_sweep') {
|
||||||
|
const erasePitches = draggedNote.visitedPitches || [];
|
||||||
|
if (!erasePitches.includes(pitch)) {
|
||||||
|
draggedNote.visitedPitches = [...erasePitches, pitch];
|
||||||
|
setNotes(prev => prev.filter(n => !(n.pitch === pitch && beat >= n.start_beat && beat < n.start_beat + n.duration_beats)));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (draggedNote.mode === 'scale') {
|
if (draggedNote.mode === 'scale') {
|
||||||
const newEnd = getSnapBeat(Math.max(draggedNote.firstStart + 0.125, beat), snapVal);
|
const newEnd = getSnapBeat(Math.max(draggedNote.firstStart + 0.125, beat), snapVal);
|
||||||
const scaleFactor = (newEnd - draggedNote.firstStart) / (draggedNote.originalEnd - draggedNote.firstStart);
|
const range = draggedNote.originalEnd - draggedNote.firstStart;
|
||||||
|
if (range <= 0) return;
|
||||||
|
const scaleFactor = (newEnd - draggedNote.firstStart) / range;
|
||||||
const ids = draggedNote.selectedNoteIds || [];
|
const ids = draggedNote.selectedNoteIds || [];
|
||||||
const firstStart = draggedNote.firstStart;
|
const firstStart = draggedNote.firstStart;
|
||||||
setNotes(prev => prev.map(n => {
|
setNotes(prev => prev.map(n => {
|
||||||
@@ -5012,9 +5046,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
} else if (draggedNote.mode === 'move') {
|
} else if (draggedNote.mode === 'move') {
|
||||||
const baseOriginal = draggedNote.selectedNotesOffset.find(o => o.id === notes[draggedNote.idx].id);
|
const firstOffset = draggedNote.selectedNotesOffset && draggedNote.selectedNotesOffset[0];
|
||||||
const deltaBeat = getSnapBeat(beat - draggedNote.startOffsetBeat, snapVal) - baseOriginal.originalStartBeat;
|
if (!firstOffset) return;
|
||||||
const deltaPitch = Math.round(pitch - draggedNote.startOffsetPitch) - baseOriginal.originalPitch;
|
const firstNote = notes.find(n => n.id === firstOffset.id);
|
||||||
|
if (!firstNote) return;
|
||||||
|
const deltaBeat = getSnapBeat(beat - draggedNote.startOffsetBeat, snapVal) - firstOffset.originalStartBeat;
|
||||||
|
const deltaPitch = Math.round(pitch - draggedNote.startOffsetPitch) - firstOffset.originalPitch;
|
||||||
|
|
||||||
setNotes(prev => prev.map(n => {
|
setNotes(prev => prev.map(n => {
|
||||||
const offset = draggedNote.selectedNotesOffset.find(o => o.id === n.id);
|
const offset = draggedNote.selectedNotesOffset.find(o => o.id === n.id);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
{"original_name": "test.sf2", "uuid": "49d84fdb-ba20-4e7b-b03e-a9e07303df24", "file": "49d84fdb-ba20-4e7b-b03e-a9e07303df24.sf2"}
|
||||||
Binary file not shown.
Reference in New Issue
Block a user