fix: Ctrl+S in piano roll + ruler range selection via drag
This commit is contained in:
+43
-2
@@ -4590,6 +4590,10 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
React.useEffect(() => { if (!draggedNoteRef.current) setNotes(st.notes || []); }, [st.notes]);
|
React.useEffect(() => { if (!draggedNoteRef.current) setNotes(st.notes || []); }, [st.notes]);
|
||||||
const brushVelocityRef = React.useRef(0.8);
|
const brushVelocityRef = React.useRef(0.8);
|
||||||
const [selectedNoteIds, setSelectedNoteIds] = React.useState([]);
|
const [selectedNoteIds, setSelectedNoteIds] = React.useState([]);
|
||||||
|
const [loopStartBeat, setLoopStartBeat] = React.useState(null);
|
||||||
|
const [loopEndBeat, setLoopEndBeat] = React.useState(null);
|
||||||
|
const [isLooping, setIsLooping] = React.useState(false);
|
||||||
|
const rulerDragRef = React.useRef(null);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const handler = (e) => {
|
const handler = (e) => {
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'a') {
|
if ((e.ctrlKey || e.metaKey) && e.key === 'a') {
|
||||||
@@ -4638,6 +4642,10 @@ 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.ctrlKey || e.metaKey) && e.key === 's') {
|
||||||
|
e.preventDefault();
|
||||||
|
onSaveNotes(st.id, st.trackId, st.target_id, notes);
|
||||||
|
showToast('Đã lưu MIDI notes', 'info');
|
||||||
} else if (e.key === 'Delete' || e.key === 'Backspace') {
|
} else if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||||
if (selectedNoteIds.length > 0 && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
|
if (selectedNoteIds.length > 0 && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -4650,7 +4658,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
};
|
};
|
||||||
window.addEventListener('keydown', handler);
|
window.addEventListener('keydown', handler);
|
||||||
return () => window.removeEventListener('keydown', handler);
|
return () => window.removeEventListener('keydown', handler);
|
||||||
}, [handleUndo, handleRedo, notes, selectedNoteIds]);
|
}, [handleUndo, handleRedo, notes, selectedNoteIds, onSaveNotes, showToast]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
onUpdateNotes(st.id, notes);
|
onUpdateNotes(st.id, notes);
|
||||||
@@ -5715,14 +5723,47 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
if (clickTime >= 0) {
|
if (clickTime >= 0) {
|
||||||
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s));
|
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s));
|
||||||
}
|
}
|
||||||
|
if (e.shiftKey) {
|
||||||
|
const beatSnap = Math.round(clickBeat / 4) * 4;
|
||||||
|
if (loopStartBeat === null) {
|
||||||
|
setLoopStartBeat(Math.max(0, beatSnap - 4));
|
||||||
|
setLoopEndBeat(Math.max(4, beatSnap));
|
||||||
|
} else {
|
||||||
|
setLoopEndBeat(Math.max(loopStartBeat + 4, beatSnap));
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rulerDragRef.current = { startX: e.clientX, startBeat: clickBeat, scrollLeft: e.currentTarget.scrollLeft };
|
||||||
|
},
|
||||||
|
onMouseMove: e => {
|
||||||
|
if (!rulerDragRef.current) return;
|
||||||
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left + rulerDragRef.current.scrollLeft;
|
||||||
|
const beat = Math.max(0, x / pixelsPerBeat);
|
||||||
|
if (Math.abs(e.clientX - rulerDragRef.current.startX) > 5) {
|
||||||
|
const sBeat = Math.max(0, Math.min(rulerDragRef.current.startBeat, beat));
|
||||||
|
const eBeat = Math.max(sBeat + 1, Math.max(rulerDragRef.current.startBeat, beat));
|
||||||
|
setLoopStartBeat(sBeat);
|
||||||
|
setLoopEndBeat(eBeat);
|
||||||
|
setIsLooping(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMouseUp: () => { rulerDragRef.current = null; },
|
||||||
|
onMouseLeave: () => { rulerDragRef.current = null; }
|
||||||
}, /*#__PURE__*/React.createElement("div", {
|
}, /*#__PURE__*/React.createElement("div", {
|
||||||
style: {
|
style: {
|
||||||
width: `${viewWidth}px`,
|
width: `${viewWidth}px`,
|
||||||
height: '100%'
|
height: '100%'
|
||||||
},
|
},
|
||||||
className: "relative h-full font-mono text-[9px] text-zinc-500 font-bold"
|
className: "relative h-full font-mono text-[9px] text-zinc-500 font-bold"
|
||||||
}, renderBarLabels()))), /*#__PURE__*/React.createElement("div", {
|
}, renderBarLabels(), loopStartBeat !== null && loopEndBeat !== null && loopEndBeat > loopStartBeat && /*#__PURE__*/React.createElement("div", {
|
||||||
|
style: {
|
||||||
|
left: `${loopStartBeat * pixelsPerBeat}px`,
|
||||||
|
width: `${(loopEndBeat - loopStartBeat) * pixelsPerBeat}px`,
|
||||||
|
top: 0, bottom: 0
|
||||||
|
},
|
||||||
|
className: "absolute bg-emerald-500/15 border-l border-r border-emerald-400 pointer-events-none"
|
||||||
|
}))), /*#__PURE__*/React.createElement("div", {
|
||||||
className: "flex-1 flex overflow-hidden min-h-0 relative"
|
className: "flex-1 flex overflow-hidden min-h-0 relative"
|
||||||
}, /*#__PURE__*/React.createElement("div", {
|
}, /*#__PURE__*/React.createElement("div", {
|
||||||
ref: keybedRef,
|
ref: keybedRef,
|
||||||
|
|||||||
Reference in New Issue
Block a user