fix: minor updated the region lasso selection behavior

This commit is contained in:
Xiaohan-Tian
2026-05-04 18:34:29 -07:00
parent f323f1a6b5
commit a2f12ca961
3 changed files with 51 additions and 3 deletions
+11
View File
@@ -94,6 +94,7 @@ const MainContent: React.FC<MainContentProps> = ({
// Refs to track pending updates for verification // Refs to track pending updates for verification
const pendingUpdates = useRef<Map<string, { trackId: string, regionId: string, startBeat: number, length: number }>>(new Map()); const pendingUpdates = useRef<Map<string, { trackId: string, regionId: string, startBeat: number, length: number }>>(new Map());
const preventEmptyMainContentDeselectRef = useRef(false);
// Refs for auto-scroll during playback // Refs for auto-scroll during playback
const mainContentRef = useRef<HTMLDivElement | null>(null); const mainContentRef = useRef<HTMLDivElement | null>(null);
@@ -632,9 +633,18 @@ const MainContent: React.FC<MainContentProps> = ({
return; return;
} }
if (preventEmptyMainContentDeselectRef.current) {
preventEmptyMainContentDeselectRef.current = false;
return;
}
handleRegionLassoSelection([], { shiftKey: false }); handleRegionLassoSelection([], { shiftKey: false });
}; };
const handleRegionLassoCommit = () => {
preventEmptyMainContentDeselectRef.current = true;
};
// Handle region single click: selection only (no piano roll opening) // Handle region single click: selection only (no piano roll opening)
const handleRegionClick = (regionId: string, options: RegionClickOptions = { shiftKey: false }) => { const handleRegionClick = (regionId: string, options: RegionClickOptions = { shiftKey: false }) => {
if (DEBUG_MODE.MAIN_CONTENT) { if (DEBUG_MODE.MAIN_CONTENT) {
@@ -990,6 +1000,7 @@ const MainContent: React.FC<MainContentProps> = ({
onRegionUpdated={handleRegionUpdated} onRegionUpdated={handleRegionUpdated}
onRegionClick={handleRegionClick} onRegionClick={handleRegionClick}
onRegionLassoSelection={handleRegionLassoSelection} onRegionLassoSelection={handleRegionLassoSelection}
onRegionLassoCommit={handleRegionLassoCommit}
onOpenPianoRoll={handleOpenPianoRoll} onOpenPianoRoll={handleOpenPianoRoll}
onOpenSpectrogram={handleOpenSpectrogram} onOpenSpectrogram={handleOpenSpectrogram}
showHybridButtonForAudio={showHybridButtonForAudio} showHybridButtonForAudio={showHybridButtonForAudio}
@@ -119,6 +119,17 @@ describe('TrackGridPanel lasso selection', () => {
expect(onRegionLassoSelection).toHaveBeenCalledWith(['region-b', 'region-a'], { shiftKey: false }); expect(onRegionLassoSelection).toHaveBeenCalledWith(['region-b', 'region-a'], { shiftKey: false });
}); });
it('uses the closest intersected region as primary when release is outside all regions', () => {
const { container, onRegionLassoSelection } = renderPanel();
const firstTrackGrid = container.querySelector('[data-test-id="track-grid-1"]') as HTMLDivElement;
fireEvent.mouseDown(firstTrackGrid, { clientX: 10, clientY: 10, button: 0 });
fireEvent.mouseMove(document, { clientX: 150, clientY: 170 });
fireEvent.mouseUp(document, { clientX: 150, clientY: 170 });
expect(onRegionLassoSelection).toHaveBeenCalledWith(['region-a', 'region-b'], { shiftKey: false });
});
it('clears selection on a plain empty-space click', () => { it('clears selection on a plain empty-space click', () => {
const { container, onRegionLassoSelection } = renderPanel(); const { container, onRegionLassoSelection } = renderPanel();
const firstTrackGrid = container.querySelector('[data-test-id="track-grid-1"]') as HTMLDivElement; const firstTrackGrid = container.querySelector('[data-test-id="track-grid-1"]') as HTMLDivElement;
+29 -3
View File
@@ -32,6 +32,7 @@ interface TrackGridPanelProps {
onRegionUpdated?: (regionId: string, updates: Partial<RegionUI>, expectedModelUpdates?: { startBeat: number, length: number }) => void; onRegionUpdated?: (regionId: string, updates: Partial<RegionUI>, expectedModelUpdates?: { startBeat: number, length: number }) => void;
onRegionClick?: (regionId: string, options: RegionClickOptions) => void; onRegionClick?: (regionId: string, options: RegionClickOptions) => void;
onRegionLassoSelection?: (regionIds: string[], options: RegionClickOptions) => void; onRegionLassoSelection?: (regionIds: string[], options: RegionClickOptions) => void;
onRegionLassoCommit?: () => void;
onOpenPianoRoll?: (regionId: string) => void; onOpenPianoRoll?: (regionId: string) => void;
onOpenSpectrogram?: (regionId: string) => void; onOpenSpectrogram?: (regionId: string) => void;
showHybridButtonForAudio?: boolean; showHybridButtonForAudio?: boolean;
@@ -53,6 +54,7 @@ const TrackGridPanel: React.FC<TrackGridPanelProps> = ({
onRegionUpdated, onRegionUpdated,
onRegionClick, onRegionClick,
onRegionLassoSelection, onRegionLassoSelection,
onRegionLassoCommit,
onOpenPianoRoll, onOpenPianoRoll,
onOpenSpectrogram, onOpenSpectrogram,
showHybridButtonForAudio, showHybridButtonForAudio,
@@ -160,12 +162,36 @@ const TrackGridPanel: React.FC<TrackGridPanelProps> = ({
}); });
const intersectedRegionIds = intersectedRegions.map(region => region.id); const intersectedRegionIds = intersectedRegions.map(region => region.id);
if (releaseRegionIndex > -1) { if (intersectedRegionIds.length > 0) {
const [releaseRegionId] = intersectedRegionIds.splice(releaseRegionIndex, 1); const primaryRegionIndex = releaseRegionIndex > -1
intersectedRegionIds.push(releaseRegionId); ? releaseRegionIndex
: intersectedRegions.reduce((closestIndex, region, index, allRegions) => {
const regionLeft = (region.barNumber - 1) * barWidth;
const regionRight = regionLeft + (region.length * barWidth);
const regionTop = region.trackIndex * 120;
const regionBottom = regionTop + 120;
const regionCenterX = (regionLeft + regionRight) / 2;
const regionCenterY = (regionTop + regionBottom) / 2;
const regionDistance = Math.hypot(releasePointX - regionCenterX, releasePointY - regionCenterY);
const closestRegion = allRegions[closestIndex];
const closestLeft = (closestRegion.barNumber - 1) * barWidth;
const closestRight = closestLeft + (closestRegion.length * barWidth);
const closestTop = closestRegion.trackIndex * 120;
const closestBottom = closestTop + 120;
const closestCenterX = (closestLeft + closestRight) / 2;
const closestCenterY = (closestTop + closestBottom) / 2;
const closestDistance = Math.hypot(releasePointX - closestCenterX, releasePointY - closestCenterY);
return regionDistance < closestDistance ? index : closestIndex;
}, 0);
const [primaryRegionId] = intersectedRegionIds.splice(primaryRegionIndex, 1);
intersectedRegionIds.push(primaryRegionId);
} }
onRegionLassoSelection?.(intersectedRegionIds, { shiftKey: isLassoShiftPressedRef.current }); onRegionLassoSelection?.(intersectedRegionIds, { shiftKey: isLassoShiftPressedRef.current });
onRegionLassoCommit?.();
} }
isLassoSelectingRef.current = false; isLassoSelectingRef.current = false;