diff --git a/Project.xml b/Project.xml index a83db1677..ccf6c83a3 100644 --- a/Project.xml +++ b/Project.xml @@ -200,6 +200,12 @@ --> --> + + +
+ +
+
diff --git a/source/funkin/input/Cursor.hx b/source/funkin/input/Cursor.hx index 37e819469..edd9e70f3 100644 --- a/source/funkin/input/Cursor.hx +++ b/source/funkin/input/Cursor.hx @@ -4,9 +4,34 @@ import openfl.utils.Assets; import lime.app.Future; import openfl.display.BitmapData; +@:nullSafety class Cursor { - public static var cursorMode(default, set):CursorMode; + /** + * The current cursor mode. + * Set this value to change the cursor graphic. + */ + public static var cursorMode(default, set):Null = null; + + /** + * Show the cursor. + */ + public static inline function show():Void + { + FlxG.mouse.visible = true; + // Reset the cursor mode. + Cursor.cursorMode = Default; + } + + /** + * Hide the cursor. + */ + public static inline function hide():Void + { + FlxG.mouse.visible = false; + // Reset the cursor mode. + Cursor.cursorMode = null; + } static final CURSOR_DEFAULT_PARAMS:CursorParams = { @@ -15,7 +40,7 @@ class Cursor offsetX: 0, offsetY: 0, }; - static var assetCursorDefault:BitmapData = null; + static var assetCursorDefault:Null = null; static final CURSOR_CROSS_PARAMS:CursorParams = { @@ -24,7 +49,7 @@ class Cursor offsetX: 0, offsetY: 0, }; - static var assetCursorCross:BitmapData = null; + static var assetCursorCross:Null = null; static final CURSOR_ERASER_PARAMS:CursorParams = { @@ -33,16 +58,16 @@ class Cursor offsetX: 0, offsetY: 0, }; - static var assetCursorEraser:BitmapData = null; + static var assetCursorEraser:Null = null; static final CURSOR_GRABBING_PARAMS:CursorParams = { graphic: "assets/images/cursor/cursor-grabbing.png", scale: 1.0, - offsetX: 32, + offsetX: -8, offsetY: 0, }; - static var assetCursorGrabbing:BitmapData = null; + static var assetCursorGrabbing:Null = null; static final CURSOR_HOURGLASS_PARAMS:CursorParams = { @@ -51,25 +76,34 @@ class Cursor offsetX: 0, offsetY: 0, }; - static var assetCursorHourglass:BitmapData = null; + static var assetCursorHourglass:Null = null; static final CURSOR_POINTER_PARAMS:CursorParams = { graphic: "assets/images/cursor/cursor-pointer.png", scale: 1.0, - offsetX: 8, + offsetX: -8, offsetY: 0, }; - static var assetCursorPointer:BitmapData = null; + static var assetCursorPointer:Null = null; static final CURSOR_TEXT_PARAMS:CursorParams = { graphic: "assets/images/cursor/cursor-text.png", - scale: 1.0, + scale: 0.2, offsetX: 0, offsetY: 0, }; - static var assetCursorText:BitmapData = null; + static var assetCursorText:Null = null; + + static final CURSOR_TEXT_VERTICAL_PARAMS:CursorParams = + { + graphic: "assets/images/cursor/cursor-text-vertical.png", + scale: 0.2, + offsetX: 0, + offsetY: 0, + }; + static var assetCursorTextVertical:Null = null; static final CURSOR_ZOOM_IN_PARAMS:CursorParams = { @@ -78,7 +112,7 @@ class Cursor offsetX: 0, offsetY: 0, }; - static var assetCursorZoomIn:BitmapData = null; + static var assetCursorZoomIn:Null = null; static final CURSOR_ZOOM_OUT_PARAMS:CursorParams = { @@ -87,11 +121,36 @@ class Cursor offsetX: 0, offsetY: 0, }; - static var assetCursorZoomOut:BitmapData = null; + static var assetCursorZoomOut:Null = null; - static function set_cursorMode(value:CursorMode):CursorMode + static final CURSOR_CROSSHAIR_PARAMS:CursorParams = + { + graphic: "assets/images/cursor/cursor-crosshair.png", + scale: 1.0, + offsetX: -16, + offsetY: -16, + }; + static var assetCursorCrosshair:Null = null; + + static final CURSOR_CELL_PARAMS:CursorParams = + { + graphic: "assets/images/cursor/cursor-cell.png", + scale: 1.0, + offsetX: -16, + offsetY: -16, + }; + static var assetCursorCell:Null = null; + + // DESIRED CURSOR: Resize NS (vertical) + // DESIRED CURSOR: Resize EW (horizontal) + // DESIRED CURSOR: Resize NESW (diagonal) + // DESIRED CURSOR: Resize NWSE (diagonal) + // DESIRED CURSOR: Help (Cursor with question mark) + // DESIRED CURSOR: Menu (Cursor with menu icon) + + static function set_cursorMode(value:Null):Null { - if (cursorMode != value) + if (value != null && cursorMode != value) { cursorMode = value; setCursorGraphic(cursorMode); @@ -99,16 +158,9 @@ class Cursor return cursorMode; } - public static inline function show():Void - { - FlxG.mouse.visible = true; - } - - public static inline function hide():Void - { - FlxG.mouse.visible = false; - } - + /** + * Synchronous. + */ static function setCursorGraphic(?value:CursorMode = null):Void { if (value == null) @@ -117,6 +169,156 @@ class Cursor return; } + switch (value) + { + case Default: + if (assetCursorDefault == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_DEFAULT_PARAMS.graphic); + assetCursorDefault = bitmapData; + applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS); + } + else + { + applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS); + } + + case Cross: + if (assetCursorCross == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_CROSS_PARAMS.graphic); + assetCursorCross = bitmapData; + applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS); + } + else + { + applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS); + } + + case Eraser: + if (assetCursorEraser == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_ERASER_PARAMS.graphic); + assetCursorEraser = bitmapData; + applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS); + } + else + { + applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS); + } + + case Grabbing: + if (assetCursorGrabbing == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_GRABBING_PARAMS.graphic); + assetCursorGrabbing = bitmapData; + applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS); + } + else + { + applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS); + } + + case Hourglass: + if (assetCursorHourglass == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_HOURGLASS_PARAMS.graphic); + assetCursorHourglass = bitmapData; + applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS); + } + else + { + applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS); + } + + case Pointer: + if (assetCursorPointer == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_POINTER_PARAMS.graphic); + assetCursorPointer = bitmapData; + applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS); + } + else + { + applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS); + } + + case Text: + if (assetCursorText == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_TEXT_PARAMS.graphic); + assetCursorText = bitmapData; + applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS); + } + else + { + applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS); + } + + case ZoomIn: + if (assetCursorZoomIn == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_ZOOM_IN_PARAMS.graphic); + assetCursorZoomIn = bitmapData; + applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS); + } + else + { + applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS); + } + + case ZoomOut: + if (assetCursorZoomOut == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_ZOOM_OUT_PARAMS.graphic); + assetCursorZoomOut = bitmapData; + applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS); + } + else + { + applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS); + } + + case Crosshair: + if (assetCursorCrosshair == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_CROSSHAIR_PARAMS.graphic); + assetCursorCrosshair = bitmapData; + applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS); + } + else + { + applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS); + } + + case Cell: + if (assetCursorCell == null) + { + var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_CELL_PARAMS.graphic); + assetCursorCell = bitmapData; + applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS); + } + else + { + applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS); + } + + default: + setCursorGraphic(null); + } + } + + /** + * Asynchronous. + */ + static function loadCursorGraphic(?value:CursorMode = null):Void + { + if (value == null) + { + FlxG.mouse.unload(); + return; + } + switch (value) { case Default: @@ -127,6 +329,7 @@ class Cursor assetCursorDefault = bitmapData; applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS); }); + future.onError(onCursorError.bind(Default)); } else { @@ -141,6 +344,7 @@ class Cursor assetCursorCross = bitmapData; applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS); }); + future.onError(onCursorError.bind(Cross)); } else { @@ -155,6 +359,7 @@ class Cursor assetCursorEraser = bitmapData; applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS); }); + future.onError(onCursorError.bind(Eraser)); } else { @@ -169,6 +374,7 @@ class Cursor assetCursorGrabbing = bitmapData; applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS); }); + future.onError(onCursorError.bind(Grabbing)); } else { @@ -183,6 +389,7 @@ class Cursor assetCursorHourglass = bitmapData; applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS); }); + future.onError(onCursorError.bind(Hourglass)); } else { @@ -197,6 +404,7 @@ class Cursor assetCursorPointer = bitmapData; applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS); }); + future.onError(onCursorError.bind(Pointer)); } else { @@ -211,6 +419,7 @@ class Cursor assetCursorText = bitmapData; applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS); }); + future.onError(onCursorError.bind(Text)); } else { @@ -225,6 +434,7 @@ class Cursor assetCursorZoomIn = bitmapData; applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS); }); + future.onError(onCursorError.bind(ZoomIn)); } else { @@ -239,14 +449,45 @@ class Cursor assetCursorZoomOut = bitmapData; applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS); }); + future.onError(onCursorError.bind(ZoomOut)); } else { applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS); } + case Crosshair: + if (assetCursorCrosshair == null) + { + var future:Future = Assets.loadBitmapData(CURSOR_CROSSHAIR_PARAMS.graphic); + future.onComplete(function(bitmapData:BitmapData) { + assetCursorCrosshair = bitmapData; + applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS); + }); + future.onError(onCursorError.bind(Crosshair)); + } + else + { + applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS); + } + + case Cell: + if (assetCursorCell == null) + { + var future:Future = Assets.loadBitmapData(CURSOR_CELL_PARAMS.graphic); + future.onComplete(function(bitmapData:BitmapData) { + assetCursorCell = bitmapData; + applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS); + }); + future.onError(onCursorError.bind(Cell)); + } + else + { + applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS); + } + default: - setCursorGraphic(null); + loadCursorGraphic(null); } } @@ -254,6 +495,11 @@ class Cursor { FlxG.mouse.load(graphic, params.scale, params.offsetX, params.offsetY); } + + static function onCursorError(cursorMode:CursorMode, error:String):Void + { + trace("Failed to load cursor graphic for cursor mode " + cursorMode + ": " + error); + } } // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor @@ -268,6 +514,8 @@ enum CursorMode Text; ZoomIn; ZoomOut; + Crosshair; + Cell; } /** diff --git a/source/funkin/play/song/SongMigrator.hx b/source/funkin/play/song/SongMigrator.hx index bb8718bb7..f33d9bbe9 100644 --- a/source/funkin/play/song/SongMigrator.hx +++ b/source/funkin/play/song/SongMigrator.hx @@ -179,7 +179,7 @@ class SongMigrator songMetadata.playData.playableChars = {}; try { - Reflect.setField(songMetadata.playData.playableChars, songData.song.player1, new SongPlayableChar('', songData.song.player2)); + songMetadata.playData.playableChars.set(songData.song.player1, new SongPlayableChar('', songData.song.player2)); } catch (e) { diff --git a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx index 7391c3d16..eb75e31c5 100644 --- a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx @@ -6,6 +6,7 @@ import funkin.util.SerializerUtil; import funkin.play.song.SongData.SongChartData; import funkin.play.song.SongData.SongMetadata; import flixel.util.FlxTimer; +import funkin.ui.haxeui.components.FunkinLink; import funkin.util.SortUtil; import funkin.input.Cursor; import funkin.play.character.BaseCharacter; @@ -134,7 +135,7 @@ class ChartEditorDialogHandler continue; } - var linkTemplateSong:Link = new Link(); + var linkTemplateSong:Link = new FunkinLink(); linkTemplateSong.text = songName; linkTemplateSong.onClick = function(_event) { dialog.hideDialog(DialogButton.CANCEL); @@ -306,6 +307,7 @@ class ChartEditorDialogHandler if (state.loadInstrumentalFromBytes(selectedFile.bytes)) { trace('Selected file: ' + selectedFile.fullPath); + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -313,6 +315,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end dialog.hideDialog(DialogButton.APPLY); removeDropHandler(onDropFile); @@ -321,6 +324,7 @@ class ChartEditorDialogHandler { trace('Failed to load instrumental (${selectedFile.fullPath})'); + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -328,6 +332,7 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } } }); @@ -339,6 +344,7 @@ class ChartEditorDialogHandler if (state.loadInstrumentalFromPath(path)) { // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -346,6 +352,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end dialog.hideDialog(DialogButton.APPLY); removeDropHandler(onDropFile); @@ -362,6 +369,7 @@ class ChartEditorDialogHandler } // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -369,6 +377,7 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } }; @@ -383,6 +392,15 @@ class ChartEditorDialogHandler handler:(String->Void) }> = []; + /** + * Add a callback for when a file is dropped on a component. + * + * On OS X you can’t drop on the application window, but rather only the app icon + * (either in the dock while running or the icon on the hard drive) so this must be disabled + * and UI updated appropriately. + * @param component + * @param handler + */ static function addDropHandler(component:Component, handler:String->Void):Void { #if desktop @@ -647,7 +665,11 @@ class ChartEditorDialogHandler var vocalsEntryLabel:Null