feat: click note focuses its MIDI item in piano roll

This commit is contained in:
2026-07-31 10:02:49 +07:00
parent 223254eb07
commit d81afba93d
2 changed files with 37 additions and 10 deletions
+31 -4
View File
@@ -5919,6 +5919,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
const [showGhostNotes, setShowGhostNotes] = React.useState(true);
const [sessionSyncMode, setSessionSyncMode] = React.useState(true);
const [activePlayTrackIds, setActivePlayTrackIds] = React.useState([]);
const [focusItemId, setFocusItemId] = React.useState(st.target_id);
const allMidiItems = React.useMemo(() => {
const result = [];
@@ -6256,8 +6257,8 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
ctx.stroke();
}
// Determine which MIDI item is focused (selected note opened item; playing item under playhead)
var focusedItemId = st.target_id;
// Determine which MIDI item is focused (clicked/selected note its item; playing item under playhead)
var focusedItemId = focusItemId || st.target_id;
if (selectedNoteIds && selectedNoteIds.length > 0) {
focusedItemId = st.target_id;
} else if (st.isPlaying && st.currentTime != null && activeTracks) {
@@ -6382,7 +6383,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
ctx.stroke();
}
}
}, [notes, snapValue, rollZoom, selectedNoteIds, selectionMarquee, st.currentTime, bpm, viewWidth, viewBeats, recordingState, recTempMidiNotes, showGhostNotes, sessionSyncMode, ghostLayers, renderBeatOffset, renderTick, activeTracks]);
}, [notes, snapValue, rollZoom, selectedNoteIds, selectionMarquee, st.currentTime, bpm, viewWidth, viewBeats, recordingState, recTempMidiNotes, showGhostNotes, sessionSyncMode, ghostLayers, renderBeatOffset, renderTick, activeTracks, focusItemId]);
React.useLayoutEffect(() => {
const canvas = ccCanvasRef.current;
@@ -6463,6 +6464,11 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
setSubTabs(function(prev) { return prev.map(function(s) { if (s.id !== st.id) return s; return Object.assign({}, s, { ghostPlayLayers: layers }); }); });
}, [ghostLayers, activePlayTrackIds, sessionSyncMode, showGhostNotes, st.id, activeTracks]);
// Reset item focus when the opened MIDI item changes
React.useEffect(function() {
setFocusItemId(st.target_id);
}, [st.id, st.target_id]);
const handleGridMouseDown = (e) => {
const canvas = canvasRef.current;
if (!canvas) return;
@@ -6618,7 +6624,8 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
}
if (clickedNoteIdx !== -1) {
// Click on existing note: drag-move
// Click on existing note: drag-move focus its MIDI item
setFocusItemId(st.target_id);
const clickedNote = notes[clickedNoteIdx];
let nextSelectedIds;
if (!selectedNoteIds.includes(clickedNote.id)) {
@@ -6645,6 +6652,26 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
clickedOriginalStartBeat: clickedNote.start_beat
});
} else {
// Click on a same-track ghost note focus that MIDI item (no drawing)
if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
var ghostHit = null;
for (var gi = 0; gi < ghostLayers.length; gi++) {
var gl = ghostLayers[gi];
if (!gl.isSameTrack || !gl.notes) continue;
for (var gn = 0; gn < gl.notes.length; gn++) {
var gnote = gl.notes[gn];
if (pitch === gnote.pitch && beat >= gnote.relative_start_beat && beat < gnote.relative_start_beat + (gnote.duration_beats || 1)) {
ghostHit = gnote;
break;
}
}
if (ghostHit) break;
}
if (ghostHit) {
setFocusItemId(ghostHit.item_id || st.target_id);
return;
}
}
// Click on empty space with pen tool: DRAW a new note (brush mode with visitedPitches)
pushToUndo(notes);
const start = getSnapBeat(beat, snapValue);