Modify time changes in the metadata toolbox
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package funkin.ui.debug.charting.commands;
|
||||
|
||||
import funkin.data.song.SongData.SongTimeChange;
|
||||
|
||||
/**
|
||||
* A command which adds a new timechange to the current song's timechanges, after the currently selected timechange.
|
||||
*/
|
||||
@:nullSafety
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
class AddNewTimeChangeCommand implements ChartEditorCommand
|
||||
{
|
||||
var currentTimeChange:Int;
|
||||
|
||||
var previousTimeChanges:Null<Array<SongTimeChange>>;
|
||||
|
||||
public function new(currentTimeChange:Int)
|
||||
{
|
||||
this.currentTimeChange = currentTimeChange;
|
||||
}
|
||||
|
||||
public function execute(state:ChartEditorState):Void
|
||||
{
|
||||
var timeChanges:Array<SongTimeChange> = state.currentSongMetadata.timeChanges;
|
||||
previousTimeChanges = timeChanges;
|
||||
if (timeChanges == null || timeChanges.length == 0)
|
||||
{
|
||||
timeChanges = [new SongTimeChange(0, 100)];
|
||||
}
|
||||
else
|
||||
{
|
||||
timeChanges.insert(currentTimeChange + 1,
|
||||
new SongTimeChange(timeChanges[currentTimeChange].timeStamp + 1, timeChanges[currentTimeChange].bpm, timeChanges[currentTimeChange].timeSignatureNum,
|
||||
timeChanges[currentTimeChange].timeSignatureDen));
|
||||
}
|
||||
|
||||
state.currentSongMetadata.timeChanges = timeChanges;
|
||||
|
||||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
state.notePreviewViewportBoundsDirty = true;
|
||||
state.scrollPositionInPixels = 0;
|
||||
|
||||
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||
|
||||
state.updateGridHeight();
|
||||
}
|
||||
|
||||
public function undo(state:ChartEditorState):Void
|
||||
{
|
||||
if (previousTimeChanges == null)
|
||||
{
|
||||
previousTimeChanges = [new SongTimeChange(0, 100)];
|
||||
}
|
||||
|
||||
state.currentSongMetadata.timeChanges = previousTimeChanges;
|
||||
|
||||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
state.notePreviewViewportBoundsDirty = true;
|
||||
state.scrollPositionInPixels = 0;
|
||||
|
||||
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||
|
||||
state.updateGridHeight();
|
||||
}
|
||||
|
||||
public function shouldAddToHistory(state:ChartEditorState):Bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
return 'Added new TimeChange after TimeChange ${currentTimeChange}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package funkin.ui.debug.charting.commands;
|
||||
|
||||
import funkin.data.song.SongData.SongTimeChange;
|
||||
|
||||
/**
|
||||
* A command which modifies the current time change's bpm and/or timestamp in the song.
|
||||
* Note that this does not have any protection to prevent time changes from being set to invalid or troublesome values.
|
||||
*/
|
||||
@:nullSafety
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
class ModifyCurrentTimeChangeCommand implements ChartEditorCommand
|
||||
{
|
||||
var currentTimeChange:Int;
|
||||
|
||||
var targetBPM:Float;
|
||||
|
||||
var previousBPM:Float = 100;
|
||||
|
||||
var targetTimeStamp:Float;
|
||||
|
||||
var previousTimeStamp:Float = 0;
|
||||
|
||||
var previousTimeChanges:Null<Array<SongTimeChange>>;
|
||||
|
||||
public function new(currentTimeChange:Int, targetBPM:Float, targetTimeStamp:Float)
|
||||
{
|
||||
this.currentTimeChange = currentTimeChange;
|
||||
this.targetBPM = targetBPM;
|
||||
this.targetTimeStamp = targetTimeStamp;
|
||||
}
|
||||
|
||||
public function execute(state:ChartEditorState):Void
|
||||
{
|
||||
var timeChanges:Array<SongTimeChange> = state.currentSongMetadata.timeChanges;
|
||||
previousTimeChanges = timeChanges;
|
||||
if (timeChanges == null || timeChanges.length == 0)
|
||||
{
|
||||
previousBPM = 100;
|
||||
previousTimeStamp = 0;
|
||||
timeChanges = [new SongTimeChange(previousTimeStamp, targetBPM)];
|
||||
}
|
||||
else
|
||||
{
|
||||
previousBPM = timeChanges[currentTimeChange].bpm;
|
||||
previousTimeStamp = timeChanges[currentTimeChange].timeStamp;
|
||||
timeChanges[currentTimeChange].bpm = targetBPM;
|
||||
timeChanges[currentTimeChange].timeStamp = targetTimeStamp;
|
||||
}
|
||||
|
||||
state.currentSongMetadata.timeChanges = timeChanges;
|
||||
|
||||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
state.notePreviewViewportBoundsDirty = true;
|
||||
state.scrollPositionInPixels = 0;
|
||||
|
||||
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||
|
||||
state.updateGridHeight();
|
||||
}
|
||||
|
||||
public function undo(state:ChartEditorState):Void
|
||||
{
|
||||
if (previousTimeChanges == null)
|
||||
{
|
||||
previousTimeChanges = [new SongTimeChange(previousTimeStamp, previousBPM)];
|
||||
}
|
||||
|
||||
state.currentSongMetadata.timeChanges = previousTimeChanges;
|
||||
|
||||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
state.notePreviewViewportBoundsDirty = true;
|
||||
state.scrollPositionInPixels = 0;
|
||||
|
||||
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||
|
||||
state.updateGridHeight();
|
||||
}
|
||||
|
||||
public function shouldAddToHistory(state:ChartEditorState):Bool
|
||||
{
|
||||
// This command is undoable. Add to the history if we actually performed an action.
|
||||
return (targetBPM != previousBPM || targetTimeStamp != previousTimeStamp);
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
if (targetBPM == previousBPM && targetTimeStamp != previousTimeStamp) return 'Changed TimeChange ${currentTimeChange} BPM to ${targetBPM}';
|
||||
else if (targetBPM != previousBPM && targetTimeStamp == previousTimeStamp) return 'Changed TimeChange ${currentTimeChange} timestamp to ${targetTimeStamp}';
|
||||
else
|
||||
return 'Changed TimeChange ${currentTimeChange} BPM to ${targetBPM} & timestamp to ${targetTimeStamp}';
|
||||
}
|
||||
}
|
||||
+14
-21
@@ -3,33 +3,32 @@ package funkin.ui.debug.charting.commands;
|
||||
import funkin.data.song.SongData.SongTimeChange;
|
||||
|
||||
/**
|
||||
* A command which changes the starting BPM of the song.
|
||||
* A command which removes the current timechange from the song's timechanges.
|
||||
*/
|
||||
@:nullSafety
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
class ChangeStartingBPMCommand implements ChartEditorCommand
|
||||
class RemoveCurrentTimeChangeCommand implements ChartEditorCommand
|
||||
{
|
||||
var targetBPM:Float;
|
||||
var currentTimeChange:Int;
|
||||
|
||||
var previousBPM:Float = 100;
|
||||
var previousTimeChanges:Null<Array<SongTimeChange>>;
|
||||
|
||||
public function new(targetBPM:Float)
|
||||
public function new(currentTimeChange:Int)
|
||||
{
|
||||
this.targetBPM = targetBPM;
|
||||
this.currentTimeChange = currentTimeChange;
|
||||
}
|
||||
|
||||
public function execute(state:ChartEditorState):Void
|
||||
{
|
||||
var timeChanges:Array<SongTimeChange> = state.currentSongMetadata.timeChanges;
|
||||
previousTimeChanges = timeChanges;
|
||||
if (timeChanges == null || timeChanges.length == 0)
|
||||
{
|
||||
previousBPM = 100;
|
||||
timeChanges = [new SongTimeChange(0, targetBPM)];
|
||||
timeChanges = [new SongTimeChange(0, 100)];
|
||||
}
|
||||
else
|
||||
{
|
||||
previousBPM = timeChanges[0].bpm;
|
||||
timeChanges[0].bpm = targetBPM;
|
||||
timeChanges.splice(currentTimeChange , 1);
|
||||
}
|
||||
|
||||
state.currentSongMetadata.timeChanges = timeChanges;
|
||||
@@ -45,17 +44,12 @@ class ChangeStartingBPMCommand implements ChartEditorCommand
|
||||
|
||||
public function undo(state:ChartEditorState):Void
|
||||
{
|
||||
var timeChanges:Array<SongTimeChange> = state.currentSongMetadata.timeChanges;
|
||||
if (timeChanges == null || timeChanges.length == 0)
|
||||
if (previousTimeChanges == null)
|
||||
{
|
||||
timeChanges = [new SongTimeChange(0, previousBPM)];
|
||||
}
|
||||
else
|
||||
{
|
||||
timeChanges[0].bpm = previousBPM;
|
||||
previousTimeChanges = [new SongTimeChange(0, 100)];
|
||||
}
|
||||
|
||||
state.currentSongMetadata.timeChanges = timeChanges;
|
||||
state.currentSongMetadata.timeChanges = previousTimeChanges;
|
||||
|
||||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
@@ -68,12 +62,11 @@ class ChangeStartingBPMCommand implements ChartEditorCommand
|
||||
|
||||
public function shouldAddToHistory(state:ChartEditorState):Bool
|
||||
{
|
||||
// This command is undoable. Add to the history if we actually performed an action.
|
||||
return (targetBPM != previousBPM);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
return 'Change Starting BPM to ${targetBPM}';
|
||||
return 'Removed TimeChange ${currentTimeChange}';
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,9 @@ import funkin.data.song.importer.ChartManifestData;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.play.notes.notestyle.NoteStyle;
|
||||
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||
import funkin.ui.debug.charting.commands.AddNewTimeChangeCommand;
|
||||
import funkin.ui.debug.charting.commands.ModifyCurrentTimeChangeCommand;
|
||||
import funkin.ui.debug.charting.commands.RemoveCurrentTimeChangeCommand;
|
||||
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||
import haxe.ui.components.Button;
|
||||
import haxe.ui.components.DropDown;
|
||||
@@ -36,6 +38,8 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
||||
var buttonCharacterGirlfriend:Button;
|
||||
var buttonCharacterOpponent:Button;
|
||||
var inputBPM:NumberStepper;
|
||||
var labelTimeStamp:Label;
|
||||
var inputTimeStamp:NumberStepper;
|
||||
var labelScrollSpeed:Label;
|
||||
var inputScrollSpeed:Slider;
|
||||
var frameVariation:Frame;
|
||||
@@ -140,36 +144,105 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
||||
var startingValueNoteStyle = ChartEditorDropdowns.populateDropdownWithNoteStyles(inputNoteStyle, chartEditorState.currentSongMetadata.playData.noteStyle);
|
||||
inputNoteStyle.value = startingValueNoteStyle;
|
||||
|
||||
inputTimeChange.onChange = function(event:UIEvent) {
|
||||
var currentTimeChange = chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex];
|
||||
inputBPM.value = currentTimeChange.bpm;
|
||||
inputTSNum.value = currentTimeChange.timeSignatureNum;
|
||||
inputTSDen.value = currentTimeChange.timeSignatureDen;
|
||||
inputTimeStamp.value = currentTimeChange.timeStamp;
|
||||
// Set the min max values of the input timestamp to previous and next time change timestamps to in the array, to prevent the conductor from breaking due to wack time change timestamps
|
||||
inputTimeStamp.min = (chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex - 1]?.timeStamp) ?? 0;
|
||||
if (inputTimeStamp.min != 0) inputTimeStamp.min += 1;
|
||||
inputTimeStamp.max = (chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex + 1]?.timeStamp) ?? chartEditorState.songLengthInMs;
|
||||
|
||||
trace(inputTimeStamp.min);
|
||||
trace(inputTimeStamp.max);
|
||||
|
||||
// Prevent the inital time change timestamp from being modified (it should always be 0) or removed
|
||||
if (inputTimeChange.selectedIndex == 0)
|
||||
{
|
||||
labelTimeStamp.hidden = true;
|
||||
inputTimeStamp.hidden = true;
|
||||
removeTimeChange.disabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
labelTimeStamp.hidden = false;
|
||||
inputTimeStamp.hidden = false;
|
||||
removeTimeChange.disabled = false;
|
||||
}
|
||||
};
|
||||
var startingTimeChange = ChartEditorDropdowns.populateDropdownWithTimeChanges(inputTimeChange, chartEditorState.currentSongMetadata.timeChanges, 0);
|
||||
inputTimeChange.value = startingTimeChange;
|
||||
|
||||
inputBPM.onChange = function(event:UIEvent) {
|
||||
if (event.value == null || event.value <= 0) return;
|
||||
|
||||
// Use a command so we can undo/redo this action.
|
||||
if (event.value != Conductor.instance.bpm)
|
||||
var currentTimeChange = chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex];
|
||||
var currentBPM = currentTimeChange.bpm;
|
||||
if (event.value != currentBPM)
|
||||
{
|
||||
chartEditorState.performCommand(new ChangeStartingBPMCommand(event.value));
|
||||
chartEditorState.performCommand(new ModifyCurrentTimeChangeCommand(inputTimeChange.selectedIndex, event.value, currentTimeChange.timeStamp));
|
||||
// TODO: Figure out why the text of the time change dropdown itself isn't updating and fix it
|
||||
inputTimeChange.value.text = '${currentTimeChange.timeStamp} : BPM: ${event.value} in ${currentTimeChange.timeSignatureNum}/${currentTimeChange.timeSignatureDen}';
|
||||
}
|
||||
};
|
||||
|
||||
inputTimeStamp.onChange = function(event:UIEvent) {
|
||||
if (event.value == null || event.value <= 0) return;
|
||||
|
||||
// Use a command so we can undo/redo this action.
|
||||
var currentTimeChange = chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex];
|
||||
var currentTimeStamp = currentTimeChange.timeStamp;
|
||||
if (inputTimeChange.selectedIndex != 0 && event.value != currentTimeStamp)
|
||||
{
|
||||
chartEditorState.performCommand(new ModifyCurrentTimeChangeCommand(inputTimeChange.selectedIndex, currentTimeChange.bpm, event.value));
|
||||
inputTimeChange.value.text = '${event.value} : BPM: ${currentTimeChange.bpm} in ${currentTimeChange.timeSignatureNum}/${currentTimeChange.timeSignatureDen}';
|
||||
trace(inputTimeChange.dataSource.get(inputTimeChange.selectedIndex));
|
||||
trace(inputTimeChange.selectedItem);
|
||||
chartEditorState.updateTimeSignature();
|
||||
}
|
||||
};
|
||||
|
||||
inputTSNum.onChange = function(event:UIEvent) {
|
||||
var numerator:Null<Int> = Std.parseInt(event?.data?.text);
|
||||
if (numerator == null) return;
|
||||
var prevNumerator:Int = chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureNum;
|
||||
var currentTimeChange = chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex];
|
||||
var prevNumerator:Int = currentTimeChange.timeSignatureNum;
|
||||
if (numerator == prevNumerator) return;
|
||||
|
||||
chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureNum = numerator;
|
||||
chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex].timeSignatureNum = numerator;
|
||||
inputTimeChange.value.text = '${currentTimeChange.timeStamp} : BPM: ${currentTimeChange.bpm} in ${numerator}/${currentTimeChange.timeSignatureDen}';
|
||||
chartEditorState.updateTimeSignature();
|
||||
}
|
||||
|
||||
inputTSDen.onChange = function(event:UIEvent) {
|
||||
var denominator:Null<Int> = Std.parseInt(event?.data?.text);
|
||||
if (denominator == null) return;
|
||||
var prevDenominator:Int = chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureDen;
|
||||
var currentTimeChange = chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex];
|
||||
var prevDenominator:Int = currentTimeChange.timeSignatureDen;
|
||||
if (denominator == prevDenominator) return;
|
||||
|
||||
chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureDen = denominator;
|
||||
chartEditorState.currentSongMetadata.timeChanges[inputTimeChange.selectedIndex].timeSignatureDen = denominator;
|
||||
inputTimeChange.value.text = '${currentTimeChange.timeStamp} : BPM: ${currentTimeChange.bpm} in ${currentTimeChange.timeSignatureNum}/${denominator}';
|
||||
chartEditorState.updateTimeSignature();
|
||||
}
|
||||
|
||||
addTimeChange.onClick = function(_:UIEvent) {
|
||||
chartEditorState.performCommand(new AddNewTimeChangeCommand(inputTimeChange.selectedIndex));
|
||||
var startingTimeChange = ChartEditorDropdowns.populateDropdownWithTimeChanges(inputTimeChange, chartEditorState.currentSongMetadata.timeChanges,
|
||||
inputTimeChange.selectedIndex + 1);
|
||||
inputTimeChange.value = startingTimeChange;
|
||||
}
|
||||
|
||||
removeTimeChange.onClick = function(_:UIEvent) {
|
||||
chartEditorState.performCommand(new RemoveCurrentTimeChangeCommand(inputTimeChange.selectedIndex));
|
||||
var startingTimeChange = ChartEditorDropdowns.populateDropdownWithTimeChanges(inputTimeChange, chartEditorState.currentSongMetadata.timeChanges,
|
||||
inputTimeChange.selectedIndex - 1);
|
||||
inputTimeChange.value = startingTimeChange;
|
||||
}
|
||||
|
||||
inputScrollSpeed.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.value != null && event.target.value > 0;
|
||||
|
||||
@@ -213,16 +286,16 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
||||
inputSongArtist.value = chartEditorState.currentSongMetadata.artist;
|
||||
inputSongCharter.value = chartEditorState.currentSongMetadata.charter;
|
||||
inputStage.value = chartEditorState.currentSongMetadata.playData.stage;
|
||||
inputNoteStyle.value = chartEditorState.currentSongNoteStyle;
|
||||
inputBPM.value = chartEditorState.currentSongMetadata.timeChanges[0].bpm;
|
||||
inputNoteStyle.value = chartEditorState.currentSongMetadata.playData.noteStyle;
|
||||
inputDifficultyRating.value = chartEditorState.currentSongChartDifficultyRating;
|
||||
inputScrollSpeed.value = chartEditorState.currentSongChartScrollSpeed;
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${chartEditorState.currentSongChartScrollSpeed}x';
|
||||
frameVariation.text = 'Variation: ${chartEditorState.selectedVariation.toTitleCase()}';
|
||||
frameDifficulty.text = 'Difficulty: ${chartEditorState.selectedDifficulty.toTitleCase()}';
|
||||
|
||||
inputTSNum.value = '${chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureNum}';
|
||||
inputTSDen.value = '${chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureDen}';
|
||||
// Reset time change dropdown and the associated inputs
|
||||
var startingTimeChange = ChartEditorDropdowns.populateDropdownWithTimeChanges(inputTimeChange, chartEditorState.currentSongMetadata.timeChanges, 0);
|
||||
inputTimeChange.value = startingTimeChange;
|
||||
trace('Setting time signature to ${chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureNum}/${chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureDen}');
|
||||
|
||||
var stageId:String = chartEditorState.currentSongMetadata.playData.stage;
|
||||
|
||||
@@ -2,6 +2,7 @@ package funkin.ui.debug.charting.util;
|
||||
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.play.notes.notestyle.NoteStyle;
|
||||
import funkin.data.song.SongData.SongTimeChange;
|
||||
import funkin.play.event.SongEvent;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.character.CharacterData;
|
||||
@@ -82,6 +83,39 @@ class ChartEditorDropdowns
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate a dropdown with a list of time changes.
|
||||
*/
|
||||
public static function populateDropdownWithTimeChanges(dropDown:DropDown, timeChanges:Array<SongTimeChange>, startingTimeChange:Int = 0):DropDownEntry
|
||||
{
|
||||
dropDown.dataSource.clear();
|
||||
|
||||
var returnValue:DropDownEntry =
|
||||
{
|
||||
id: "0",
|
||||
text: '${timeChanges[0].timeStamp} : BPM: ${timeChanges[0].bpm} in ${timeChanges[0].timeSignatureNum}/${timeChanges[0].timeSignatureDen}'
|
||||
};
|
||||
|
||||
for (index in 0...timeChanges.length)
|
||||
{
|
||||
var value =
|
||||
{
|
||||
id: '$index',
|
||||
text: '${timeChanges[index].timeStamp} : BPM: ${timeChanges[index].bpm} in ${timeChanges[index].timeSignatureNum}/${timeChanges[index].timeSignatureDen}'
|
||||
};
|
||||
if (startingTimeChange == index) returnValue = value;
|
||||
|
||||
dropDown.dataSource.add(value);
|
||||
}
|
||||
|
||||
dropDown.dataSource.sort('id', ASCENDING);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate a dropdown with a list of song events.
|
||||
*/
|
||||
public static function populateDropdownWithSongEvents(dropDown:DropDown, startingEventId:String):DropDownEntry
|
||||
{
|
||||
dropDown.dataSource.clear();
|
||||
|
||||
Reference in New Issue
Block a user