feat: per-MIDI-item focus brightness in piano roll

This commit is contained in:
2026-07-31 09:59:14 +07:00
parent 698fa818e9
commit 223254eb07
3 changed files with 41 additions and 16 deletions
+35 -11
View File
@@ -6256,15 +6256,38 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
ctx.stroke();
}
// Layer 2: Ghost Notes (same-track = lighter reference, other tracks = faint)
// Determine which MIDI item is focused (selected note opened item; playing item under playhead)
var focusedItemId = st.target_id;
if (selectedNoteIds && selectedNoteIds.length > 0) {
focusedItemId = st.target_id;
} else if (st.isPlaying && st.currentTime != null && activeTracks) {
var curSec = st.currentTime || 0;
var fidxTrk = activeTracks.find(function(t) { return t.id === st.trackId; });
var itemsArr = fidxTrk ? (fidxTrk.midiItems || []) : [];
for (var fi = 0; fi < itemsArr.length; fi++) {
var fit = itemsArr[fi];
var fStart = fit.startTime || 0;
var fEnd = fStart + (fit.duration || 4);
if (curSec >= fStart && curSec < fEnd) { focusedItemId = fit.id; break; }
}
}
// Layer 2: Ghost Notes (same-track: focused item bright, siblings dim; other tracks faint)
if (showGhostNotes && sessionSyncMode && ghostLayers.length > 0) {
ghostLayers.forEach(function(layer) {
ctx.save();
var isSameTrackLayer = layer.isSameTrack;
ctx.globalAlpha = isSameTrackLayer ? 0.35 : 0.25;
ctx.fillStyle = isSameTrackLayer ? 'rgba(251, 191, 36, 0.35)' : (layer.track_color || '#888');
ctx.strokeStyle = isSameTrackLayer ? 'rgba(245, 158, 11, 0.6)' : (layer.track_color || '#888');
layer.notes.forEach(function(note) {
var isFocusedNote = isSameTrackLayer && note.item_id === focusedItemId;
if (isSameTrackLayer) {
ctx.globalAlpha = isFocusedNote ? 0.7 : 0.3;
ctx.fillStyle = isFocusedNote ? 'rgba(253, 224, 71, 0.7)' : 'rgba(251, 191, 36, 0.3)';
ctx.strokeStyle = isFocusedNote ? '#fde047' : 'rgba(245, 158, 11, 0.6)';
} else {
ctx.globalAlpha = 0.25;
ctx.fillStyle = layer.track_color || '#888';
ctx.strokeStyle = layer.track_color || '#888';
}
var snapStart = snapValue !== 'free' ? getSnapBeat(note.relative_start_beat, snapValue) : note.relative_start_beat;
var rawEnd = note.relative_start_beat + note.duration_beats;
var snapEnd = snapValue !== 'free' ? getSnapBeat(rawEnd, snapValue) : rawEnd;
@@ -6273,7 +6296,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
var w = Math.max(2, (snapEnd - snapStart) * pixelsPerBeat);
var h = NoteHeight - 1;
ctx.fillRect(x, y, w, h);
if (isSameTrackLayer) ctx.strokeRect(x + 0.5, y + 0.5, w - 1, h - 1);
if (isSameTrackLayer && isFocusedNote) ctx.strokeRect(x + 0.5, y + 0.5, w - 1, h - 1);
});
ctx.restore();
});
@@ -6289,18 +6312,19 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
const w = Math.max(2, (snapEnd - snapStart) * pixelsPerBeat);
const isSelected = selectedNoteIds.includes(note.id);
// Draw background of note (focused item = brighter)
// Draw background of note (focused item = brighter, siblings = dim)
const playingNow = st.isPlaying;
ctx.fillStyle = isSelected ? 'rgba(96, 165, 250, 0.6)' : (playingNow ? 'rgba(254, 240, 138, 0.75)' : 'rgba(253, 224, 71, 0.6)');
ctx.strokeStyle = isSelected ? '#60a5fa' : (playingNow ? '#fef08a' : '#fde047');
ctx.lineWidth = isSelected ? 1.5 : 1.2;
const mainFocused = focusedItemId === st.target_id;
ctx.fillStyle = isSelected ? 'rgba(96, 165, 250, 0.6)' : (mainFocused ? (playingNow ? 'rgba(254, 240, 138, 0.75)' : 'rgba(253, 224, 71, 0.6)') : 'rgba(120, 100, 40, 0.35)');
ctx.strokeStyle = isSelected ? '#60a5fa' : (mainFocused ? (playingNow ? '#fef08a' : '#fde047') : '#8a7504');
ctx.lineWidth = isSelected ? 1.5 : (mainFocused ? 1.2 : 0.8);
ctx.fillRect(x + 1, y + 1, w - 2, NoteHeight - 2);
ctx.strokeRect(x + 1, y + 1, w - 2, NoteHeight - 2);
// Draw velocity layer (solid yellow/blue bar inside, proportional to velocity)
const vel = note.velocity !== undefined ? note.velocity : 0.8;
const velW = Math.max(2, (w - 2) * vel);
ctx.fillStyle = isSelected ? '#60a5fa' : (playingNow ? '#fde047' : '#facc15');
ctx.fillStyle = isSelected ? '#60a5fa' : (mainFocused ? (playingNow ? '#fde047' : '#facc15') : '#7a6a10');
ctx.fillRect(x + 1, y + 1, velW, NoteHeight - 2);
});
@@ -6358,7 +6382,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]);
}, [notes, snapValue, rollZoom, selectedNoteIds, selectionMarquee, st.currentTime, bpm, viewWidth, viewBeats, recordingState, recTempMidiNotes, showGhostNotes, sessionSyncMode, ghostLayers, renderBeatOffset, renderTick, activeTracks]);
React.useLayoutEffect(() => {
const canvas = ccCanvasRef.current;