1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2024-12-25 14:46:43 +00:00
Funkin/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx
2024-08-28 05:43:23 -04:00

309 lines
11 KiB
Haxe

package funkin.ui.debug.charting.toolboxes;
import funkin.play.character.BaseCharacter.CharacterType;
import funkin.play.character.CharacterData;
import funkin.data.stage.StageData;
import funkin.play.event.SongEvent;
import funkin.data.event.SongEventSchema;
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
import haxe.ui.components.Button;
import haxe.ui.components.CheckBox;
import haxe.ui.components.DropDown;
import haxe.ui.components.HorizontalSlider;
import haxe.ui.components.Label;
import haxe.ui.components.NumberStepper;
import haxe.ui.components.Slider;
import haxe.ui.core.Component;
import funkin.data.event.SongEventRegistry;
import haxe.ui.components.TextField;
import haxe.ui.containers.Box;
import haxe.ui.containers.HBox;
import haxe.ui.containers.Frame;
import haxe.ui.events.UIEvent;
import haxe.ui.data.ArrayDataSource;
import haxe.ui.containers.Grid;
import haxe.ui.components.DropDown;
import haxe.ui.containers.Frame;
/**
* The toolbox which allows modifying information like Song Title, Scroll Speed, Characters/Stages, and starting BPM.
*/
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
@:access(funkin.ui.debug.charting.ChartEditorState)
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/toolboxes/event-data.xml"))
class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
{
var toolboxEventsEventKind:DropDown;
var toolboxEventsDataFrame:Frame;
var toolboxEventsDataGrid:Grid;
var _initializing:Bool = true;
public function new(chartEditorState2:ChartEditorState)
{
super(chartEditorState2);
initialize();
this.onDialogClosed = onClose;
this._initializing = false;
}
function onClose(event:UIEvent)
{
chartEditorState.menubarItemToggleToolboxEventData.selected = false;
}
function initialize():Void
{
toolboxEventsEventKind.onChange = function(event:UIEvent) {
var eventType:String = event.data.id;
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType');
// Edit the event data to place.
chartEditorState.eventKindToPlace = eventType;
var schema:SongEventSchema = SongEventRegistry.getEventSchema(eventType);
if (schema == null)
{
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Unknown event kind: $eventType');
return;
}
buildEventDataFormFromSchema(toolboxEventsDataGrid, schema, chartEditorState.eventKindToPlace);
if (!_initializing && chartEditorState.currentEventSelection.length > 0)
{
// Edit the event data of any selected events.
for (event in chartEditorState.currentEventSelection)
{
event.eventKind = chartEditorState.eventKindToPlace;
event.value = chartEditorState.eventDataToPlace;
}
chartEditorState.saveDataDirty = true;
chartEditorState.noteDisplayDirty = true;
chartEditorState.notePreviewDirty = true;
}
}
var startingEventValue = ChartEditorDropdowns.populateDropdownWithSongEvents(toolboxEventsEventKind, chartEditorState.eventKindToPlace);
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Starting event kind: ${startingEventValue}');
toolboxEventsEventKind.value = startingEventValue;
}
public override function refresh():Void
{
super.refresh();
var newDropdownElement = ChartEditorDropdowns.findDropdownElement(chartEditorState.eventKindToPlace, toolboxEventsEventKind);
if (newDropdownElement == null)
{
throw 'ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event kind not in dropdown: ${chartEditorState.eventKindToPlace}';
}
else if (toolboxEventsEventKind.value != newDropdownElement || lastEventKind != toolboxEventsEventKind.value.id)
{
toolboxEventsEventKind.value = newDropdownElement;
var schema:SongEventSchema = SongEventRegistry.getEventSchema(chartEditorState.eventKindToPlace);
if (schema == null)
{
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Unknown event kind: ${chartEditorState.eventKindToPlace}');
}
else
{
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event kind changed: ${toolboxEventsEventKind.value.id} != ${newDropdownElement.id} != ${lastEventKind}, rebuilding form');
buildEventDataFormFromSchema(toolboxEventsDataGrid, schema, chartEditorState.eventKindToPlace);
}
}
else
{
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event kind not changed: ${toolboxEventsEventKind.value} == ${newDropdownElement} == ${lastEventKind}');
}
for (pair in chartEditorState.eventDataToPlace.keyValueIterator())
{
var fieldId:String = pair.key;
var value:Null<Dynamic> = pair.value;
var field:Component = toolboxEventsDataGrid.findComponent(fieldId);
if (field == null)
{
throw 'ChartEditorToolboxHandler.refresh() - Field "${fieldId}" does not exist in the event data form for kind ${lastEventKind}.';
}
else
{
switch (field)
{
case Std.isOfType(_, NumberStepper) => true:
var numberStepper:NumberStepper = cast field;
numberStepper.value = value;
case Std.isOfType(_, CheckBox) => true:
var checkBox:CheckBox = cast field;
checkBox.selected = value;
case Std.isOfType(_, DropDown) => true:
var dropDown:DropDown = cast field;
dropDown.value = value;
case Std.isOfType(_, TextField) => true:
var textField:TextField = cast field;
textField.text = value;
default:
throw 'ChartEditorToolboxHandler.refresh() - Field "${fieldId}" is of unknown type "${Type.getClassName(Type.getClass(field))}".';
}
}
}
}
var lastEventKind:String = 'unknown';
function buildEventDataFormFromSchema(target:Box, schema:SongEventSchema, eventKind:String):Void
{
trace('Building event data form from schema for event kind: ${eventKind}');
// trace(schema);
lastEventKind = eventKind ?? 'unknown';
// Clear the frame.
target.removeAllComponents();
chartEditorState.eventDataToPlace = {};
for (field in schema)
{
if (field == null) continue;
// Add a label for the data field.
var label:Label = new Label();
label.text = field.title;
label.verticalAlign = "center";
target.addComponent(label);
// Add an input field for the data field.
var input:Component;
switch (field.type)
{
case INTEGER:
var numberStepper:NumberStepper = new NumberStepper();
numberStepper.id = field.name;
numberStepper.step = field.step ?? 1.0;
if (field.min != null) numberStepper.min = field.min;
if (field.min != null) numberStepper.max = field.max;
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
input = numberStepper;
case FLOAT:
var numberStepper:NumberStepper = new NumberStepper();
numberStepper.id = field.name;
numberStepper.step = field.step ?? 0.1;
if (field.min != null) numberStepper.min = field.min;
if (field.max != null) numberStepper.max = field.max;
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
input = numberStepper;
case BOOL:
var checkBox:CheckBox = new CheckBox();
checkBox.id = field.name;
if (field.defaultValue != null) checkBox.selected = field.defaultValue;
input = checkBox;
case ENUM:
var dropDown:DropDown = new DropDown();
dropDown.id = field.name;
dropDown.width = 200.0;
dropDown.dropdownSize = 10;
dropDown.dropdownWidth = 300;
dropDown.searchable = true;
dropDown.dataSource = new ArrayDataSource();
if (field.keys == null) throw 'Field "${field.name}" is of Enum type but has no keys.';
// Add entries to the dropdown.
for (optionName in field.keys.keys())
{
var optionValue:Null<Dynamic> = field.keys.get(optionName);
// trace('$optionName : $optionValue');
dropDown.dataSource.add({value: optionValue, text: optionName});
}
dropDown.value = field.defaultValue;
// TODO: Add an option to customize sort.
dropDown.dataSource.sort('text', ASCENDING);
input = dropDown;
case STRING:
input = new TextField();
input.id = field.name;
if (field.defaultValue != null) input.text = field.defaultValue;
default:
// Unknown type. Display a label that proclaims the type so we can debug it.
input = new Label();
input.id = field.name;
input.text = field.type;
}
// Putting in a box so we can add a unit label easily if there is one.
var inputBox:HBox = new HBox();
inputBox.addComponent(input);
// Add a unit label if applicable.
if (field.units != null && field.units != "")
{
var units:Label = new Label();
units.text = field.units;
units.verticalAlign = "center";
inputBox.addComponent(units);
}
target.addComponent(inputBox);
// Update the value of the event data.
input.onChange = function(event:UIEvent) {
var value = event.target.value;
if (field.type == ENUM)
{
value = event.target.value.value;
}
else if (field.type == BOOL)
{
var chk:CheckBox = cast event.target;
value = cast(chk.selected, Null<Bool>); // Need to cast to nullable bool or the compiler will get mad.
}
trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${value}');
// Edit the event data to place.
if (value == null)
{
chartEditorState.eventDataToPlace.remove(event.target.id);
}
else
{
chartEditorState.eventDataToPlace.set(event.target.id, value);
}
// Edit the event data of any existing events.
if (!_initializing && chartEditorState.currentEventSelection.length > 0)
{
for (songEvent in chartEditorState.currentEventSelection)
{
songEvent.eventKind = chartEditorState.eventKindToPlace;
songEvent.value = Reflect.copy(chartEditorState.eventDataToPlace);
}
chartEditorState.saveDataDirty = true;
chartEditorState.noteDisplayDirty = true;
chartEditorState.notePreviewDirty = true;
chartEditorState.noteTooltipsDirty = true;
}
}
}
}
public static function build(chartEditorState:ChartEditorState):ChartEditorEventDataToolbox
{
return new ChartEditorEventDataToolbox(chartEditorState);
}
}