fix: brush pitch-based scroll + CC velocity per-note paint

This commit is contained in:
2026-07-26 10:14:40 +07:00
parent 9237d01f35
commit 8ec4b05e21
3 changed files with 53 additions and 43 deletions
+45 -41
View File
@@ -5240,25 +5240,27 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
const container = gridScrollRef.current;
if (container) {
const cr = container.getBoundingClientRect();
const edgeThreshold = 30;
const scrollStep = 6;
if (e.clientY < cr.top + edgeThreshold) {
const visTop = container.scrollTop;
const visBot = visTop + container.clientHeight;
const pitchPixel = (127 - snappedPitch) * NoteHeight;
const pitchThreshold = NoteHeight * 6;
if (pitchPixel < visTop + pitchThreshold) {
if (!brushAutoScrollRef.current || brushAutoScrollRef.current.direction !== 'up') {
if (brushAutoScrollRef.current) clearInterval(brushAutoScrollRef.current.id);
brushAutoScrollRef.current = {
direction: 'up',
id: setInterval(() => {
if (gridScrollRef.current) gridScrollRef.current.scrollTop = Math.max(0, gridScrollRef.current.scrollTop - scrollStep);
if (gridScrollRef.current) gridScrollRef.current.scrollTop = Math.max(0, gridScrollRef.current.scrollTop - NoteHeight * 2);
}, 30)
};
}
} else if (e.clientY > cr.bottom - edgeThreshold) {
} else if (pitchPixel + NoteHeight > visBot - pitchThreshold) {
if (!brushAutoScrollRef.current || brushAutoScrollRef.current.direction !== 'down') {
if (brushAutoScrollRef.current) clearInterval(brushAutoScrollRef.current.id);
brushAutoScrollRef.current = {
direction: 'down',
id: setInterval(() => {
if (gridScrollRef.current) gridScrollRef.current.scrollTop = Math.min(gridScrollRef.current.scrollHeight - gridScrollRef.current.clientHeight, gridScrollRef.current.scrollTop + scrollStep);
if (gridScrollRef.current) gridScrollRef.current.scrollTop = Math.min(gridScrollRef.current.scrollHeight - gridScrollRef.current.clientHeight, gridScrollRef.current.scrollTop + NoteHeight * 2);
}, 30)
};
}
@@ -5399,22 +5401,25 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
if (e.ctrlKey) {
if (selectedNoteIds.length > 0) {
const updates = {};
selectedNoteIds.forEach(id => {
const idx = notes.findIndex(n => n.id === id);
if (idx !== -1) updates[idx] = val;
});
const idxs = Object.keys(updates).map(Number);
if (idxs.length > 0) {
setNotes(prev => prev.map((n, i) => {
if (i in updates) {
if (ccMode === 'pan') return { ...n, pan: (updates[i] - 0.5) * 2.0 };
return { ...n, velocity: updates[i] };
}
return n;
}));
let cursorNoteIdx = notes.findIndex(n => beat >= n.start_beat && beat <= n.start_beat + n.duration_beats);
if (cursorNoteIdx === -1) {
let minDist = Infinity;
notes.forEach((n, idx) => {
const center = n.start_beat + n.duration_beats / 2;
const dist = Math.abs(center - beat);
if (dist < minDist) { minDist = dist; cursorNoteIdx = idx; }
});
}
if (cursorNoteIdx !== -1 && selectedNoteIds.includes(notes[cursorNoteIdx] ? notes[cursorNoteIdx].id : -1)) {
setNotes(prev => prev.map((n, i) => {
if (i !== cursorNoteIdx) return n;
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
return { ...n, velocity: val };
}));
ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: [cursorNoteIdx] };
} else {
ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: [] };
}
ccDragRef.current = { active: true, lastBeat: beat, selectedMode: true, lastPainted: selectedNoteIds.map(id => notes.findIndex(n => n.id === id)).filter(i => i !== -1) };
} else {
if (noteIdx !== -1) paintNote(noteIdx, val);
ccDragRef.current = { active: true, lastBeat: beat, lastPainted: noteIdx !== -1 ? [noteIdx] : [] };
@@ -5440,27 +5445,26 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
const painted = drag.lastPainted || [];
if (drag.selectedMode && selectedNoteIds.length > 0) {
const updates = {};
selectedNoteIds.forEach(id => {
const idx = notes.findIndex(n => n.id === id);
if (idx !== -1 && !painted.includes(idx)) {
updates[idx] = val;
}
});
const idxs = Object.keys(updates).map(Number);
if (idxs.length > 0) {
setNotes(prev => prev.map((n, i) => {
if (i in updates) {
if (ccMode === 'pan') return { ...n, pan: (updates[i] - 0.5) * 2.0 };
return { ...n, velocity: updates[i] };
}
return n;
}));
let cursorNoteIdx = notes.findIndex(n => beat >= n.start_beat && beat <= n.start_beat + n.duration_beats);
if (cursorNoteIdx === -1) {
let minDist = Infinity;
notes.forEach((n, idx) => {
const center = n.start_beat + n.duration_beats / 2;
const dist = Math.abs(center - beat);
if (dist < minDist) { minDist = dist; cursorNoteIdx = idx; }
});
}
if (cursorNoteIdx !== -1) {
const cursorNote = notes[cursorNoteIdx];
if (cursorNote && selectedNoteIds.includes(cursorNote.id) && !painted.includes(cursorNoteIdx)) {
setNotes(prev => prev.map((n, i) => {
if (i !== cursorNoteIdx) return n;
if (ccMode === 'pan') return { ...n, pan: (val - 0.5) * 2.0 };
return { ...n, velocity: val };
}));
drag.lastPainted = [...painted, cursorNoteIdx];
}
}
const newPainted = selectedNoteIds
.map(id => notes.findIndex(n => n.id === id))
.filter(i => i !== -1 && !painted.includes(i));
ccDragRef.current.lastPainted = [...painted, ...newPainted];
return;
}
File diff suppressed because one or more lines are too long