diff --git a/Project.xml b/Project.xml
index 69400d8b1..a1772a9ef 100644
--- a/Project.xml
+++ b/Project.xml
@@ -165,7 +165,10 @@
+
+
+
diff --git a/assets b/assets
index ef79a6cf1..6b6f2462a 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit ef79a6cf1ae3dcbd86a5b798f8117a6c692c0156
+Subproject commit 6b6f2462afb099a7301a782ae521a0175fb7c71b
diff --git a/source/funkin/MusicBeatState.hx b/source/funkin/MusicBeatState.hx
index 9a986a8b5..b045fdc24 100644
--- a/source/funkin/MusicBeatState.hx
+++ b/source/funkin/MusicBeatState.hx
@@ -56,27 +56,45 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
Conductor.stepHit.remove(this.stepHit);
}
- override function update(elapsed:Float)
+ function handleControls():Void
{
- super.update(elapsed);
+ var isHaxeUIFocused:Bool = haxe.ui.focus.FocusManager.instance?.focus != null;
- // Rebindable volume keys.
- if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted();
- else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1);
- else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
+ if (!isHaxeUIFocused)
+ {
+ // Rebindable volume keys.
+ if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted();
+ else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1);
+ else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
+ }
+ }
+ function handleFunctionControls():Void
+ {
// Emergency exit button.
if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState());
// This can now be used in EVERY STATE YAY!
if (FlxG.keys.justPressed.F5) debug_refreshModules();
+ }
+ function handleQuickWatch():Void
+ {
// Display Conductor info in the watch window.
FlxG.watch.addQuick("songPosition", Conductor.songPosition);
FlxG.watch.addQuick("bpm", Conductor.bpm);
FlxG.watch.addQuick("currentMeasureTime", Conductor.currentBeatTime);
FlxG.watch.addQuick("currentBeatTime", Conductor.currentBeatTime);
FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime);
+ }
+
+ override function update(elapsed:Float)
+ {
+ super.update(elapsed);
+
+ handleControls();
+ handleFunctionControls();
+ handleQuickWatch();
dispatchEvent(new UpdateScriptEvent(elapsed));
}
diff --git a/source/funkin/data/event/SongEventData.hx b/source/funkin/data/event/SongEventData.hx
index 831a53fbd..7a167b031 100644
--- a/source/funkin/data/event/SongEventData.hx
+++ b/source/funkin/data/event/SongEventData.hx
@@ -208,25 +208,32 @@ typedef SongEventSchemaField =
type:SongEventFieldType,
/**
- * Used for ENUM values.
+ * Used only for ENUM values.
* The key is the display name and the value is the actual value.
*/
?keys:Map,
+
/**
* Used for INTEGER and FLOAT values.
* The minimum value that can be entered.
+ * @default No minimum
*/
?min:Float,
+
/**
* Used for INTEGER and FLOAT values.
* The maximum value that can be entered.
+ * @default No maximum
*/
?max:Float,
+
/**
* Used for INTEGER and FLOAT values.
* The step value that will be used when incrementing/decrementing the value.
+ * @default `0.1`
*/
?step:Float,
+
/**
* An optional default value for the field.
*/
diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx
index 88993e519..eac4a4cb5 100644
--- a/source/funkin/data/song/SongData.hx
+++ b/source/funkin/data/song/SongData.hx
@@ -516,12 +516,22 @@ class SongEventData
public inline function getInt(key:String):Null
{
- return value == null ? null : cast Reflect.field(value, key);
+ if (value == null) return null;
+ var result = Reflect.field(value, key);
+ if (result == null) return null;
+ if (Std.isOfType(result, Int)) return result;
+ if (Std.isOfType(result, String)) return Std.parseInt(cast result);
+ return cast result;
}
public inline function getFloat(key:String):Null
{
- return value == null ? null : cast Reflect.field(value, key);
+ if (value == null) return null;
+ var result = Reflect.field(value, key);
+ if (result == null) return null;
+ if (Std.isOfType(result, Float)) return result;
+ if (Std.isOfType(result, String)) return Std.parseFloat(cast result);
+ return cast result;
}
public inline function getString(key:String):String
diff --git a/source/funkin/import.hx b/source/funkin/import.hx
index 1c3a0fdb4..62241c4f4 100644
--- a/source/funkin/import.hx
+++ b/source/funkin/import.hx
@@ -12,6 +12,7 @@ using Lambda;
using StringTools;
using funkin.util.tools.ArraySortTools;
using funkin.util.tools.ArrayTools;
+using funkin.util.tools.DynamicTools;
using funkin.util.tools.Int64Tools;
using funkin.util.tools.IteratorTools;
using funkin.util.tools.MapTools;
diff --git a/source/funkin/ui/debug/charting/ChartEditorEventSprite.hx b/source/funkin/ui/debug/charting/ChartEditorEventSprite.hx
index 021abde0f..d5d81ae29 100644
--- a/source/funkin/ui/debug/charting/ChartEditorEventSprite.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorEventSprite.hx
@@ -123,21 +123,25 @@ class ChartEditorEventSprite extends FlxSprite
function set_eventData(value:Null):Null
{
- this.eventData = value;
-
- if (this.eventData == null)
+ if (value == null)
{
+ this.eventData = null;
// Disown parent. MAKE SURE TO REVIVE BEFORE REUSING
this.kill();
+ this.visible = false;
+ return null;
+ }
+ else
+ {
+ this.visible = true;
+ // Only play the animation if the event type has changed.
+ // if (this.eventData == null || this.eventData.event != value.event)
+ playAnimation(value.event);
+ this.eventData = value;
+ // Update the position to match the note data.
+ updateEventPosition();
return this.eventData;
}
-
- this.visible = true;
- playAnimation(this.eventData.event);
- // Update the position to match the note data.
- updateEventPosition();
-
- return this.eventData;
}
public function updateEventPosition(?origin:FlxObject)
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 05173726f..c421c08f2 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -71,6 +71,7 @@ import haxe.ui.core.Component;
import haxe.ui.core.Screen;
import haxe.ui.events.DragEvent;
import haxe.ui.events.UIEvent;
+import haxe.ui.focus.FocusManager;
import haxe.ui.notifications.NotificationManager;
import haxe.ui.notifications.NotificationType;
import openfl.Assets;
@@ -492,22 +493,14 @@ class ChartEditorState extends HaxeUIState
var hitsoundsEnabledOpponent:Bool = true;
/**
- * Whether the user's mouse cursor is hovering over a SOLID component of the HaxeUI.
- * If so, ignore mouse events underneath.
+ * Whether the user is focused on an input in the Haxe UI, and inputs are being fed into it.
+ * If the user clicks off the input, focus will leave.
*/
- var isCursorOverHaxeUI(get, never):Bool;
+ var isHaxeUIFocused(get, never):Bool;
- function get_isCursorOverHaxeUI():Bool
+ function get_isHaxeUIFocused():Bool
{
- return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
- }
-
- var isCursorOverHaxeUIButton(get, never):Bool;
-
- function get_isCursorOverHaxeUIButton():Bool
- {
- return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY, haxe.ui.components.Button)
- || Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY, haxe.ui.components.Link);
+ return FocusManager.instance.focus != null;
}
/**
@@ -1905,11 +1898,11 @@ class ChartEditorState extends HaxeUIState
handleHelpKeybinds();
#if debug
- handleQuickWatch();
+ handleQuickWatches();
#end
}
- function handleQuickWatch():Void
+ function handleQuickWatches():Void
{
FlxG.watch.addQuick('scrollPosInPixels', scrollPositionInPixels);
FlxG.watch.addQuick('playheadPosInPixels', playheadPositionInPixels);
@@ -1957,8 +1950,8 @@ class ChartEditorState extends HaxeUIState
**/
function handleScrollKeybinds():Void
{
- // Don't scroll when the cursor is over the UI, unless a playbar button (the << >> ones) is pressed.
- if (isCursorOverHaxeUI && playbarButtonPressed == null) return;
+ // Don't scroll when the user is interacting with the UI, unless a playbar button (the << >> ones) is pressed.
+ if (isHaxeUIFocused && playbarButtonPressed == null) return;
var scrollAmount:Float = 0; // Amount to scroll the grid.
var playheadAmount:Float = 0; // Amount to scroll the playhead relative to the grid.
@@ -2157,7 +2150,7 @@ class ChartEditorState extends HaxeUIState
if (FlxG.mouse.justReleased) FlxG.sound.play(Paths.sound("chartingSounds/ClickUp"));
// Note: If a menu is open in HaxeUI, don't handle cursor behavior.
- var shouldHandleCursor:Bool = !isCursorOverHaxeUI || (selectionBoxStartPos != null);
+ var shouldHandleCursor:Bool = !isHaxeUIFocused || (selectionBoxStartPos != null);
var eventColumn:Int = (STRUMLINE_SIZE * 2 + 1) - 1;
if (shouldHandleCursor)
@@ -2612,14 +2605,14 @@ class ChartEditorState extends HaxeUIState
{
// Create an event and place it in the chart.
// TODO: Figure out configuring event data.
- var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData);
+ var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData.clone());
performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL));
}
else
{
// Create a note and place it in the chart.
- var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind);
+ var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind.clone());
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
@@ -3392,7 +3385,7 @@ class ChartEditorState extends HaxeUIState
*/
function handleTestKeybinds():Void
{
- if (!isHaxeUIDialogOpen && !isCursorOverHaxeUI && FlxG.keys.justPressed.ENTER)
+ if (!isHaxeUIDialogOpen && !isHaxeUIFocused && FlxG.keys.justPressed.ENTER)
{
var minimal = FlxG.keys.pressed.SHIFT;
ChartEditorToolboxHandler.hideAllToolboxes(this);
@@ -3889,7 +3882,7 @@ class ChartEditorState extends HaxeUIState
}
}
- if (FlxG.keys.justPressed.SPACE && !isHaxeUIDialogOpen)
+ if (FlxG.keys.justPressed.SPACE && !(isHaxeUIDialogOpen || isHaxeUIFocused))
{
toggleAudioPlayback();
}
diff --git a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx
index 7cee1edde..fb0981bae 100644
--- a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx
@@ -346,6 +346,8 @@ class ChartEditorToolboxHandler
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType');
+ state.selectedEventKind = eventType;
+
var schema:SongEventSchema = SongEventParser.getEventSchema(eventType);
if (schema == null)
@@ -356,6 +358,7 @@ class ChartEditorToolboxHandler
buildEventDataFormFromSchema(state, toolboxEventsDataGrid, schema);
}
+ toolboxEventsEventKind.value = state.selectedEventKind;
return toolbox;
}
@@ -379,6 +382,7 @@ class ChartEditorToolboxHandler
// Add a label.
var label:Label = new Label();
label.text = field.title;
+ label.verticalAlign = "center";
target.addComponent(label);
var input:Component;
@@ -396,8 +400,8 @@ class ChartEditorToolboxHandler
var numberStepper:NumberStepper = new NumberStepper();
numberStepper.id = field.name;
numberStepper.step = field.step ?? 0.1;
- numberStepper.min = field.min ?? 0.0;
- numberStepper.max = field.max ?? 1.0;
+ 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:
@@ -416,7 +420,7 @@ class ChartEditorToolboxHandler
for (optionName in field.keys.keys())
{
- var optionValue:Null = field.keys.get(optionName);
+ var optionValue:Null = field.keys.get(optionName);
trace('$optionName : $optionValue');
dropDown.dataSource.add({value: optionValue, text: optionName});
}
@@ -438,11 +442,21 @@ class ChartEditorToolboxHandler
target.addComponent(input);
input.onChange = function(event:UIEvent) {
- trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${event.target.value}');
+ var value = event.target.value;
+ if (field.type == ENUM)
+ {
+ value = event.target.value.value;
+ }
+ trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${value}');
- if (event.target.value == null) state.selectedEventData.remove(event.target.id);
+ if (value == null)
+ {
+ state.selectedEventData.remove(event.target.id);
+ }
else
- state.selectedEventData.set(event.target.id, event.target.value);
+ {
+ state.selectedEventData.set(event.target.id, value);
+ }
}
}
}
diff --git a/source/funkin/util/tools/DynamicTools.hx b/source/funkin/util/tools/DynamicTools.hx
new file mode 100644
index 000000000..47501ea22
--- /dev/null
+++ b/source/funkin/util/tools/DynamicTools.hx
@@ -0,0 +1,14 @@
+package funkin.util.tools;
+
+class DynamicTools
+{
+ /**
+ * Creates a full clone of the input `Dynamic`. Only guaranteed to work on anonymous structures.
+ * @param input The `Dynamic` to clone.
+ * @return A clone of the input `Dynamic`.
+ */
+ public static function clone(input:Dynamic):Dynamic
+ {
+ return Reflect.copy(input);
+ }
+}