feat: added zoom-in/out feature for piano roll window.
This commit is contained in:
@@ -315,3 +315,31 @@
|
||||
justify-content: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.piano-roll-zoom-popup {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
background-color: #2d2d2d;
|
||||
border: 1px solid #444;
|
||||
border-radius: 3px;
|
||||
padding: 8px 12px;
|
||||
z-index: 1500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
white-space: nowrap;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.piano-roll-zoom-popup input[type="range"] {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.piano-roll-zoom-value {
|
||||
font-size: 12px;
|
||||
color: #e0e0e0;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -50,6 +50,9 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
||||
const [spectrogramThresholdDb, setSpectrogramThresholdDb] = useState<number>(-25);
|
||||
const [spectrogramPower, setSpectrogramPower] = useState<number>(0.5);
|
||||
|
||||
// Piano roll zoom (1x–8x); updates --region-grid-beat-width CSS variable
|
||||
const [pianoRollZoom, setPianoRollZoom] = useState<number>(1);
|
||||
|
||||
// Quantization state
|
||||
const [quantPosition, setQuantPosition] = useState<string>('1/8');
|
||||
const [quantLength, setQuantLength] = useState<string>('1/8');
|
||||
@@ -686,6 +689,17 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
||||
container.scrollLeft = clampedScrollLeft;
|
||||
}, [playheadPosition, isPlaying, autoScrollEnabled]);
|
||||
|
||||
// Update --region-grid-beat-width when zoom changes; reset on unmount
|
||||
useEffect(() => {
|
||||
document.documentElement.style.setProperty('--region-grid-beat-width', `${40 * pianoRollZoom}px`);
|
||||
if (triggerNoteUpdateRef.current) {
|
||||
triggerNoteUpdateRef.current(prev => prev + 1);
|
||||
}
|
||||
return () => {
|
||||
document.documentElement.style.setProperty('--region-grid-beat-width', '40px');
|
||||
};
|
||||
}, [pianoRollZoom]);
|
||||
|
||||
// Scroll horizontally to the active region's starting bar
|
||||
useEffect(() => {
|
||||
if (pianoRollContentRef.current && activeRegion) {
|
||||
@@ -927,6 +941,8 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
||||
onThresholdChange={setSpectrogramThresholdDb}
|
||||
power={spectrogramPower}
|
||||
onPowerChange={setSpectrogramPower}
|
||||
zoom={pianoRollZoom}
|
||||
onZoomChange={setPianoRollZoom}
|
||||
/>
|
||||
|
||||
<PianoRollContent
|
||||
|
||||
@@ -29,6 +29,8 @@ interface PianoRollToolbarProps {
|
||||
onThresholdChange?: (db: number) => void;
|
||||
power?: number;
|
||||
onPowerChange?: (power: number) => void;
|
||||
zoom: number;
|
||||
onZoomChange: (value: number) => void;
|
||||
}
|
||||
|
||||
const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
||||
@@ -49,11 +51,27 @@ const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
||||
onThresholdChange,
|
||||
power = 0.5,
|
||||
onPowerChange,
|
||||
zoom,
|
||||
onZoomChange,
|
||||
}) => {
|
||||
const isSpectrogram = mode === 'spectrogram';
|
||||
const showMidiControls = mode !== 'spectrogram'; // midi-edit and hybrid
|
||||
const showSpecControls = mode === 'spectrogram' || mode === 'hybrid';
|
||||
|
||||
const [showZoomSlider, setShowZoomSlider] = React.useState(false);
|
||||
const zoomSliderRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!showZoomSlider) return;
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
if (zoomSliderRef.current && !zoomSliderRef.current.contains(e.target as Node)) {
|
||||
setShowZoomSlider(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, [showZoomSlider]);
|
||||
|
||||
return (
|
||||
<div className="piano-roll-toolbar">
|
||||
{showMidiControls && (
|
||||
@@ -148,6 +166,29 @@ const PianoRollToolbar: React.FC<PianoRollToolbarProps> = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="quant-dropdown-container" ref={zoomSliderRef}>
|
||||
<button
|
||||
className="quant-button"
|
||||
onClick={() => setShowZoomSlider(!showZoomSlider)}
|
||||
title="Zoom"
|
||||
>
|
||||
{zoom}x
|
||||
</button>
|
||||
{showZoomSlider && (
|
||||
<div className="piano-roll-zoom-popup">
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="8"
|
||||
step="1"
|
||||
value={zoom}
|
||||
onChange={(e) => onZoomChange(parseInt(e.target.value))}
|
||||
/>
|
||||
<span className="piano-roll-zoom-value">{zoom}x</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user