package funkin.input; #if FEATURE_HAXEUI import haxe.ui.backend.flixel.CursorHelper; #end import lime.app.Future; import openfl.display.BitmapData; @:nullSafety class Cursor { /** * 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; } public static inline function toggle():Void { if (FlxG.mouse.visible) { hide(); } else { show(); } } public static final CURSOR_DEFAULT_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-default.png', scale: 1.0, offsetX: 0, offsetY: 0, }; static var assetCursorDefault:Null = null; public static final CURSOR_CROSS_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-cross.png', scale: 1.0, offsetX: 0, offsetY: 0, }; static var assetCursorCross:Null = null; public static final CURSOR_ERASER_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-eraser.png', scale: 1.0, offsetX: 0, offsetY: 0, }; static var assetCursorEraser:Null = null; public static final CURSOR_GRABBING_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-grabbing.png', scale: 1.0, offsetX: -8, offsetY: 0, }; static var assetCursorGrabbing:Null = null; public static final CURSOR_HOURGLASS_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-hourglass.png', scale: 1.0, offsetX: 0, offsetY: 0, }; static var assetCursorHourglass:Null = null; public static final CURSOR_POINTER_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-pointer.png', scale: 1.0, offsetX: -8, offsetY: 0, }; static var assetCursorPointer:Null = null; public static final CURSOR_TEXT_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-text.png', scale: 0.2, offsetX: 0, offsetY: 0, }; static var assetCursorText:Null = null; public 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; public static final CURSOR_ZOOM_IN_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-zoom-in.png', scale: 1.0, offsetX: 0, offsetY: 0, }; static var assetCursorZoomIn:Null = null; public static final CURSOR_ZOOM_OUT_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-zoom-out.png', scale: 1.0, offsetX: 0, offsetY: 0, }; static var assetCursorZoomOut:Null = null; public 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; public 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; public static final CURSOR_SCROLL_PARAMS:CursorParams = { graphic: 'assets/images/cursor/cursor-scroll.png', scale: 0.2, offsetX: -15, offsetY: -15, }; static var assetCursorScroll:Null = null; static function set_cursorMode(value:Null):Null { if (value != null && cursorMode != value) { cursorMode = value; loadCursorGraphicSync(cursorMode); } return cursorMode; } /** * Loads the cursor graphic synchronously. * * @param value The cursor mode to load. If null, the cursor is unloaded. */ static function loadCursorGraphicSync(?value:CursorMode):Void { applyCursorParams(value); } /** * Loads the cursor graphic asynchronously. * * @param value The cursor mode to load. If null, the cursor is unloaded. */ static function loadCursorGraphicAsync(?value:CursorMode):Void { applyCursorParams(value, true); } static function applyCursorParams(mode:Null, async:Bool = false) { if (mode == null) { FlxG.mouse.unload(); return; } var data = switch (mode) { case Default: {cache: assetCursorDefault, params: CURSOR_DEFAULT_PARAMS, set: (bmp) -> assetCursorDefault = bmp}; case Cross: {cache: assetCursorCross, params: CURSOR_CROSS_PARAMS, set: (bmp) -> assetCursorCross = bmp}; case Eraser: {cache: assetCursorEraser, params: CURSOR_ERASER_PARAMS, set: (bmp) -> assetCursorEraser = bmp}; case Grabbing: {cache: assetCursorGrabbing, params: CURSOR_GRABBING_PARAMS, set: (bmp) -> assetCursorGrabbing = bmp}; case Hourglass: {cache: assetCursorHourglass, params: CURSOR_HOURGLASS_PARAMS, set: (bmp) -> assetCursorHourglass = bmp}; case Pointer: {cache: assetCursorPointer, params: CURSOR_POINTER_PARAMS, set: (bmp) -> assetCursorPointer = bmp}; case Text: {cache: assetCursorText, params: CURSOR_TEXT_PARAMS, set: (bmp) -> assetCursorText = bmp}; case TextVertical: {cache: assetCursorTextVertical, params: CURSOR_TEXT_VERTICAL_PARAMS, set: (bmp) -> assetCursorTextVertical = bmp}; case ZoomIn: {cache: assetCursorZoomIn, params: CURSOR_ZOOM_IN_PARAMS, set: (bmp) -> assetCursorZoomIn = bmp}; case ZoomOut: {cache: assetCursorZoomOut, params: CURSOR_ZOOM_OUT_PARAMS, set: (bmp) -> assetCursorZoomOut = bmp}; case Crosshair: {cache: assetCursorCrosshair, params: CURSOR_CROSSHAIR_PARAMS, set: (bmp) -> assetCursorCrosshair = bmp}; case Cell: {cache: assetCursorCell, params: CURSOR_CELL_PARAMS, set: (bmp) -> assetCursorCell = bmp}; case Scroll: {cache: assetCursorScroll, params: CURSOR_SCROLL_PARAMS, set: (bmp) -> assetCursorScroll = bmp}; default: null; } if (data == null) { FlxG.mouse.unload(); return; } if (data.cache != null) { applyGraphic(data.cache, data.params); return; } if (async) { var future:Future = Assets.loadBitmapData(data.params.graphic); future.onComplete((bmp:BitmapData) -> { data.set(bmp); applyGraphic(bmp, data.params); }); future.onError(onCursorError.bind(mode)); } else { var bitmapData:BitmapData = Assets.getBitmapData(data.params.graphic); data.set(bitmapData); applyGraphic(bitmapData, data.params); } } static inline function applyGraphic(graphic:BitmapData, params:CursorParams):Void { 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); } #if FEATURE_HAXEUI public static function registerHaxeUICursors():Void { CursorHelper.useCustomCursors = true; registerHaxeUICursor('default', CURSOR_DEFAULT_PARAMS); registerHaxeUICursor('cross', CURSOR_CROSS_PARAMS); registerHaxeUICursor('eraser', CURSOR_ERASER_PARAMS); registerHaxeUICursor('grabbing', CURSOR_GRABBING_PARAMS); registerHaxeUICursor('hourglass', CURSOR_HOURGLASS_PARAMS); registerHaxeUICursor('pointer', CURSOR_POINTER_PARAMS); registerHaxeUICursor('move', CURSOR_POINTER_PARAMS); registerHaxeUICursor('text', CURSOR_TEXT_PARAMS); registerHaxeUICursor('text-vertical', CURSOR_TEXT_VERTICAL_PARAMS); registerHaxeUICursor('zoom-in', CURSOR_ZOOM_IN_PARAMS); registerHaxeUICursor('zoom-out', CURSOR_ZOOM_OUT_PARAMS); registerHaxeUICursor('crosshair', CURSOR_CROSSHAIR_PARAMS); registerHaxeUICursor('cell', CURSOR_CELL_PARAMS); registerHaxeUICursor('scroll', CURSOR_SCROLL_PARAMS); } public static function registerHaxeUICursor(id:String, params:CursorParams):Void { CursorHelper.registerCursor(id, params.graphic, params.scale, params.offsetX, params.offsetY); } #end } // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor enum CursorMode { Default; Cross; Eraser; Grabbing; Hourglass; Pointer; Text; TextVertical; ZoomIn; ZoomOut; Crosshair; Cell; Scroll; } /** * Static data describing how a cursor should be rendered. */ typedef CursorParams = { graphic:String, scale:Float, offsetX:Int, offsetY:Int, }