Funkin/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx

753 lines
30 KiB
Haxe
Raw Normal View History

package funkin.ui.debug.charting;
import funkin.ui.haxeui.components.FunkinDropDown;
import funkin.play.stage.StageData.StageDataParser;
import funkin.play.stage.StageData;
import funkin.play.character.CharacterData;
import funkin.play.character.CharacterData.CharacterDataParser;
2023-09-12 23:37:07 -04:00
import haxe.ui.components.HorizontalSlider;
import haxe.ui.containers.TreeView;
import haxe.ui.containers.TreeViewNode;
import funkin.play.character.BaseCharacter.CharacterType;
2023-01-22 19:55:30 -05:00
import funkin.play.event.SongEvent;
import funkin.data.event.SongEventData;
import funkin.data.song.SongData.SongTimeChange;
import funkin.play.song.SongSerializer;
2023-01-22 19:55:30 -05:00
import funkin.ui.haxeui.components.CharacterPlayer;
import funkin.util.FileUtil;
import haxe.ui.components.Button;
2023-01-22 19:55:30 -05:00
import haxe.ui.components.CheckBox;
import haxe.ui.components.DropDown;
2023-01-22 19:55:30 -05:00
import haxe.ui.components.Label;
import haxe.ui.components.NumberStepper;
import haxe.ui.components.Slider;
import haxe.ui.components.TextField;
import haxe.ui.containers.Box;
import haxe.ui.containers.Grid;
import haxe.ui.containers.Group;
2023-06-08 16:48:34 -04:00
import haxe.ui.containers.VBox;
import haxe.ui.containers.Frame;
2023-06-08 16:48:34 -04:00
import haxe.ui.containers.dialogs.CollapsibleDialog;
import haxe.ui.containers.dialogs.Dialog.DialogButton;
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
2023-01-22 19:55:30 -05:00
import haxe.ui.core.Component;
2023-06-08 16:48:34 -04:00
import haxe.ui.data.ArrayDataSource;
import haxe.ui.events.UIEvent;
/**
* Available tools for the chart editor state.
*/
enum ChartEditorToolMode
{
2023-01-22 19:55:30 -05:00
Select;
Place;
}
2023-06-08 16:48:34 -04:00
/**
* Static functions which handle building themed UI elements for a provided ChartEditorState.
*/
2023-08-31 18:47:23 -04:00
@:nullSafety
@:allow(funkin.ui.debug.charting.ChartEditorState)
class ChartEditorToolboxHandler
{
2023-01-22 19:55:30 -05:00
public static function setToolboxState(state:ChartEditorState, id:String, shown:Bool):Void
{
2023-06-08 16:48:34 -04:00
if (shown)
{
showToolbox(state, id);
}
2023-01-22 19:55:30 -05:00
else
2023-06-08 16:48:34 -04:00
{
2023-01-22 19:55:30 -05:00
hideToolbox(state, id);
2023-06-08 16:48:34 -04:00
}
2023-01-22 19:55:30 -05:00
}
2023-06-08 16:48:34 -04:00
public static function showToolbox(state:ChartEditorState, id:String):Void
2023-01-22 19:55:30 -05:00
{
2023-08-31 18:47:23 -04:00
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
2023-01-22 19:55:30 -05:00
if (toolbox == null) toolbox = initToolbox(state, id);
2023-01-22 19:55:30 -05:00
if (toolbox != null)
{
toolbox.showDialog(false);
switch (id)
{
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
onShowToolboxTools(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
onShowToolboxNoteData(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
onShowToolboxEventData(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
onShowToolboxDifficulty(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
onShowToolboxMetadata(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
onShowToolboxPlayerPreview(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:
onShowToolboxOpponentPreview(state, toolbox);
default:
// This happens if you try to load an unknown layout.
trace('ChartEditorToolboxHandler.showToolbox() - Unknown toolbox ID: $id');
}
2023-01-22 19:55:30 -05:00
}
else
{
trace('ChartEditorToolboxHandler.showToolbox() - Could not retrieve toolbox: $id');
}
}
public static function hideToolbox(state:ChartEditorState, id:String):Void
{
2023-08-31 18:47:23 -04:00
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
2023-01-22 19:55:30 -05:00
if (toolbox == null) toolbox = initToolbox(state, id);
2023-01-22 19:55:30 -05:00
if (toolbox != null)
{
toolbox.hideDialog(DialogButton.CANCEL);
switch (id)
{
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
onHideToolboxTools(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
onHideToolboxNoteData(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
onHideToolboxEventData(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
onHideToolboxDifficulty(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
onHideToolboxMetadata(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
onHideToolboxPlayerPreview(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:
onHideToolboxOpponentPreview(state, toolbox);
default:
// This happens if you try to load an unknown layout.
trace('ChartEditorToolboxHandler.hideToolbox() - Unknown toolbox ID: $id');
}
2023-01-22 19:55:30 -05:00
}
else
{
trace('ChartEditorToolboxHandler.hideToolbox() - Could not retrieve toolbox: $id');
}
}
public static function rememberOpenToolboxes(state:ChartEditorState):Void {}
public static function openRememberedToolboxes(state:ChartEditorState):Void {}
public static function hideAllToolboxes(state:ChartEditorState):Void
{
for (toolbox in state.activeToolboxes.values())
{
toolbox.hideDialog(DialogButton.CANCEL);
}
}
2023-06-08 16:48:34 -04:00
public static function minimizeToolbox(state:ChartEditorState, id:String):Void
{
2023-08-31 18:47:23 -04:00
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
2023-06-08 16:48:34 -04:00
if (toolbox == null) return;
2023-01-22 19:55:30 -05:00
2023-06-08 16:48:34 -04:00
toolbox.minimized = true;
}
2023-01-22 19:55:30 -05:00
2023-06-08 16:48:34 -04:00
public static function maximizeToolbox(state:ChartEditorState, id:String):Void
2023-01-22 19:55:30 -05:00
{
2023-08-31 18:47:23 -04:00
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
2023-06-08 16:48:34 -04:00
if (toolbox == null) return;
toolbox.minimized = false;
}
2023-08-31 18:47:23 -04:00
public static function initToolbox(state:ChartEditorState, id:String):Null<CollapsibleDialog>
2023-06-08 16:48:34 -04:00
{
2023-08-31 18:47:23 -04:00
var toolbox:Null<CollapsibleDialog> = null;
2023-01-22 19:55:30 -05:00
switch (id)
{
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
toolbox = buildToolboxToolsLayout(state);
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
toolbox = buildToolboxNoteDataLayout(state);
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
toolbox = buildToolboxEventDataLayout(state);
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
toolbox = buildToolboxDifficultyLayout(state);
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
toolbox = buildToolboxMetadataLayout(state);
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
toolbox = buildToolboxPlayerPreviewLayout(state);
2023-01-22 19:55:30 -05:00
case ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:
toolbox = buildToolboxOpponentPreviewLayout(state);
2023-01-22 19:55:30 -05:00
default:
// This happens if you try to load an unknown layout.
trace('ChartEditorToolboxHandler.initToolbox() - Unknown toolbox ID: $id');
toolbox = null;
}
// This happens if the layout you try to load has a syntax error.
if (toolbox == null) return null;
2023-01-22 19:55:30 -05:00
// Make sure we can reuse the toolbox later.
toolbox.destroyOnClose = false;
state.activeToolboxes.set(id, toolbox);
return toolbox;
}
2023-06-08 16:48:34 -04:00
/**
* Retrieve a toolbox by its layout's asset ID.
* @param state The ChartEditorState instance.
* @param id The asset ID of the toolbox layout.
* @return The toolbox.
*/
2023-08-31 18:47:23 -04:00
public static function getToolbox(state:ChartEditorState, id:String):Null<CollapsibleDialog>
2023-01-22 19:55:30 -05:00
{
2023-08-31 18:47:23 -04:00
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
2023-01-22 19:55:30 -05:00
// Initialize the toolbox without showing it.
if (toolbox == null) toolbox = initToolbox(state, id);
2023-01-22 19:55:30 -05:00
if (toolbox == null) throw 'ChartEditorToolboxHandler.getToolbox() - Could not retrieve or build toolbox: $id';
2023-01-22 19:55:30 -05:00
return toolbox;
}
2023-08-31 18:47:23 -04:00
static function buildToolboxToolsLayout(state:ChartEditorState):Null<CollapsibleDialog>
2023-01-22 19:55:30 -05:00
{
2023-06-08 16:48:34 -04:00
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT);
2023-01-22 19:55:30 -05:00
if (toolbox == null) return null;
2023-01-22 19:55:30 -05:00
// Starting position.
toolbox.x = 50;
toolbox.y = 50;
2023-06-08 16:48:34 -04:00
toolbox.onDialogClosed = function(event:DialogEvent) {
2023-01-22 19:55:30 -05:00
state.setUICheckboxSelected('menubarItemToggleToolboxTools', false);
}
2023-08-31 18:47:23 -04:00
var toolsGroup:Null<Group> = toolbox.findComponent('toolboxToolsGroup', Group);
if (toolsGroup == null) throw 'ChartEditorToolboxHandler.buildToolboxToolsLayout() - Could not find toolboxToolsGroup component.';
2023-01-22 19:55:30 -05:00
if (toolsGroup == null) return null;
2023-01-22 19:55:30 -05:00
2023-06-08 16:48:34 -04:00
toolsGroup.onChange = function(event:UIEvent) {
2023-01-22 19:55:30 -05:00
switch (event.target.id)
{
case 'toolboxToolsGroupSelect':
state.currentToolMode = ChartEditorToolMode.Select;
case 'toolboxToolsGroupPlace':
state.currentToolMode = ChartEditorToolMode.Place;
default:
trace('ChartEditorToolboxHandler.buildToolboxToolsLayout() - Unknown toolbox tool selected: $event.target.id');
}
}
return toolbox;
}
static function onShowToolboxTools(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxTools(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
2023-08-31 18:47:23 -04:00
static function buildToolboxNoteDataLayout(state:ChartEditorState):Null<CollapsibleDialog>
2023-01-22 19:55:30 -05:00
{
2023-06-08 16:48:34 -04:00
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT);
2023-01-22 19:55:30 -05:00
if (toolbox == null) return null;
2023-01-22 19:55:30 -05:00
// Starting position.
toolbox.x = 75;
toolbox.y = 100;
2023-06-08 16:48:34 -04:00
toolbox.onDialogClosed = function(event:DialogEvent) {
2023-01-22 19:55:30 -05:00
state.setUICheckboxSelected('menubarItemToggleToolboxNotes', false);
}
2023-08-31 18:47:23 -04:00
var toolboxNotesNoteKind:Null<DropDown> = toolbox.findComponent('toolboxNotesNoteKind', DropDown);
if (toolboxNotesNoteKind == null) throw 'ChartEditorToolboxHandler.buildToolboxNoteDataLayout() - Could not find toolboxNotesNoteKind component.';
var toolboxNotesCustomKindLabel:Null<Label> = toolbox.findComponent('toolboxNotesCustomKindLabel', Label);
if (toolboxNotesCustomKindLabel == null)
throw 'ChartEditorToolboxHandler.buildToolboxNoteDataLayout() - Could not find toolboxNotesCustomKindLabel component.';
var toolboxNotesCustomKind:Null<TextField> = toolbox.findComponent('toolboxNotesCustomKind', TextField);
if (toolboxNotesCustomKind == null) throw 'ChartEditorToolboxHandler.buildToolboxNoteDataLayout() - Could not find toolboxNotesCustomKind component.';
2023-01-22 19:55:30 -05:00
2023-06-08 16:48:34 -04:00
toolboxNotesNoteKind.onChange = function(event:UIEvent) {
var isCustom:Bool = (event.data.id == '~CUSTOM~');
2023-01-22 19:55:30 -05:00
if (isCustom)
{
toolboxNotesCustomKindLabel.hidden = false;
toolboxNotesCustomKind.hidden = false;
state.selectedNoteKind = toolboxNotesCustomKind.text;
}
else
{
toolboxNotesCustomKindLabel.hidden = true;
toolboxNotesCustomKind.hidden = true;
state.selectedNoteKind = event.data.id;
}
}
2023-06-08 16:48:34 -04:00
toolboxNotesCustomKind.onChange = function(event:UIEvent) {
2023-01-22 19:55:30 -05:00
state.selectedNoteKind = toolboxNotesCustomKind.text;
}
return toolbox;
}
static function onShowToolboxNoteData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxNoteData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
2023-08-31 18:47:23 -04:00
static function buildToolboxEventDataLayout(state:ChartEditorState):Null<CollapsibleDialog>
2023-01-22 19:55:30 -05:00
{
2023-06-08 16:48:34 -04:00
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT);
2023-01-22 19:55:30 -05:00
if (toolbox == null) return null;
2023-01-22 19:55:30 -05:00
// Starting position.
toolbox.x = 100;
toolbox.y = 150;
2023-06-08 16:48:34 -04:00
toolbox.onDialogClosed = function(event:DialogEvent) {
2023-01-22 19:55:30 -05:00
state.setUICheckboxSelected('menubarItemToggleToolboxEvents', false);
}
2023-08-31 18:47:23 -04:00
var toolboxEventsEventKind:Null<DropDown> = toolbox.findComponent('toolboxEventsEventKind', DropDown);
if (toolboxEventsEventKind == null) throw 'ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Could not find toolboxEventsEventKind component.';
var toolboxEventsDataGrid:Null<Grid> = toolbox.findComponent('toolboxEventsDataGrid', Grid);
if (toolboxEventsDataGrid == null) throw 'ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Could not find toolboxEventsDataGrid component.';
2023-01-22 19:55:30 -05:00
toolboxEventsEventKind.dataSource = new ArrayDataSource();
var songEvents:Array<SongEvent> = SongEventParser.listEvents();
for (event in songEvents)
{
toolboxEventsEventKind.dataSource.add({text: event.getTitle(), value: event.id});
}
2023-06-08 16:48:34 -04:00
toolboxEventsEventKind.onChange = function(event:UIEvent) {
2023-01-22 19:55:30 -05:00
var eventType:String = event.data.value;
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType');
var schema:SongEventSchema = SongEventParser.getEventSchema(eventType);
if (schema == null)
{
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Unknown event kind: $eventType');
return;
}
buildEventDataFormFromSchema(state, toolboxEventsDataGrid, schema);
}
return toolbox;
}
static function onShowToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
2023-01-22 19:55:30 -05:00
static function buildEventDataFormFromSchema(state:ChartEditorState, target:Box, schema:SongEventSchema):Void
{
trace(schema);
// Clear the frame.
target.removeAllComponents();
state.selectedEventData = {};
for (field in schema)
{
2023-08-31 18:47:23 -04:00
if (field == null) continue;
2023-01-22 19:55:30 -05:00
// Add a label.
var label:Label = new Label();
label.text = field.title;
target.addComponent(label);
var input:Component;
switch (field.type)
{
case INTEGER:
var numberStepper:NumberStepper = new NumberStepper();
numberStepper.id = field.name;
2023-08-31 18:47:23 -04:00
numberStepper.step = field.step ?? 1.0;
numberStepper.min = field.min ?? 0.0;
numberStepper.max = field.max ?? 10.0;
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
2023-01-22 19:55:30 -05:00
input = numberStepper;
case FLOAT:
var numberStepper:NumberStepper = new NumberStepper();
numberStepper.id = field.name;
2023-08-31 18:47:23 -04:00
numberStepper.step = field.step ?? 0.1;
numberStepper.min = field.min ?? 0.0;
numberStepper.max = field.max ?? 1.0;
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
2023-01-22 19:55:30 -05:00
input = numberStepper;
case BOOL:
2023-06-08 16:48:34 -04:00
var checkBox:CheckBox = new CheckBox();
2023-01-22 19:55:30 -05:00
checkBox.id = field.name;
2023-08-31 18:47:23 -04:00
if (field.defaultValue != null) checkBox.selected = field.defaultValue;
2023-01-22 19:55:30 -05:00
input = checkBox;
case ENUM:
var dropDown:DropDown = new DropDown();
dropDown.id = field.name;
dropDown.dataSource = new ArrayDataSource();
2023-08-31 18:47:23 -04:00
if (field.keys == null) throw 'Field "${field.name}" is of Enum type but has no keys.';
2023-01-22 19:55:30 -05:00
// Add entries to the dropdown.
2023-08-31 18:47:23 -04:00
2023-01-22 19:55:30 -05:00
for (optionName in field.keys.keys())
{
2023-08-31 18:47:23 -04:00
var optionValue:Null<String> = field.keys.get(optionName);
2023-01-22 19:55:30 -05:00
trace('$optionName : $optionValue');
dropDown.dataSource.add({value: optionValue, text: optionName});
}
dropDown.value = field.defaultValue;
input = dropDown;
case STRING:
input = new TextField();
input.id = field.name;
2023-08-31 18:47:23 -04:00
if (field.defaultValue != null) input.text = field.defaultValue;
2023-01-22 19:55:30 -05:00
default:
// Unknown type. Display a label so we know what it is.
input = new Label();
input.id = field.name;
input.text = field.type;
}
target.addComponent(input);
2023-06-08 16:48:34 -04:00
input.onChange = function(event:UIEvent) {
2023-01-22 19:55:30 -05:00
trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${event.target.value}');
if (event.target.value == null) state.selectedEventData.remove(event.target.id);
2023-01-22 19:55:30 -05:00
else
state.selectedEventData.set(event.target.id, event.target.value);
}
}
}
2023-08-31 18:47:23 -04:00
static function buildToolboxDifficultyLayout(state:ChartEditorState):Null<CollapsibleDialog>
2023-01-22 19:55:30 -05:00
{
2023-06-08 16:48:34 -04:00
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
2023-01-22 19:55:30 -05:00
if (toolbox == null) return null;
2023-01-22 19:55:30 -05:00
// Starting position.
toolbox.x = 125;
toolbox.y = 200;
2023-06-08 16:48:34 -04:00
toolbox.onDialogClosed = function(event:UIEvent) {
2023-01-22 19:55:30 -05:00
state.setUICheckboxSelected('menubarItemToggleToolboxDifficulty', false);
}
var difficultyToolboxAddVariation:Null<Button> = toolbox.findComponent('difficultyToolboxAddVariation', Button);
if (difficultyToolboxAddVariation == null)
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxAddVariation component.';
var difficultyToolboxAddDifficulty:Null<Button> = toolbox.findComponent('difficultyToolboxAddDifficulty', Button);
if (difficultyToolboxAddDifficulty == null)
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxAddDifficulty component.';
2023-08-31 18:47:23 -04:00
var difficultyToolboxSaveMetadata:Null<Button> = toolbox.findComponent('difficultyToolboxSaveMetadata', Button);
if (difficultyToolboxSaveMetadata == null)
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxSaveMetadata component.';
var difficultyToolboxSaveChart:Null<Button> = toolbox.findComponent('difficultyToolboxSaveChart', Button);
if (difficultyToolboxSaveChart == null)
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxSaveChart component.';
// var difficultyToolboxSaveAll:Null<Button> = toolbox.findComponent('difficultyToolboxSaveAll', Button);
// if (difficultyToolboxSaveAll == null) throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxSaveAll component.';
2023-08-31 18:47:23 -04:00
var difficultyToolboxLoadMetadata:Null<Button> = toolbox.findComponent('difficultyToolboxLoadMetadata', Button);
if (difficultyToolboxLoadMetadata == null)
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxLoadMetadata component.';
var difficultyToolboxLoadChart:Null<Button> = toolbox.findComponent('difficultyToolboxLoadChart', Button);
if (difficultyToolboxLoadChart == null)
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxLoadChart component.';
2023-01-22 19:55:30 -05:00
difficultyToolboxAddVariation.onClick = function(_:UIEvent) {
ChartEditorDialogHandler.openAddVariationDialog(state, true);
2023-01-22 19:55:30 -05:00
};
difficultyToolboxAddDifficulty.onClick = function(_:UIEvent) {
ChartEditorDialogHandler.openAddDifficultyDialog(state, true);
2023-01-22 19:55:30 -05:00
};
difficultyToolboxSaveMetadata.onClick = function(_:UIEvent) {
var vari:String = state.selectedVariation != Constants.DEFAULT_VARIATION ? '-${state.selectedVariation}' : '';
FileUtil.writeFileReference('${state.currentSongId}$vari-metadata.json', state.currentSongMetadata.serialize());
2023-01-22 19:55:30 -05:00
};
difficultyToolboxSaveChart.onClick = function(_:UIEvent) {
var vari:String = state.selectedVariation != Constants.DEFAULT_VARIATION ? '-${state.selectedVariation}' : '';
FileUtil.writeFileReference('${state.currentSongId}$vari-chart.json', state.currentSongChartData.serialize());
};
difficultyToolboxLoadMetadata.onClick = function(_:UIEvent) {
2023-01-22 19:55:30 -05:00
// Replace metadata for current variation.
2023-06-02 15:25:13 -04:00
SongSerializer.importSongMetadataAsync(function(songMetadata) {
2023-01-22 19:55:30 -05:00
state.currentSongMetadata = songMetadata;
});
};
difficultyToolboxLoadChart.onClick = function(_:UIEvent) {
2023-01-22 19:55:30 -05:00
// Replace chart data for current variation.
2023-06-02 15:25:13 -04:00
SongSerializer.importSongChartDataAsync(function(songChartData) {
2023-01-22 19:55:30 -05:00
state.currentSongChartData = songChartData;
state.noteDisplayDirty = true;
});
};
state.difficultySelectDirty = true;
return toolbox;
}
static function onShowToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void
{
// Update the selected difficulty when reopening the toolbox.
2023-08-31 18:47:23 -04:00
var treeView:Null<TreeView> = toolbox.findComponent('difficultyToolboxTree');
if (treeView == null) return;
2023-08-31 18:47:23 -04:00
var current = state.getCurrentTreeDifficultyNode(treeView);
if (current == null) return;
treeView.selectedNode = current;
trace('selected node: ${treeView.selectedNode}');
}
static function onHideToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
2023-08-31 18:47:23 -04:00
static function buildToolboxMetadataLayout(state:ChartEditorState):Null<CollapsibleDialog>
2023-01-22 19:55:30 -05:00
{
2023-06-08 16:48:34 -04:00
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
2023-01-22 19:55:30 -05:00
if (toolbox == null) return null;
2023-01-22 19:55:30 -05:00
// Starting position.
toolbox.x = 150;
toolbox.y = 250;
2023-06-08 16:48:34 -04:00
toolbox.onDialogClosed = function(event:UIEvent) {
2023-01-22 19:55:30 -05:00
state.setUICheckboxSelected('menubarItemToggleToolboxMetadata', false);
}
2023-08-31 18:47:23 -04:00
var inputSongName:Null<TextField> = toolbox.findComponent('inputSongName', TextField);
if (inputSongName == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputSongName component.';
2023-06-08 16:48:34 -04:00
inputSongName.onChange = function(event:UIEvent) {
var valid:Bool = event.target.text != null && event.target.text != '';
2023-01-22 19:55:30 -05:00
if (valid)
{
inputSongName.removeClass('invalid-value');
state.currentSongMetadata.songName = event.target.text;
}
else
{
2023-08-31 18:47:23 -04:00
state.currentSongMetadata.songName = '';
2023-01-22 19:55:30 -05:00
}
};
2023-06-08 16:48:34 -04:00
inputSongName.value = state.currentSongMetadata.songName;
2023-01-22 19:55:30 -05:00
2023-08-31 18:47:23 -04:00
var inputSongArtist:Null<TextField> = toolbox.findComponent('inputSongArtist', TextField);
if (inputSongArtist == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputSongArtist component.';
2023-06-08 16:48:34 -04:00
inputSongArtist.onChange = function(event:UIEvent) {
var valid:Bool = event.target.text != null && event.target.text != '';
2023-01-22 19:55:30 -05:00
if (valid)
{
inputSongArtist.removeClass('invalid-value');
state.currentSongMetadata.artist = event.target.text;
}
else
{
2023-08-31 18:47:23 -04:00
state.currentSongMetadata.artist = '';
2023-01-22 19:55:30 -05:00
}
};
2023-06-08 16:48:34 -04:00
inputSongArtist.value = state.currentSongMetadata.artist;
2023-01-22 19:55:30 -05:00
var inputStage:Null<FunkinDropDown> = toolbox.findComponent('inputStage', FunkinDropDown);
2023-08-31 18:47:23 -04:00
if (inputStage == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputStage component.';
2023-06-08 16:48:34 -04:00
inputStage.onChange = function(event:UIEvent) {
var valid:Bool = event.data != null && event.data.id != null;
2023-01-22 19:55:30 -05:00
if (valid)
{
state.currentSongMetadata.playData.stage = event.data.id;
}
};
var startingValueStage = ChartEditorDropdowns.populateDropdownWithStages(inputStage, state.currentSongMetadata.playData.stage);
inputStage.value = startingValueStage;
var inputNoteStyle:Null<FunkinDropDown> = toolbox.findComponent('inputNoteStyle', FunkinDropDown);
if (inputNoteStyle == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputNoteStyle component.';
inputNoteStyle.onChange = function(event:UIEvent) {
if (event.data?.id == null) return;
state.currentSongNoteStyle = event.data.id;
};
inputNoteStyle.value = state.currentSongNoteStyle;
2023-01-22 19:55:30 -05:00
// By using this flag, we prevent the dropdown value from changing while it is being populated.
var inputCharacterPlayer:Null<FunkinDropDown> = toolbox.findComponent('inputCharacterPlayer', FunkinDropDown);
if (inputCharacterPlayer == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputCharacterPlayer component.';
inputCharacterPlayer.onChange = function(event:UIEvent) {
if (event.data?.id == null) return;
state.currentSongMetadata.playData.characters.player = event.data.id;
};
var startingValuePlayer = ChartEditorDropdowns.populateDropdownWithCharacters(inputCharacterPlayer, CharacterType.BF,
state.currentSongMetadata.playData.characters.player);
inputCharacterPlayer.value = startingValuePlayer;
var inputCharacterOpponent:Null<FunkinDropDown> = toolbox.findComponent('inputCharacterOpponent', FunkinDropDown);
if (inputCharacterOpponent == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputCharacterOpponent component.';
inputCharacterOpponent.onChange = function(event:UIEvent) {
if (event.data?.id == null) return;
state.currentSongMetadata.playData.characters.opponent = event.data.id;
2023-01-22 19:55:30 -05:00
};
var startingValueOpponent = ChartEditorDropdowns.populateDropdownWithCharacters(inputCharacterOpponent, CharacterType.DAD,
state.currentSongMetadata.playData.characters.opponent);
inputCharacterOpponent.value = startingValueOpponent;
var inputCharacterGirlfriend:Null<FunkinDropDown> = toolbox.findComponent('inputCharacterGirlfriend', FunkinDropDown);
if (inputCharacterGirlfriend == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputCharacterGirlfriend component.';
inputCharacterGirlfriend.onChange = function(event:UIEvent) {
if (event.data?.id == null) return;
state.currentSongMetadata.playData.characters.girlfriend = event.data.id == "none" ? "" : event.data.id;
};
var startingValueGirlfriend = ChartEditorDropdowns.populateDropdownWithCharacters(inputCharacterGirlfriend, CharacterType.GF,
state.currentSongMetadata.playData.characters.girlfriend);
inputCharacterGirlfriend.value = startingValueGirlfriend;
2023-01-22 19:55:30 -05:00
2023-08-31 18:47:23 -04:00
var inputBPM:Null<NumberStepper> = toolbox.findComponent('inputBPM', NumberStepper);
if (inputBPM == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputBPM component.';
2023-06-08 16:48:34 -04:00
inputBPM.onChange = function(event:UIEvent) {
if (event.value == null || event.value <= 0) return;
2023-01-22 19:55:30 -05:00
2023-06-08 16:48:34 -04:00
var timeChanges:Array<SongTimeChange> = state.currentSongMetadata.timeChanges;
2023-01-22 19:55:30 -05:00
if (timeChanges == null || timeChanges.length == 0)
{
timeChanges = [new SongTimeChange(0, event.value)];
2023-01-22 19:55:30 -05:00
}
else
{
timeChanges[0].bpm = event.value;
}
state.currentSongMetadata.timeChanges = timeChanges;
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
2023-01-22 19:55:30 -05:00
};
2023-06-08 16:48:34 -04:00
inputBPM.value = state.currentSongMetadata.timeChanges[0].bpm;
2023-01-22 19:55:30 -05:00
2023-08-31 18:47:23 -04:00
var labelScrollSpeed:Null<Label> = toolbox.findComponent('labelScrollSpeed', Label);
if (labelScrollSpeed == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find labelScrollSpeed component.';
2023-08-31 18:47:23 -04:00
var inputScrollSpeed:Null<Slider> = toolbox.findComponent('inputScrollSpeed', Slider);
if (inputScrollSpeed == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputScrollSpeed component.';
2023-06-08 16:48:34 -04:00
inputScrollSpeed.onChange = function(event:UIEvent) {
var valid:Bool = event.target.value != null && event.target.value > 0;
2023-01-22 19:55:30 -05:00
if (valid)
{
inputScrollSpeed.removeClass('invalid-value');
2023-06-08 16:48:34 -04:00
state.currentSongChartScrollSpeed = event.target.value;
2023-01-22 19:55:30 -05:00
}
else
{
2023-06-08 16:48:34 -04:00
state.currentSongChartScrollSpeed = 1.0;
2023-01-22 19:55:30 -05:00
}
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
2023-01-22 19:55:30 -05:00
};
inputScrollSpeed.value = state.currentSongChartScrollSpeed;
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
2023-01-22 19:55:30 -05:00
2023-08-31 18:47:23 -04:00
var frameVariation:Null<Frame> = toolbox.findComponent('frameVariation', Frame);
if (frameVariation == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find frameVariation component.';
frameVariation.text = 'Variation: ${state.selectedVariation.toTitleCase()}';
2023-08-31 18:47:23 -04:00
var frameDifficulty:Null<Frame> = toolbox.findComponent('frameDifficulty', Frame);
if (frameDifficulty == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find frameDifficulty component.';
frameDifficulty.text = 'Difficulty: ${state.selectedDifficulty.toTitleCase()}';
2023-01-22 19:55:30 -05:00
return toolbox;
}
static function onShowToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void
{
state.refreshMetadataToolbox();
}
static function onHideToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
2023-08-31 18:47:23 -04:00
static function buildToolboxPlayerPreviewLayout(state:ChartEditorState):Null<CollapsibleDialog>
2023-01-22 19:55:30 -05:00
{
2023-06-08 16:48:34 -04:00
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT);
2023-01-22 19:55:30 -05:00
if (toolbox == null) return null;
2023-01-22 19:55:30 -05:00
// Starting position.
toolbox.x = 200;
toolbox.y = 350;
2023-06-08 16:48:34 -04:00
toolbox.onDialogClosed = function(event:DialogEvent) {
2023-01-22 19:55:30 -05:00
state.setUICheckboxSelected('menubarItemToggleToolboxPlayerPreview', false);
}
2023-08-31 18:47:23 -04:00
var charPlayer:Null<CharacterPlayer> = toolbox.findComponent('charPlayer');
if (charPlayer == null) throw 'ChartEditorToolboxHandler.buildToolboxPlayerPreviewLayout() - Could not find charPlayer component.';
2023-01-22 19:55:30 -05:00
// TODO: We need to implement character swapping in ChartEditorState.
charPlayer.loadCharacter('bf');
2023-06-08 16:48:34 -04:00
charPlayer.characterType = CharacterType.BF;
2023-01-22 19:55:30 -05:00
charPlayer.flip = true;
2023-06-08 16:48:34 -04:00
charPlayer.targetScale = 0.5;
2023-01-22 19:55:30 -05:00
return toolbox;
}
static function onShowToolboxPlayerPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxPlayerPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
2023-08-31 18:47:23 -04:00
static function buildToolboxOpponentPreviewLayout(state:ChartEditorState):Null<CollapsibleDialog>
2023-01-22 19:55:30 -05:00
{
2023-06-08 16:48:34 -04:00
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT);
2023-01-22 19:55:30 -05:00
if (toolbox == null) return null;
2023-01-22 19:55:30 -05:00
// Starting position.
toolbox.x = 200;
toolbox.y = 350;
2023-06-08 16:30:45 -04:00
toolbox.onDialogClosed = (event:DialogEvent) -> {
2023-01-22 19:55:30 -05:00
state.setUICheckboxSelected('menubarItemToggleToolboxOpponentPreview', false);
}
2023-08-31 18:47:23 -04:00
var charPlayer:Null<CharacterPlayer> = toolbox.findComponent('charPlayer');
if (charPlayer == null) throw 'ChartEditorToolboxHandler.buildToolboxOpponentPreviewLayout() - Could not find charPlayer component.';
2023-01-22 19:55:30 -05:00
// TODO: We need to implement character swapping in ChartEditorState.
charPlayer.loadCharacter('dad');
2023-06-08 16:48:34 -04:00
charPlayer.characterType = CharacterType.DAD;
2023-01-22 19:55:30 -05:00
charPlayer.flip = false;
2023-06-08 16:48:34 -04:00
charPlayer.targetScale = 0.5;
2023-01-22 19:55:30 -05:00
return toolbox;
}
static function onShowToolboxOpponentPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxOpponentPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
}