feat: added shortcut for split and merge regions
This commit is contained in:
+15
-119
@@ -16,14 +16,11 @@ import {
|
||||
} from 'react-icons/fa';
|
||||
import { KGProject, type KeySignature } from '../core/KGProject';
|
||||
import { KGMidiInput } from '../core/midi-input/KGMidiInput';
|
||||
import { KGMidiRegion } from '../core/region/KGMidiRegion';
|
||||
import { KGAudioTrack } from '../core/track/KGAudioTrack';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { FaPencil, FaCopy, FaPaste, FaTrash, FaWandMagicSparkles, FaListUl } from 'react-icons/fa6';
|
||||
import { KGMainContentState } from '../core/state/KGMainContentState';
|
||||
import { regionDeleteManager } from '../util/regionDeleteUtil';
|
||||
import { SplitRegionCommand } from '../core/commands/region/SplitRegionCommand';
|
||||
import { MergeMidiRegionsCommand } from '../core/commands/region/MergeMidiRegionsCommand';
|
||||
import { handleCopyOperation, handlePasteOperation } from '../util/copyPasteUtil';
|
||||
import { convertProjectToMidi, convertMidiToProject } from '../util/midiUtil';
|
||||
import { KEY_SIGNATURE_MAP } from '../constants/coreConstants';
|
||||
@@ -35,7 +32,7 @@ import OpenProjectModal from './common/OpenProjectModal';
|
||||
import { clearChatHistoryAndUI } from '../util/chatUtil';
|
||||
import PianoIcon from './common/icons/PianoIcon';
|
||||
import MetronomeIcon from './common/icons/MetronomeIcon';
|
||||
import { ConfigManager } from '../core/config/ConfigManager';
|
||||
import { mergeSelectedMidiRegions, splitSelectedRegionAtPlayhead } from '../util/regionEditUtil';
|
||||
import { showAlert, showChoice, showConfirm, showPrompt, showTimeSigPrompt } from '../util/dialogUtil';
|
||||
|
||||
const Toolbar: React.FC = () => {
|
||||
@@ -791,43 +788,19 @@ const Toolbar: React.FC = () => {
|
||||
console.log("Split button clicked");
|
||||
}
|
||||
|
||||
if (selectedRegionIds.length === 0) {
|
||||
await showAlert("Please select a region to split.");
|
||||
return;
|
||||
}
|
||||
if (selectedRegionIds.length > 1) {
|
||||
await showAlert("Please select exactly one region to split.");
|
||||
const status = await splitSelectedRegionAtPlayhead({
|
||||
selectedRegionIds,
|
||||
playheadPosition,
|
||||
refreshProjectState,
|
||||
});
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
const regionId = lastSelectedRegionId;
|
||||
const tracks = KGCore.instance().getCurrentProject().getTracks();
|
||||
let targetRegion = null;
|
||||
for (const track of tracks) {
|
||||
const found = track.getRegions().find(r => r.getId() === regionId);
|
||||
if (found) { targetRegion = found; break; }
|
||||
}
|
||||
|
||||
if (!targetRegion) {
|
||||
await showAlert("Selected region not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
const regionStart = targetRegion.getStartFromBeat();
|
||||
const regionEnd = regionStart + targetRegion.getLength();
|
||||
|
||||
if (playheadPosition <= regionStart || playheadPosition >= regionEnd) {
|
||||
await showAlert("The playhead is not inside the selected region. Move the playhead inside the region before splitting.");
|
||||
return;
|
||||
}
|
||||
|
||||
const command = new SplitRegionCommand(regionId, playheadPosition);
|
||||
KGCore.instance().executeCommand(command);
|
||||
refreshProjectState();
|
||||
setStatus(`Split region at beat ${playheadPosition.toFixed(2)}`);
|
||||
setStatus(status);
|
||||
|
||||
if (DEBUG_MODE.TOOLBAR) {
|
||||
console.log(`Split region ${regionId} at beat ${playheadPosition}`);
|
||||
console.log(`Split selected region at beat ${playheadPosition}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -836,92 +809,15 @@ const Toolbar: React.FC = () => {
|
||||
console.log('Merge button clicked');
|
||||
}
|
||||
|
||||
if (selectedRegionIds.length < 2) {
|
||||
await showAlert('Please select at least two MIDI regions on the same track to merge.');
|
||||
return;
|
||||
}
|
||||
|
||||
const tracks = KGCore.instance().getCurrentProject().getTracks();
|
||||
const selectedRegionIdSet = new Set(selectedRegionIds);
|
||||
const selectedMidiRegions: KGMidiRegion[] = [];
|
||||
let targetTrackId: string | null = null;
|
||||
|
||||
for (const track of tracks) {
|
||||
for (const region of track.getRegions()) {
|
||||
if (!selectedRegionIdSet.has(region.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(region instanceof KGMidiRegion)) {
|
||||
await showAlert('Only MIDI regions can be merged. Please adjust your selection and try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
const regionTrackId = track.getId().toString();
|
||||
if (targetTrackId && targetTrackId !== regionTrackId) {
|
||||
await showAlert('Please select only MIDI regions from a single track before merging.');
|
||||
return;
|
||||
}
|
||||
|
||||
targetTrackId = regionTrackId;
|
||||
selectedMidiRegions.push(region);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedMidiRegions.length !== selectedRegionIds.length || !targetTrackId) {
|
||||
await showAlert('Some selected regions could not be found. Please reselect the MIDI regions and try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
const sortedSelectedRegions = [...selectedMidiRegions].sort((a, b) => {
|
||||
const startDelta = a.getStartFromBeat() - b.getStartFromBeat();
|
||||
if (startDelta !== 0) return startDelta;
|
||||
return a.getLength() - b.getLength();
|
||||
const status = await mergeSelectedMidiRegions({
|
||||
selectedRegionIds,
|
||||
refreshProjectState,
|
||||
});
|
||||
|
||||
let regionIdsToMerge = selectedRegionIds;
|
||||
const firstSelectedRegion = sortedSelectedRegions[0];
|
||||
const lastSelectedRegion = sortedSelectedRegions[sortedSelectedRegions.length - 1];
|
||||
const spanStart = firstSelectedRegion.getStartFromBeat();
|
||||
const spanEnd = lastSelectedRegion.getStartFromBeat() + lastSelectedRegion.getLength();
|
||||
|
||||
const targetTrack = tracks.find(track => track.getId().toString() === targetTrackId);
|
||||
const inBetweenRegions = targetTrack
|
||||
?.getRegions()
|
||||
.filter(region => (
|
||||
region instanceof KGMidiRegion &&
|
||||
!selectedRegionIdSet.has(region.getId()) &&
|
||||
region.getStartFromBeat() >= spanStart &&
|
||||
region.getStartFromBeat() <= spanEnd
|
||||
)) ?? [];
|
||||
|
||||
if (inBetweenRegions.length > 0) {
|
||||
const shouldIncludeInBetweenRegions = await showConfirm(
|
||||
'There are additional MIDI regions between the first and last selected regions on this track. Would you like KGStudio to merge those as well?',
|
||||
{
|
||||
confirmLabel: 'Merge All In Between',
|
||||
cancelLabel: 'Stop',
|
||||
}
|
||||
);
|
||||
|
||||
if (!shouldIncludeInBetweenRegions) {
|
||||
return;
|
||||
}
|
||||
|
||||
regionIdsToMerge = Array.from(new Set([
|
||||
...selectedRegionIds,
|
||||
...inBetweenRegions.map(region => region.getId()),
|
||||
]));
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const command = new MergeMidiRegionsCommand(regionIdsToMerge);
|
||||
KGCore.instance().executeCommand(command, { rethrow: true });
|
||||
refreshProjectState();
|
||||
setStatus(`Merged ${regionIdsToMerge.length} MIDI regions`);
|
||||
} catch (error) {
|
||||
await showAlert(error instanceof Error ? error.message : 'Unable to merge the selected MIDI regions.');
|
||||
}
|
||||
setStatus(status);
|
||||
};
|
||||
|
||||
// Handle undo button click
|
||||
|
||||
Reference in New Issue
Block a user