From 983466c0feb080bb591799fb6649ba82592786ee Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 27 Jun 2023 15:21:09 -0400 Subject: [PATCH 01/16] Visual fixes for story menu --- source/funkin/ui/story/Level.hx | 35 ++++++++-- source/funkin/ui/story/StoryMenuState.hx | 89 +++++++++++++++++------- 2 files changed, 90 insertions(+), 34 deletions(-) diff --git a/source/funkin/ui/story/Level.hx b/source/funkin/ui/story/Level.hx index 83682fec9..b936ab513 100644 --- a/source/funkin/ui/story/Level.hx +++ b/source/funkin/ui/story/Level.hx @@ -93,19 +93,40 @@ class Level implements IRegistryEntry return true; } + /** + * Build a sprite for the background of the level. + * Can be overriden by ScriptedLevel. Not used if `isBackgroundSimple` returns true. + */ public function buildBackground():FlxSprite { - if (_data.background.startsWith('#')) - { - // Color specified - var color:FlxColor = FlxColor.fromString(_data.background); - return new FlxSprite().makeGraphic(FlxG.width, 400, color); - } - else + if (!_data.background.startsWith('#')) { // Image specified return new FlxSprite().loadGraphic(Paths.image(_data.background)); } + + // Color specified + var result:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 400, FlxColor.WHITE); + result.color = getBackgroundColor(); + return result; + } + + /** + * Returns true if the background is a solid color. + * If you have a ScriptedLevel with a fancy background, you may want to override this to false. + */ + public function isBackgroundSimple():Bool + { + return _data.background.startsWith('#'); + } + + /** + * Returns true if the background is a solid color. + * If you have a ScriptedLevel with a fancy background, you may want to override this to false. + */ + public function getBackgroundColor():FlxColor + { + return FlxColor.fromString(_data.background); } public function getDifficulties():Array diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 1dc59f3ec..853d5ecc0 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -145,6 +145,10 @@ class StoryMenuState extends MusicBeatState updateBackground(); + var black:FlxSprite = new FlxSprite(levelBackground.x, 0).makeGraphic(FlxG.width, Std.int(400 + levelBackground.y), FlxColor.BLACK); + black.zIndex = levelBackground.zIndex - 1; + add(black); + levelProps = new FlxTypedGroup(); levelProps.zIndex = 1000; add(levelProps); @@ -378,6 +382,7 @@ class StoryMenuState extends MusicBeatState if (currentIndex < 0) currentIndex = levelList.length - 1; if (currentIndex >= levelList.length) currentIndex = 0; + var previousLevelId:String = currentLevelId; currentLevelId = levelList[currentIndex]; updateData(); @@ -393,18 +398,14 @@ class StoryMenuState extends MusicBeatState currentLevelTitle = item; item.alpha = 1.0; } - else if (index > currentIndex) - { - item.alpha = 0.6; - } else { - item.alpha = 0.0; + item.alpha = 0.6; } } updateText(); - updateBackground(); + updateBackground(previousLevelId); updateProps(); refresh(); } @@ -517,32 +518,66 @@ class StoryMenuState extends MusicBeatState }); } - function updateBackground():Void + function updateBackground(?previousLevelId:String = ''):Void { - if (levelBackground != null) + if (levelBackground == null || previousLevelId == '') { - var oldBackground:FlxSprite = levelBackground; - - FlxTween.tween(oldBackground, {alpha: 0.0}, 0.6, - { - ease: FlxEase.linear, - onComplete: function(_) { - remove(oldBackground); - } - }); + // Build a new background and display it immediately. + levelBackground = currentLevel.buildBackground(); + levelBackground.x = 0; + levelBackground.y = 56; + levelBackground.zIndex = 100; + levelBackground.alpha = 1.0; // Not hidden. + add(levelBackground); } + else + { + var previousLevel = LevelRegistry.instance.fetchEntry(previousLevelId); - levelBackground = currentLevel.buildBackground(); - levelBackground.x = 0; - levelBackground.y = 56; - levelBackground.alpha = 0.0; - levelBackground.zIndex = 100; - add(levelBackground); - - FlxTween.tween(levelBackground, {alpha: 1.0}, 0.6, + if (currentLevel.isBackgroundSimple() && previousLevel.isBackgroundSimple()) { - ease: FlxEase.linear - }); + var previousColor:FlxColor = previousLevel.getBackgroundColor(); + var currentColor:FlxColor = currentLevel.getBackgroundColor(); + if (previousColor != currentColor) + { + // Both the previous and current level were simple backgrounds. + // Fade between colors directly, rather than fading one background out and another in. + FlxTween.color(levelBackground, 0.4, previousColor, currentColor); + } + else + { + // Do no fade at all if the colors aren't different. + } + } + else + { + // Either the previous or current level has a complex background. + // We need to fade the old background out and the new one in. + + // Reference the old background and fade it out. + var oldBackground:FlxSprite = levelBackground; + FlxTween.tween(oldBackground, {alpha: 0.0}, 0.6, + { + ease: FlxEase.linear, + onComplete: function(_) { + remove(oldBackground); + } + }); + + // Build a new background and fade it in. + levelBackground = currentLevel.buildBackground(); + levelBackground.x = 0; + levelBackground.y = 56; + levelBackground.alpha = 0.0; // Hidden to start. + levelBackground.zIndex = 100; + add(levelBackground); + + FlxTween.tween(levelBackground, {alpha: 1.0}, 0.6, + { + ease: FlxEase.linear + }); + } + } } function updateProps():Void From bc5b34cdd2e26bf4b122ca83c7cc9ab3181f5cc4 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 31 Aug 2023 19:14:01 -0400 Subject: [PATCH 02/16] Fixed a bug where the playbar buttons wouldn't work while the cursor was over the UI. --- .../ui/debug/charting/ChartEditorState.hx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 83c052050..84e3caba1 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1846,14 +1846,21 @@ class ChartEditorState extends HaxeUIState **/ function handleScrollKeybinds():Void { - // Don't scroll when the cursor is over the UI. - if (isCursorOverHaxeUI) return; + // Don't scroll when the cursor is over the UI, unless a playbar button (the << >> ones) is pressed. + if (isCursorOverHaxeUI && 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. var shouldPause:Bool = false; // Whether to pause the song when scrolling. var shouldEase:Bool = false; // Whether to ease the scroll. + // Mouse Wheel = Scroll + if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL) + { + scrollAmount = -10 * FlxG.mouse.wheel; + shouldPause = true; + } + // Up Arrow = Scroll Up if (upKeyHandler.activated && currentLiveInputStyle != LiveInputStyle.WASD) { @@ -1909,13 +1916,6 @@ class ChartEditorState extends HaxeUIState shouldPause = true; } - // Mouse Wheel = Scroll - if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL) - { - scrollAmount = -10 * FlxG.mouse.wheel; - shouldPause = true; - } - // Middle Mouse + Drag = Scroll but move the playhead the same amount. if (FlxG.mouse.pressedMiddle) { From 2f26a97dcb5070295370ffcea8f5adab401e5116 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 31 Aug 2023 21:56:49 -0400 Subject: [PATCH 03/16] WIP on chart editor snapping --- .../ui/debug/charting/ChartEditorState.hx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 83c052050..8cc90aead 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -179,6 +179,8 @@ class ChartEditorState extends HaxeUIState */ static final SNAP_QUANTS:Array = [4, 8, 12, 16, 20, 24, 32, 48, 64, 96, 192]; + static final BASE_QUANT:Int = 16; + /** * INSTANCE DATA */ @@ -1770,7 +1772,7 @@ class ChartEditorState extends HaxeUIState // These ones only happen if the modal dialog is not open. handleScrollKeybinds(); // handleZoom(); - // handleSnap(); + handleSnap(); handleCursor(); handleMenubar(); @@ -2028,11 +2030,25 @@ class ChartEditorState extends HaxeUIState if (FlxG.keys.justPressed.LEFT) { noteSnapQuantIndex--; + NotificationManager.instance.addNotification( + { + title: 'Note Snapping', + body: 'Updated note snapping to 1/${noteSnapQuant}', + type: NotificationType.Success, + expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME + }); } if (FlxG.keys.justPressed.RIGHT) { noteSnapQuantIndex++; + NotificationManager.instance.addNotification( + { + title: 'Note Snapping', + body: 'Updated note snapping to 1/${noteSnapQuant}', + type: NotificationType.Success, + expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME + }); } } From 97b640f30b4912b5ab31e0367108197c37c7973c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 31 Aug 2023 23:48:15 -0400 Subject: [PATCH 04/16] Note snapping yay! --- .../charting/ChartEditorDialogHandler.hx | 26 +++++++++++++ .../debug/charting/ChartEditorNoteSprite.hx | 8 ++-- .../ui/debug/charting/ChartEditorState.hx | 37 +++++++++++++++++-- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx index 63dc8bd92..fa47e17b2 100644 --- a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx @@ -290,6 +290,7 @@ class ChartEditorDialogHandler if (state.loadInstrumentalFromBytes(selectedFile.bytes)) { trace('Selected file: ' + selectedFile.fullPath); + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -297,6 +298,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end dialog.hideDialog(DialogButton.APPLY); removeDropHandler(onDropFile); @@ -305,6 +307,7 @@ class ChartEditorDialogHandler { trace('Failed to load instrumental (${selectedFile.fullPath})'); + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -312,6 +315,7 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } } }); @@ -323,6 +327,7 @@ class ChartEditorDialogHandler if (state.loadInstrumentalFromPath(path)) { // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -330,6 +335,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end dialog.hideDialog(DialogButton.APPLY); removeDropHandler(onDropFile); @@ -346,6 +352,7 @@ class ChartEditorDialogHandler } // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -353,6 +360,7 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } }; @@ -622,6 +630,7 @@ class ChartEditorDialogHandler if (state.loadVocalsFromPath(path, charKey)) { // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -629,6 +638,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end vocalsEntryLabel.text = 'Vocals for $charName (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; dialogNoVocals.hidden = true; removeDropHandler(onDropFile); @@ -645,6 +655,7 @@ class ChartEditorDialogHandler } // Vocals failed to load. + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -652,6 +663,7 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.'; } @@ -768,6 +780,7 @@ class ChartEditorDialogHandler if (songMetadataVariation == null) { // Tell the user the load was not successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -775,12 +788,14 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end return; } songMetadata.set(variation, songMetadataVariation); // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -788,6 +803,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; @@ -809,6 +825,7 @@ class ChartEditorDialogHandler songMetadata.set(variation, songMetadataVariation); // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -816,6 +833,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}'; @@ -835,6 +853,7 @@ class ChartEditorDialogHandler songChartData.set(variation, songChartDataVariation); // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -842,6 +861,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; }; @@ -860,6 +880,7 @@ class ChartEditorDialogHandler songChartData.set(variation, songChartDataVariation); // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -867,6 +888,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}'; } @@ -942,6 +964,7 @@ class ChartEditorDialogHandler state.loadSong([Constants.DEFAULT_VARIATION => songMetadata], [Constants.DEFAULT_VARIATION => songChartData]); dialog.hideDialog(DialogButton.APPLY); + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -949,6 +972,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } }); } @@ -962,6 +986,7 @@ class ChartEditorDialogHandler state.loadSong([Constants.DEFAULT_VARIATION => songMetadata], [Constants.DEFAULT_VARIATION => songChartData]); dialog.hideDialog(DialogButton.APPLY); + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -969,6 +994,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end }; addDropHandler(importBox, onDropFile); diff --git a/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx index 0adbf1a20..ca480291a 100644 --- a/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx @@ -130,8 +130,10 @@ class ChartEditorNoteSprite extends FlxSprite return this.noteData; } - public function updateNotePosition(?origin:FlxObject) + public function updateNotePosition(?origin:FlxObject):Void { + if (this.noteData == null) return; + var cursorColumn:Int = this.noteData.data; if (cursorColumn < 0) cursorColumn = 0; @@ -158,9 +160,7 @@ class ChartEditorNoteSprite extends FlxSprite if (this.noteData.stepTime >= 0) { // noteData.stepTime is a calculated value which accounts for BPM changes - var stepTime:Float = this.noteData.stepTime; - var roundedStepTime:Float = Math.floor(stepTime + 0.01); // Add epsilon to fix rounding issues - this.y = roundedStepTime * ChartEditorState.GRID_SIZE; + this.y = this.noteData.stepTime * ChartEditorState.GRID_SIZE; } if (origin != null) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 8cc90aead..058703733 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -187,8 +187,16 @@ class ChartEditorState extends HaxeUIState // ============================== public var currentZoomLevel:Float = 1.0; - var noteSnapQuantIndex:Int = 3; + /** + * The internal index of what note snapping value is in use. + * Increment to make placement more preceise and decrement to make placement less precise. + */ + var noteSnapQuantIndex:Int = 3; // default is 16 + /** + * The current note snapping value. + * For example, `32` when snapping to 32nd notes. + */ public var noteSnapQuant(get, never):Int; function get_noteSnapQuant():Int @@ -196,6 +204,17 @@ class ChartEditorState extends HaxeUIState return SNAP_QUANTS[noteSnapQuantIndex]; } + /** + * The ratio of the current note snapping value to the default. + * For example, `32` becomes `0.5` when snapping to 16th notes. + */ + public var noteSnapRatio(get, never):Float; + + function get_noteSnapRatio():Float + { + return BASE_QUANT / noteSnapQuant; + } + /** * scrollPosition is the current position in the song, in pixels. * One pixel is 1/40 of 1 step, and 1/160 of 1 beat. @@ -2030,6 +2049,7 @@ class ChartEditorState extends HaxeUIState if (FlxG.keys.justPressed.LEFT) { noteSnapQuantIndex--; + #if !mac NotificationManager.instance.addNotification( { title: 'Note Snapping', @@ -2037,11 +2057,13 @@ class ChartEditorState extends HaxeUIState type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } if (FlxG.keys.justPressed.RIGHT) { noteSnapQuantIndex++; + #if !mac NotificationManager.instance.addNotification( { title: 'Note Snapping', @@ -2049,6 +2071,7 @@ class ChartEditorState extends HaxeUIState type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } } @@ -2136,8 +2159,12 @@ class ChartEditorState extends HaxeUIState } // The song position of the cursor, in steps. - var cursorFractionalStep:Float = cursorY / GRID_SIZE / (16 / noteSnapQuant); + var cursorFractionalStep:Float = cursorY / GRID_SIZE; var cursorMs:Float = Conductor.getStepTimeInMs(cursorFractionalStep); + // Round the cursor step to the nearest snap quant. + var cursorSnappedStep:Float = Math.floor(cursorFractionalStep / noteSnapRatio) * noteSnapRatio; + var cursorSnappedMs:Float = Conductor.getStepTimeInMs(cursorSnappedStep); + // The direction value for the column at the cursor. var cursorColumn:Int = Math.floor(cursorX / GRID_SIZE); if (cursorColumn < 0) cursorColumn = 0; @@ -2560,7 +2587,7 @@ class ChartEditorState extends HaxeUIState { eventData.event = selectedEventKind; } - eventData.time = cursorMs; + eventData.time = cursorSnappedMs; gridGhostEvent.visible = true; gridGhostEvent.eventData = eventData; @@ -2581,7 +2608,7 @@ class ChartEditorState extends HaxeUIState noteData.data = cursorColumn; gridGhostNote.playNoteAnimation(); } - noteData.time = cursorMs; + noteData.time = cursorSnappedMs; gridGhostNote.visible = true; gridGhostNote.noteData = noteData; @@ -4017,6 +4044,7 @@ class ChartEditorState extends HaxeUIState } } + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -4024,6 +4052,7 @@ class ChartEditorState extends HaxeUIState type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } /** From 3a47eed3b9d067e8d9dfb493b5d24622ed9cdfdf Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 1 Sep 2023 00:56:10 -0400 Subject: [PATCH 05/16] Darkened beat dividers on chart grid. --- .../debug/charting/ChartEditorThemeHandler.hx | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorThemeHandler.hx b/source/funkin/ui/debug/charting/ChartEditorThemeHandler.hx index 9b1e82df1..fb867c1fd 100644 --- a/source/funkin/ui/debug/charting/ChartEditorThemeHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorThemeHandler.hx @@ -50,6 +50,11 @@ class ChartEditorThemeHandler static final GRID_MEASURE_DIVIDER_COLOR_DARK:FlxColor = 0xFFC4C4C4; static final GRID_MEASURE_DIVIDER_WIDTH:Float = ChartEditorState.GRID_SELECTION_BORDER_WIDTH; + // Horizontal divider between beats. + static final GRID_BEAT_DIVIDER_COLOR_LIGHT:FlxColor = 0xFFC1C1C1; + static final GRID_BEAT_DIVIDER_COLOR_DARK:FlxColor = 0xFF848484; + static final GRID_BEAT_DIVIDER_WIDTH:Float = ChartEditorState.GRID_SELECTION_BORDER_WIDTH; + // Border on the square highlighting selected notes. static final SELECTION_SQUARE_BORDER_COLOR_LIGHT:FlxColor = 0xFF339933; static final SELECTION_SQUARE_BORDER_COLOR_DARK:FlxColor = 0xFF339933; @@ -141,7 +146,7 @@ class ChartEditorThemeHandler ChartEditorState.GRID_SELECTION_BORDER_WIDTH), selectionBorderColor); - // Selection borders in the middle. + // Selection borders horizontally along the middle. for (i in 1...(Conductor.stepsPerMeasure)) { state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2), @@ -159,7 +164,7 @@ class ChartEditorThemeHandler state.gridBitmap.height), selectionBorderColor); - // Selection borders across the middle. + // Selection borders vertically along the middle. for (i in 1...TOTAL_COLUMN_COUNT) { state.gridBitmap.fillRect(new Rectangle((ChartEditorState.GRID_SIZE * i) - (ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2), 0, @@ -172,7 +177,7 @@ class ChartEditorThemeHandler ChartEditorState.GRID_SELECTION_BORDER_WIDTH, state.gridBitmap.height), selectionBorderColor); - // Draw dividers between the measures. + // Draw horizontal dividers between the measures. var gridMeasureDividerColor:FlxColor = switch (state.currentTheme) { @@ -187,7 +192,30 @@ class ChartEditorThemeHandler var dividerLineBY:Float = state.gridBitmap.height - (GRID_MEASURE_DIVIDER_WIDTH / 2); state.gridBitmap.fillRect(new Rectangle(0, dividerLineBY, state.gridBitmap.width, GRID_MEASURE_DIVIDER_WIDTH / 2), gridMeasureDividerColor); - // Draw dividers between the strumlines. + // Draw horizontal dividers between the beats. + + var gridBeatDividerColor:FlxColor = switch (state.currentTheme) + { + case Light: GRID_BEAT_DIVIDER_COLOR_LIGHT; + case Dark: GRID_BEAT_DIVIDER_COLOR_DARK; + default: GRID_BEAT_DIVIDER_COLOR_LIGHT; + }; + + // Selection borders horizontally in the middle. + for (i in 1...(Conductor.stepsPerMeasure)) + { + if ((i % Conductor.beatsPerMeasure) == 0) + { + state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (GRID_BEAT_DIVIDER_WIDTH / 2), state.gridBitmap.width, + GRID_BEAT_DIVIDER_WIDTH), + gridBeatDividerColor); + } + } + + // Divider at top + state.gridBitmap.fillRect(new Rectangle(0, 0, state.gridBitmap.width, GRID_MEASURE_DIVIDER_WIDTH / 2), gridMeasureDividerColor); + + // Draw vertical dividers between the strumlines. var gridStrumlineDividerColor:FlxColor = switch (state.currentTheme) { From 2b092abd8abad84ef0bf1cf38c0ed980325441a7 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 4 Sep 2023 15:33:08 -0400 Subject: [PATCH 06/16] Added missing File menu keybinds --- source/funkin/ui/debug/charting/ChartEditorState.hx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f27fbb6c7..e51a94177 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2899,6 +2899,18 @@ class ChartEditorState extends HaxeUIState */ function handleFileKeybinds():Void { + // CTRL + N = New Chart + if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.N) + { + ChartEditorDialogHandler.openWelcomeDialog(this, true); + } + + // CTRL + O = Open Chart + if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.N) + { + ChartEditorDialogHandler.openBrowseWizard(this, true); + } + // CTRL + Q = Quit to Menu if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.Q) { From 2c3e513d5be23a3487609fa918170013caee32de Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 8 Sep 2023 17:41:20 -0400 Subject: [PATCH 07/16] Fixed bugs with saving charts on Mac Added FILE_DROP_SUPPORTED compilation flag and edited UI text to match. --- Project.xml | 6 ++ .../charting/ChartEditorDialogHandler.hx | 70 +++++++++++++++++++ .../ui/debug/charting/ChartEditorState.hx | 9 ++- source/funkin/util/FileUtil.hx | 4 ++ 4 files changed, 84 insertions(+), 5 deletions(-) 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/ui/debug/charting/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx index e5b2d332c..5c3f6cc2c 100644 --- a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx @@ -367,6 +367,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 @@ -613,7 +622,11 @@ class ChartEditorDialogHandler var vocalsEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_UPLOAD_VOCALS_ENTRY_LAYOUT); var vocalsEntryLabel:Label = vocalsEntry.findComponent('vocalsEntryLabel', Label); + #if FILE_DROP_SUPPORTED vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.'; + #else + vocalsEntryLabel.text = 'Click to browse for vocals for $charName.'; + #end var onDropFile:String->Void = function(pathStr:String) { trace('Selected file: $pathStr'); @@ -629,7 +642,12 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #if FILE_DROP_SUPPORTED vocalsEntryLabel.text = 'Vocals for $charName (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; + #else + vocalsEntryLabel.text = 'Vocals for $charName (click to browse)\n${path.file}.${path.ext}'; + #end + dialogNoVocals.hidden = true; removeDropHandler(onDropFile); } @@ -653,7 +671,11 @@ class ChartEditorDialogHandler expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #if FILE_DROP_SUPPORTED vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.'; + #else + vocalsEntryLabel.text = 'Click to browse for vocals for $charName.'; + #end } }; @@ -663,7 +685,11 @@ class ChartEditorDialogHandler if (selectedFile != null) { trace('Selected file: ' + selectedFile.name); + #if FILE_DROP_SUPPORTED + vocalsEntryLabel.text = 'Vocals for $charName (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}'; + #else vocalsEntryLabel.text = 'Vocals for $charName (click to browse)\n${selectedFile.name}'; + #end state.loadVocalsFromBytes(selectedFile.bytes, charKey); dialogNoVocals.hidden = true; removeDropHandler(onDropFile); @@ -672,7 +698,9 @@ class ChartEditorDialogHandler } // onDropFile + #if FILE_DROP_SUPPORTED addDropHandler(vocalsEntry, onDropFile); + #end dialogContainer.addComponent(vocalsEntry); } @@ -729,7 +757,11 @@ class ChartEditorDialogHandler // Build an entry for -chart.json. var songDefaultChartDataEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT); var songDefaultChartDataEntryLabel:Label = songDefaultChartDataEntry.findComponent('chartEntryLabel', Label); + #if FILE_DROP_SUPPORTED songDefaultChartDataEntryLabel.text = 'Drag and drop -chart.json file, or click to browse.'; + #else + songDefaultChartDataEntryLabel.text = 'Click to browse for -chart.json file.'; + #end songDefaultChartDataEntry.onClick = onClickChartDataVariation.bind(Constants.DEFAULT_VARIATION).bind(songDefaultChartDataEntryLabel); addDropHandler(songDefaultChartDataEntry, onDropFileChartDataVariation.bind(Constants.DEFAULT_VARIATION).bind(songDefaultChartDataEntryLabel)); @@ -740,19 +772,31 @@ class ChartEditorDialogHandler // Build entries for -metadata-.json. var songVariationMetadataEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT); var songVariationMetadataEntryLabel:Label = songVariationMetadataEntry.findComponent('chartEntryLabel', Label); + #if FILE_DROP_SUPPORTED songVariationMetadataEntryLabel.text = 'Drag and drop -metadata-${variation}.json file, or click to browse.'; + #else + songVariationMetadataEntryLabel.text = 'Click to browse for -metadata-${variation}.json file.'; + #end songVariationMetadataEntry.onClick = onClickMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel); + #if FILE_DROP_SUPPORTED addDropHandler(songVariationMetadataEntry, onDropFileMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel)); + #end chartContainerB.addComponent(songVariationMetadataEntry); // Build entries for -chart-.json. var songVariationChartDataEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT); var songVariationChartDataEntryLabel:Label = songVariationChartDataEntry.findComponent('chartEntryLabel', Label); + #if FILE_DROP_SUPPORTED songVariationChartDataEntryLabel.text = 'Drag and drop -chart-${variation}.json file, or click to browse.'; + #else + songVariationChartDataEntryLabel.text = 'Click to browse for -chart-${variation}.json file.'; + #end songVariationChartDataEntry.onClick = onClickChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel); + #if FILE_DROP_SUPPORTED addDropHandler(songVariationChartDataEntry, onDropFileChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel)); + #end chartContainerB.addComponent(songVariationChartDataEntry); } } @@ -789,7 +833,11 @@ class ChartEditorDialogHandler expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #if FILE_DROP_SUPPORTED label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; + #else + label.text = 'Metadata file (click to browse)\n${path.file}.${path.ext}'; + #end if (variation == Constants.DEFAULT_VARIATION) constructVariationEntries(songMetadataVariation.playData.songVariations); }; @@ -817,7 +865,11 @@ class ChartEditorDialogHandler expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #if FILE_DROP_SUPPORTED label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}'; + #else + label.text = 'Metadata file (click to browse)\n${selectedFile.name}'; + #end if (variation == Constants.DEFAULT_VARIATION) constructVariationEntries(songMetadataVariation.playData.songVariations); } @@ -846,7 +898,11 @@ class ChartEditorDialogHandler expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #if FILE_DROP_SUPPORTED label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; + #else + label.text = 'Chart data file (click to browse)\n${path.file}.${path.ext}'; + #end }; onClickChartDataVariation = function(variation:String, label:Label, _event:UIEvent) { @@ -874,14 +930,28 @@ class ChartEditorDialogHandler expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #if FILE_DROP_SUPPORTED label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}'; + #else + label.text = 'Chart data file (click to browse)\n${selectedFile.name}'; + #end } }); } var metadataEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT); var metadataEntryLabel:Label = metadataEntry.findComponent('chartEntryLabel', Label); + #if FILE_DROP_UNSUPPORTED + trace('File drop unsupported'); + #elseif FILE_DROP_SUPPORTED + trace('File drop supported'); + #end + + #if FILE_DROP_SUPPORTED metadataEntryLabel.text = 'Drag and drop -metadata.json file, or click to browse.'; + #else + metadataEntryLabel.text = 'Click to browse for -metadata.json file.'; + #end metadataEntry.onClick = onClickMetadataVariation.bind(Constants.DEFAULT_VARIATION).bind(metadataEntryLabel); addDropHandler(metadataEntry, onDropFileMetadataVariation.bind(Constants.DEFAULT_VARIATION).bind(metadataEntryLabel)); diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f27fbb6c7..0fe6909e7 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2555,8 +2555,7 @@ class ChartEditorState extends HaxeUIState if (gridGhostNote == null) throw "ERROR: Tried to handle cursor, but gridGhostNote is null! Check ChartEditorState.buildGrid()"; - var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, - selectedNoteKind); + var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, selectedNoteKind); if (cursorColumn != noteData.data || selectedNoteKind != noteData.kind) { @@ -2717,12 +2716,12 @@ class ChartEditorState extends HaxeUIState trace('Creating new Note... (${renderedNotes.members.length})'); noteSprite.parentState = this; - // The note sprite handles animation playback and positioning. - noteSprite.noteData = noteData; - // Setting note data resets position relative to the grid so we fix that. noteSprite.updateNotePosition(renderedNotes); + // The note sprite handles animation playback and positioning. + noteSprite.noteData = noteData; + // Add hold notes that are now visible (and not already displayed). if (noteSprite.noteData != null && noteSprite.noteData.length > 0 && displayedHoldNoteData.indexOf(noteSprite.noteData) == -1) { diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index 21c2920d9..3a6f4e330 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -240,6 +240,10 @@ class FileUtil onSaveAll(paths); } + trace('Browsing for directory to save individual files to...'); + #if mac + defaultPath = null; + #end browseForDirectory(null, onSelectDir, onCancel, defaultPath, 'Choose directory to save all files to...'); return true; From c24fa3c154817d9eeb9fded5ac0583cee0d8d9f3 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 8 Sep 2023 17:27:14 -0400 Subject: [PATCH 08/16] Fix issue where playbar scroll area wasn't scaled to the grid. --- .../ui/debug/charting/ChartEditorState.hx | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f27fbb6c7..5090c791a 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1282,9 +1282,13 @@ class ChartEditorState extends HaxeUIState buildNoteGroup(); - gridPlayheadScrollArea = new FlxSprite(gridTiledSprite.x - PLAYHEAD_SCROLL_AREA_WIDTH, - MENU_BAR_HEIGHT).makeGraphic(PLAYHEAD_SCROLL_AREA_WIDTH, FlxG.height - MENU_BAR_HEIGHT, PLAYHEAD_SCROLL_AREA_COLOR); + gridPlayheadScrollArea = new FlxSprite(0, 0); + gridPlayheadScrollArea.makeGraphic(10, 10, PLAYHEAD_SCROLL_AREA_COLOR); // Make it 10x10px and then scale it as needed. add(gridPlayheadScrollArea); + gridPlayheadScrollArea.setGraphicSize(PLAYHEAD_SCROLL_AREA_WIDTH, 3000); + gridPlayheadScrollArea.updateHitbox(); + gridPlayheadScrollArea.x = gridTiledSprite.x - PLAYHEAD_SCROLL_AREA_WIDTH; + gridPlayheadScrollArea.y = MENU_BAR_HEIGHT + GRID_TOP_PAD; gridPlayheadScrollArea.zIndex = 25; // The playhead that show the current position in the song. @@ -3685,25 +3689,29 @@ class ChartEditorState extends HaxeUIState this.scrollPositionInPixels = value; // Move the grid sprite to the correct position. - if (isViewDownscroll) + if (gridTiledSprite != null) { - if (gridTiledSprite != null) gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); - } - else - { - if (gridTiledSprite != null) gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); + if (isViewDownscroll) + { + gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); + gridPlayheadScrollArea.y = gridTiledSprite.y; + } + else + { + gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); + gridPlayheadScrollArea.y = gridTiledSprite.y; + } } + // Move the rendered notes to the correct position. renderedNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); renderedHoldNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); renderedEvents.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); renderedSelectionSquares.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); - // Offset the selection box start position, if we are dragging. if (selectionBoxStartPos != null) selectionBoxStartPos.y -= diff; // Update the note preview viewport box. setNotePreviewViewportBounds(calculateNotePreviewViewportBounds()); - return this.scrollPositionInPixels; } @@ -3862,6 +3870,11 @@ class ChartEditorState extends HaxeUIState songLengthInMs = audioInstTrack.length; if (gridTiledSprite != null) gridTiledSprite.height = songLengthInPixels; + if (gridPlayheadScrollArea != null) + { + gridPlayheadScrollArea.setGraphicSize(Std.int(gridPlayheadScrollArea.width), songLengthInPixels); + gridPlayheadScrollArea.updateHitbox(); + } buildSpectrogram(audioInstTrack); } From 2dad5953fd393bc3723a5452d8ef6723d5deedd2 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 11 Sep 2023 17:42:46 -0400 Subject: [PATCH 09/16] I hope someone got fired for that blunder. --- source/funkin/ui/debug/charting/ChartEditorState.hx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 0fe6909e7..2adc99569 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2716,12 +2716,13 @@ class ChartEditorState extends HaxeUIState trace('Creating new Note... (${renderedNotes.members.length})'); noteSprite.parentState = this; - // Setting note data resets position relative to the grid so we fix that. - noteSprite.updateNotePosition(renderedNotes); - // The note sprite handles animation playback and positioning. noteSprite.noteData = noteData; + // Setting note data resets the position relative to the group! + // If we don't update the note position AFTER setting the note data, the note will be rendered offscreen at y=5000. + noteSprite.updateNotePosition(renderedNotes); + // Add hold notes that are now visible (and not already displayed). if (noteSprite.noteData != null && noteSprite.noteData.length > 0 && displayedHoldNoteData.indexOf(noteSprite.noteData) == -1) { From d3f5a81b1301cd9426f697ad27e39fde15d4bf4b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 11 Sep 2023 18:29:08 -0400 Subject: [PATCH 10/16] Fix a bug where ghost note would be properly snapped but placed note would not. --- source/funkin/ui/debug/charting/ChartEditorState.hx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index fe2ab6dab..52468070d 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2414,7 +2414,7 @@ class ChartEditorState extends HaxeUIState // Handle extending the note as you drag. // TODO: This should be beat snapped? - var dragLengthSteps:Float = Conductor.getTimeInSteps(cursorMs) - currentPlaceNoteData.stepTime; + var dragLengthSteps:Float = Conductor.getTimeInSteps(cursorSnappedMs) - currentPlaceNoteData.stepTime; // Without this, the newly placed note feels too short compared to the user's input. var INCREMENT:Float = 1.0; @@ -2508,14 +2508,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(cursorMs, selectedEventKind, selectedEventData); + var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData); performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL)); } else { // Create a note and place it in the chart. - var newNoteData:SongNoteData = new SongNoteData(cursorMs, cursorColumn, 0, selectedNoteKind); + var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); @@ -2598,8 +2598,7 @@ class ChartEditorState extends HaxeUIState if (gridGhostNote == null) throw "ERROR: Tried to handle cursor, but gridGhostNote is null! Check ChartEditorState.buildGrid()"; - var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, - selectedNoteKind); + var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, selectedNoteKind); if (cursorColumn != noteData.data || selectedNoteKind != noteData.kind) { From a63246ff517377d9c8f861e3d4af046e5963c9ad Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 12 Sep 2023 00:25:05 -0400 Subject: [PATCH 11/16] Remove notification on snapping change. --- .../ui/debug/charting/ChartEditorState.hx | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 52468070d..ab9cc6808 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2048,29 +2048,11 @@ class ChartEditorState extends HaxeUIState if (FlxG.keys.justPressed.LEFT) { noteSnapQuantIndex--; - #if !mac - NotificationManager.instance.addNotification( - { - title: 'Note Snapping', - body: 'Updated note snapping to 1/${noteSnapQuant}', - type: NotificationType.Success, - expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME - }); - #end } if (FlxG.keys.justPressed.RIGHT) { noteSnapQuantIndex++; - #if !mac - NotificationManager.instance.addNotification( - { - title: 'Note Snapping', - body: 'Updated note snapping to 1/${noteSnapQuant}', - type: NotificationType.Success, - expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME - }); - #end } } From aa1ca65c35d2318c08781487a2fa2ddc87f970b4 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 12 Sep 2023 01:17:15 -0400 Subject: [PATCH 12/16] Fix issue where title text wasn't displaying properly. --- source/funkin/ui/story/StoryMenuState.hx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 4683c3456..34dd49e22 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -135,6 +135,7 @@ class StoryMenuState extends MusicBeatState this.bgColor = FlxColor.BLACK; levelTitles = new FlxTypedGroup(); + levelTitles.zIndex = 15; add(levelTitles); updateBackground(); @@ -157,17 +158,20 @@ class StoryMenuState extends MusicBeatState scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420'); scoreText.setFormat("VCR OSD Mono", 32); + scoreText.zIndex = 1000; add(scoreText); modeText = new FlxText(10, 10, 0, 'Base Game Levels [TAB to switch]'); modeText.setFormat("VCR OSD Mono", 32); modeText.screenCenter(X); modeText.visible = hasModdedLevels(); + modeText.zIndex = 1000; add(modeText); levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1'); levelTitleText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT); levelTitleText.alpha = 0.7; + levelTitleText.zIndex = 1000; add(levelTitleText); buildLevelTitles(); From fa28932deaa53fe5e14f2bb2fc95f1f0cab5cf9d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 12 Sep 2023 18:37:59 -0400 Subject: [PATCH 13/16] Implementing custom cursor modes for better interface readability. --- source/funkin/input/Cursor.hx | 300 ++++++++++++++++-- source/funkin/play/song/SongMigrator.hx | 2 +- .../charting/ChartEditorDialogHandler.hx | 28 +- .../ui/debug/charting/ChartEditorState.hx | 99 ++++-- .../ui/haxeui/components/CharacterPlayer.hx | 12 +- .../ui/haxeui/components/FunkinButton.hx | 30 ++ .../components/FunkinHorizontalSlider.hx | 30 ++ .../funkin/ui/haxeui/components/FunkinLink.hx | 30 ++ .../ui/haxeui/components/FunkinMenuBar.hx | 40 +++ .../haxeui/components/FunkinMenuCheckBox.hx | 30 ++ .../ui/haxeui/components/FunkinMenuItem.hx | 30 ++ .../haxeui/components/FunkinMenuOptionBox.hx | 30 ++ 12 files changed, 594 insertions(+), 67 deletions(-) create mode 100644 source/funkin/ui/haxeui/components/FunkinButton.hx create mode 100644 source/funkin/ui/haxeui/components/FunkinHorizontalSlider.hx create mode 100644 source/funkin/ui/haxeui/components/FunkinLink.hx create mode 100644 source/funkin/ui/haxeui/components/FunkinMenuBar.hx create mode 100644 source/funkin/ui/haxeui/components/FunkinMenuCheckBox.hx create mode 100644 source/funkin/ui/haxeui/components/FunkinMenuItem.hx create mode 100644 source/funkin/ui/haxeui/components/FunkinMenuOptionBox.hx 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 e5b2d332c..3d1819403 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; @@ -122,7 +123,7 @@ class ChartEditorDialogHandler trace('[WARN] Could not fetch song name for ${targetSongId}'); } - var linkTemplateSong:Link = new Link(); + var linkTemplateSong:Link = new FunkinLink(); linkTemplateSong.text = songName; linkTemplateSong.onClick = function(_event) { dialog.hideDialog(DialogButton.CANCEL); @@ -742,6 +743,14 @@ class ChartEditorDialogHandler var songVariationMetadataEntryLabel:Label = songVariationMetadataEntry.findComponent('chartEntryLabel', Label); songVariationMetadataEntryLabel.text = 'Drag and drop -metadata-${variation}.json file, or click to browse.'; + songVariationMetadataEntry.onMouseOver = function(_event) { + songVariationMetadataEntry.swapClass('upload-bg', 'upload-bg-hover'); + Cursor.cursorMode = Pointer; + } + songVariationMetadataEntry.onMouseOut = function(_event) { + songVariationMetadataEntry.swapClass('upload-bg-hover', 'upload-bg'); + Cursor.cursorMode = Default; + } songVariationMetadataEntry.onClick = onClickMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel); addDropHandler(songVariationMetadataEntry, onDropFileMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel)); chartContainerB.addComponent(songVariationMetadataEntry); @@ -751,6 +760,14 @@ class ChartEditorDialogHandler var songVariationChartDataEntryLabel:Label = songVariationChartDataEntry.findComponent('chartEntryLabel', Label); songVariationChartDataEntryLabel.text = 'Drag and drop -chart-${variation}.json file, or click to browse.'; + songVariationChartDataEntry.onMouseOver = function(_event) { + songVariationChartDataEntry.swapClass('upload-bg', 'upload-bg-hover'); + Cursor.cursorMode = Pointer; + } + songVariationChartDataEntry.onMouseOut = function(_event) { + songVariationChartDataEntry.swapClass('upload-bg-hover', 'upload-bg'); + Cursor.cursorMode = Default; + } songVariationChartDataEntry.onClick = onClickChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel); addDropHandler(songVariationChartDataEntry, onDropFileChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel)); chartContainerB.addComponent(songVariationChartDataEntry); @@ -885,6 +902,14 @@ class ChartEditorDialogHandler metadataEntry.onClick = onClickMetadataVariation.bind(Constants.DEFAULT_VARIATION).bind(metadataEntryLabel); addDropHandler(metadataEntry, onDropFileMetadataVariation.bind(Constants.DEFAULT_VARIATION).bind(metadataEntryLabel)); + metadataEntry.onMouseOver = function(_event) { + metadataEntry.swapClass('upload-bg', 'upload-bg-hover'); + Cursor.cursorMode = Pointer; + } + metadataEntry.onMouseOut = function(_event) { + metadataEntry.swapClass('upload-bg-hover', 'upload-bg'); + Cursor.cursorMode = Default; + } chartContainerA.addComponent(metadataEntry); @@ -928,7 +953,6 @@ class ChartEditorDialogHandler importBox.swapClass('upload-bg', 'upload-bg-hover'); Cursor.cursorMode = Pointer; } - importBox.onMouseOut = function(_event) { importBox.swapClass('upload-bg-hover', 'upload-bg'); Cursor.cursorMode = Default; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 5090c791a..7f6e2b679 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1195,6 +1195,9 @@ class ChartEditorState extends HaxeUIState // Set the z-index of the HaxeUI. this.component.zIndex = 100; + // Show the mouse cursor. + Cursor.show(); + fixCamera(); // Get rid of any music from the previous state. @@ -2050,6 +2053,11 @@ class ChartEditorState extends HaxeUIState if (shouldHandleCursor) { + // Over the course of this big conditional block, + // we determine what the cursor should look like, + // and fall back to the default cursor if none of the conditions are met. + var targetCursorMode:Null = null; + if (gridTiledSprite == null) throw "ERROR: Tried to handle cursor, but gridTiledSprite is null! Check ChartEditorState.buildGrid()"; var overlapsGrid:Bool = FlxG.mouse.overlaps(gridTiledSprite); @@ -2059,9 +2067,9 @@ class ChartEditorState extends HaxeUIState var cursorY:Float = FlxG.mouse.screenY - gridTiledSprite.y; var overlapsSelectionBorder:Bool = overlapsGrid - && (cursorX % 40) < (GRID_SELECTION_BORDER_WIDTH / 2) + && ((cursorX % 40) < (GRID_SELECTION_BORDER_WIDTH / 2) || (cursorX % 40) > (40 - (GRID_SELECTION_BORDER_WIDTH / 2)) - || (cursorY % 40) < (GRID_SELECTION_BORDER_WIDTH / 2) || (cursorY % 40) > (40 - (GRID_SELECTION_BORDER_WIDTH / 2)); + || (cursorY % 40) < (GRID_SELECTION_BORDER_WIDTH / 2) || (cursorY % 40) > (40 - (GRID_SELECTION_BORDER_WIDTH / 2))); if (FlxG.mouse.justPressed) { @@ -2077,6 +2085,8 @@ class ChartEditorState extends HaxeUIState else if (!overlapsGrid || overlapsSelectionBorder) { selectionBoxStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY); + // Drawing selection box. + targetCursorMode = Crosshair; } else { @@ -2087,23 +2097,6 @@ class ChartEditorState extends HaxeUIState } } - if (gridPlayheadScrollAreaPressed) - { - Cursor.cursorMode = Grabbing; - } - else if (notePreviewScrollAreaStartPos != null) - { - Cursor.cursorMode = Pointer; - } - else if (gridPlayheadScrollArea != null && FlxG.mouse.overlaps(gridPlayheadScrollArea)) - { - Cursor.cursorMode = Pointer; - } - else - { - Cursor.cursorMode = Default; - } - if (gridPlayheadScrollAreaPressed && FlxG.mouse.released) { gridPlayheadScrollAreaPressed = false; @@ -2120,6 +2113,9 @@ class ChartEditorState extends HaxeUIState // Move the playhead to the cursor position. this.playheadPositionInPixels = FlxG.mouse.screenY - MENU_BAR_HEIGHT - GRID_TOP_PAD; moveSongToScrollPosition(); + + // Cursor should be a grabby hand. + if (targetCursorMode == null) targetCursorMode = Grabbing; } // The song position of the cursor, in steps. @@ -2272,6 +2268,8 @@ class ChartEditorState extends HaxeUIState selectionRect.width = Math.abs(FlxG.mouse.screenX - selectionBoxStartPos.x); selectionRect.height = Math.abs(FlxG.mouse.screenY - selectionBoxStartPos.y); setSelectionBoxBounds(selectionRect); + + targetCursorMode = Crosshair; } } else if (FlxG.mouse.justReleased) @@ -2363,7 +2361,9 @@ class ChartEditorState extends HaxeUIState } else if (notePreviewScrollAreaStartPos != null) { - trace('Updating current song time while clicking and holding...'); + // Player is clicking and holding on note preview to scrub around. + targetCursorMode = Grabbing; + var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.screenY, (notePreview?.y ?? 0.0), (notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0), 0, songLengthInPixels); @@ -2531,8 +2531,6 @@ class ChartEditorState extends HaxeUIState // Handle grid cursor. if (overlapsGrid && !overlapsSelectionBorder && !gridPlayheadScrollAreaPressed) { - Cursor.cursorMode = Pointer; - // Indicate that we can place a note here. if (cursorColumn == eventColumn) @@ -2552,6 +2550,8 @@ class ChartEditorState extends HaxeUIState gridGhostEvent.visible = true; gridGhostEvent.eventData = eventData; gridGhostEvent.updateEventPosition(renderedEvents); + + targetCursorMode = Cell; } else { @@ -2573,30 +2573,56 @@ class ChartEditorState extends HaxeUIState gridGhostNote.visible = true; gridGhostNote.noteData = noteData; gridGhostNote.updateNotePosition(renderedNotes); - } - // gridCursor.visible = true; - // // X and Y are the cursor position relative to the grid, snapped to the top left of the grid square. - // gridCursor.x = Math.floor(cursorX / GRID_SIZE) * GRID_SIZE + gridTiledSprite.x + (GRID_SELECTION_BORDER_WIDTH / 2); - // gridCursor.y = cursorStep * GRID_SIZE + gridTiledSprite.y + (GRID_SELECTION_BORDER_WIDTH / 2); + targetCursorMode = Cell; + } } else { if (gridGhostNote != null) gridGhostNote.visible = false; if (gridGhostEvent != null) gridGhostEvent.visible = false; - Cursor.cursorMode = Default; } } + + if (targetCursorMode == null) + { + if (FlxG.mouse.pressed) + { + if (overlapsSelectionBorder) + { + targetCursorMode = Crosshair; + } + } + else + { + if (FlxG.mouse.overlaps(notePreview)) + { + targetCursorMode = Pointer; + } + else if (FlxG.mouse.overlaps(gridPlayheadScrollArea)) + { + targetCursorMode = Pointer; + } + else if (overlapsSelectionBorder) + { + targetCursorMode = Crosshair; + } + else if (overlapsGrid) + { + targetCursorMode = Cell; + } + } + } + + // Actually set the cursor mode to the one we specified earlier. + Cursor.cursorMode = targetCursorMode ?? Default; } else { if (gridGhostNote != null) gridGhostNote.visible = false; if (gridGhostEvent != null) gridGhostEvent.visible = false; - } - if (isCursorOverHaxeUIButton && Cursor.cursorMode == Default) - { - Cursor.cursorMode = Pointer; + // Do not set Cursor.cursorMode here, because it will be set by the HaxeUI. } } @@ -2842,10 +2868,10 @@ class ChartEditorState extends HaxeUIState } // Sort the notes DESCENDING. This keeps the sustain behind the associated note. - renderedNotes.sort(FlxSort.byY, FlxSort.DESCENDING); + renderedNotes.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort() // Sort the events DESCENDING. This keeps the sustain behind the associated note. - renderedEvents.sort(FlxSort.byY, FlxSort.DESCENDING); + renderedEvents.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort() } // Add a debug value which displays the current size of the note pool. @@ -4159,10 +4185,12 @@ class ChartEditorState extends HaxeUIState function sortChartData():Void { + // TODO: .insertionSort() currentSongChartNoteData.sort(function(a:SongNoteData, b:SongNoteData):Int { return FlxSort.byValues(FlxSort.ASCENDING, a.time, b.time); }); + // TODO: .insertionSort() currentSongChartEventData.sort(function(a:SongEventData, b:SongEventData):Int { return FlxSort.byValues(FlxSort.ASCENDING, a.time, b.time); }); @@ -4210,6 +4238,9 @@ class ChartEditorState extends HaxeUIState cleanupAutoSave(); + // Hide the mouse cursor on other states. + Cursor.hide(); + @:privateAccess ChartEditorNoteSprite.noteFrameCollection = null; } diff --git a/source/funkin/ui/haxeui/components/CharacterPlayer.hx b/source/funkin/ui/haxeui/components/CharacterPlayer.hx index c638e8a72..daeb17fc9 100644 --- a/source/funkin/ui/haxeui/components/CharacterPlayer.hx +++ b/source/funkin/ui/haxeui/components/CharacterPlayer.hx @@ -30,7 +30,7 @@ typedef AnimationInfo = @:composite(Layout) class CharacterPlayer extends Box { - var character:BaseCharacter; + var character:Null; public function new(defaultToBf:Bool = true) { @@ -47,7 +47,7 @@ class CharacterPlayer extends Box function get_charId():String { - return character.characterId; + return character?.characterId ?? ''; } function set_charId(value:String):String @@ -60,7 +60,7 @@ class CharacterPlayer extends Box function get_charName():String { - return character.characterName; + return character?.characterName ?? "Unknown"; } // possible haxeui bug: if listener is added after event is dispatched, event is "lost"... is it smart to "collect and redispatch"? Not sure @@ -86,7 +86,11 @@ class CharacterPlayer extends Box // Prevent script issues by fetching with debug=true. var newCharacter:BaseCharacter = CharacterDataParser.fetchCharacter(id, true); - if (newCharacter == null) return; // Fail if character doesn't exist. + if (newCharacter == null) + { + character = null; + return; // Fail if character doesn't exist. + } // Assign character. character = newCharacter; diff --git a/source/funkin/ui/haxeui/components/FunkinButton.hx b/source/funkin/ui/haxeui/components/FunkinButton.hx new file mode 100644 index 000000000..45987b9ec --- /dev/null +++ b/source/funkin/ui/haxeui/components/FunkinButton.hx @@ -0,0 +1,30 @@ +package funkin.ui.haxeui.components; + +import funkin.input.Cursor; +import haxe.ui.events.MouseEvent; +import haxe.ui.components.Button; + +/** + * A HaxeUI button which: + * - Changes the current cursor when hovered over. + */ +class FunkinButton extends Button +{ + public function new() + { + super(); + + this.onMouseOver = handleMouseOver; + this.onMouseOut = handleMouseOut; + } + + private function handleMouseOver(event:MouseEvent) + { + Cursor.cursorMode = Pointer; + } + + private function handleMouseOut(event:MouseEvent) + { + Cursor.cursorMode = Default; + } +} diff --git a/source/funkin/ui/haxeui/components/FunkinHorizontalSlider.hx b/source/funkin/ui/haxeui/components/FunkinHorizontalSlider.hx new file mode 100644 index 000000000..baf42aada --- /dev/null +++ b/source/funkin/ui/haxeui/components/FunkinHorizontalSlider.hx @@ -0,0 +1,30 @@ +package funkin.ui.haxeui.components; + +import haxe.ui.components.HorizontalSlider; +import funkin.input.Cursor; +import haxe.ui.events.MouseEvent; + +/** + * A HaxeUI horizontal slider which: + * - Changes the current cursor when hovered over. + */ +class FunkinHorizontalSlider extends HorizontalSlider +{ + public function new() + { + super(); + + this.onMouseOver = handleMouseOver; + this.onMouseOut = handleMouseOut; + } + + private function handleMouseOver(event:MouseEvent) + { + Cursor.cursorMode = Pointer; + } + + private function handleMouseOut(event:MouseEvent) + { + Cursor.cursorMode = Default; + } +} diff --git a/source/funkin/ui/haxeui/components/FunkinLink.hx b/source/funkin/ui/haxeui/components/FunkinLink.hx new file mode 100644 index 000000000..74eb6e7c4 --- /dev/null +++ b/source/funkin/ui/haxeui/components/FunkinLink.hx @@ -0,0 +1,30 @@ +package funkin.ui.haxeui.components; + +import funkin.input.Cursor; +import haxe.ui.events.MouseEvent; +import haxe.ui.components.Link; + +/** + * A HaxeUI link which: + * - Changes the current cursor when hovered over. + */ +class FunkinLink extends Link +{ + public function new() + { + super(); + + this.onMouseOver = handleMouseOver; + this.onMouseOut = handleMouseOut; + } + + private function handleMouseOver(event:MouseEvent) + { + Cursor.cursorMode = Pointer; + } + + private function handleMouseOut(event:MouseEvent) + { + Cursor.cursorMode = Default; + } +} diff --git a/source/funkin/ui/haxeui/components/FunkinMenuBar.hx b/source/funkin/ui/haxeui/components/FunkinMenuBar.hx new file mode 100644 index 000000000..1dcfb770c --- /dev/null +++ b/source/funkin/ui/haxeui/components/FunkinMenuBar.hx @@ -0,0 +1,40 @@ +package funkin.ui.haxeui.components; + +import funkin.input.Cursor; +import haxe.ui.events.MouseEvent; +import haxe.ui.containers.menus.MenuBar; +import haxe.ui.core.CompositeBuilder; + +/** + * A HaxeUI menu bar which: + * - Changes the current cursor when each button is hovered over. + */ +class FunkinMenuBar extends MenuBar +{ + public function new() + { + super(); + + registerListeners(); + } + + private function registerListeners():Void + { + var builder = cast(this._compositeBuilder, MenuBar.Builder); + for (button in builder._buttons) + { + button.onMouseOver = handleMouseOver; + button.onMouseOut = handleMouseOut; + } + } + + private function handleMouseOver(event:MouseEvent) + { + Cursor.cursorMode = Pointer; + } + + private function handleMouseOut(event:MouseEvent) + { + Cursor.cursorMode = Default; + } +} diff --git a/source/funkin/ui/haxeui/components/FunkinMenuCheckBox.hx b/source/funkin/ui/haxeui/components/FunkinMenuCheckBox.hx new file mode 100644 index 000000000..263277c6f --- /dev/null +++ b/source/funkin/ui/haxeui/components/FunkinMenuCheckBox.hx @@ -0,0 +1,30 @@ +package funkin.ui.haxeui.components; + +import funkin.input.Cursor; +import haxe.ui.events.MouseEvent; +import haxe.ui.containers.menus.MenuCheckBox; + +/** + * A HaxeUI menu checkbox which: + * - Changes the current cursor when hovered over. + */ +class FunkinMenuCheckBox extends MenuCheckBox +{ + public function new() + { + super(); + + this.onMouseOver = handleMouseOver; + this.onMouseOut = handleMouseOut; + } + + private function handleMouseOver(event:MouseEvent) + { + Cursor.cursorMode = Pointer; + } + + private function handleMouseOut(event:MouseEvent) + { + Cursor.cursorMode = Default; + } +} diff --git a/source/funkin/ui/haxeui/components/FunkinMenuItem.hx b/source/funkin/ui/haxeui/components/FunkinMenuItem.hx new file mode 100644 index 000000000..2eb7db729 --- /dev/null +++ b/source/funkin/ui/haxeui/components/FunkinMenuItem.hx @@ -0,0 +1,30 @@ +package funkin.ui.haxeui.components; + +import funkin.input.Cursor; +import haxe.ui.events.MouseEvent; +import haxe.ui.containers.menus.MenuItem; + +/** + * A HaxeUI menu item which: + * - Changes the current cursor when hovered over. + */ +class FunkinMenuItem extends MenuItem +{ + public function new() + { + super(); + + this.onMouseOver = handleMouseOver; + this.onMouseOut = handleMouseOut; + } + + private function handleMouseOver(event:MouseEvent) + { + Cursor.cursorMode = Pointer; + } + + private function handleMouseOut(event:MouseEvent) + { + Cursor.cursorMode = Default; + } +} diff --git a/source/funkin/ui/haxeui/components/FunkinMenuOptionBox.hx b/source/funkin/ui/haxeui/components/FunkinMenuOptionBox.hx new file mode 100644 index 000000000..d9985eede --- /dev/null +++ b/source/funkin/ui/haxeui/components/FunkinMenuOptionBox.hx @@ -0,0 +1,30 @@ +package funkin.ui.haxeui.components; + +import haxe.ui.containers.menus.MenuOptionBox; +import funkin.input.Cursor; +import haxe.ui.events.MouseEvent; + +/** + * A HaxeUI menu option box which: + * - Changes the current cursor when hovered over. + */ +class FunkinMenuOptionBox extends MenuOptionBox +{ + public function new() + { + super(); + + this.onMouseOver = handleMouseOver; + this.onMouseOut = handleMouseOut; + } + + private function handleMouseOver(event:MouseEvent) + { + Cursor.cursorMode = Pointer; + } + + private function handleMouseOut(event:MouseEvent) + { + Cursor.cursorMode = Default; + } +} From 7083f6e7ce40160936bd12fc2d2a5f5c5918c870 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 12 Sep 2023 19:40:58 -0400 Subject: [PATCH 14/16] Additional cursor and playbar fixes. --- .../ui/debug/charting/ChartEditorState.hx | 20 +++++++++++-------- .../ui/haxeui/components/FunkinMenuBar.hx | 10 +--------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 7f6e2b679..a68067f16 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1877,13 +1877,15 @@ class ChartEditorState extends HaxeUIState if (pageUpKeyHandler.activated) { var measureHeight:Float = GRID_SIZE * 4 * Conductor.beatsPerMeasure; - var targetScrollPosition:Float = Math.floor(scrollPositionInPixels / measureHeight) * measureHeight; + var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; + var targetScrollPosition:Float = Math.floor(playheadPos / measureHeight) * measureHeight; // If we would move less than one grid, instead move to the top of the previous measure. - if (Math.abs(targetScrollPosition - scrollPositionInPixels) < GRID_SIZE) + var targetScrollAmount = Math.abs(targetScrollPosition - playheadPos); + if (targetScrollAmount < GRID_SIZE) { - targetScrollPosition -= GRID_SIZE * 4 * Conductor.beatsPerMeasure; + targetScrollPosition -= GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.beatsPerMeasure; } - scrollAmount = targetScrollPosition - scrollPositionInPixels; + scrollAmount = targetScrollPosition - playheadPos; shouldPause = true; } @@ -1898,13 +1900,15 @@ class ChartEditorState extends HaxeUIState if (pageDownKeyHandler.activated) { var measureHeight:Float = GRID_SIZE * 4 * Conductor.beatsPerMeasure; - var targetScrollPosition:Float = Math.ceil(scrollPositionInPixels / measureHeight) * measureHeight; + var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; + var targetScrollPosition:Float = Math.ceil(playheadPos / measureHeight) * measureHeight; // If we would move less than one grid, instead move to the top of the next measure. - if (Math.abs(targetScrollPosition - scrollPositionInPixels) < GRID_SIZE) + var targetScrollAmount = Math.abs(targetScrollPosition - playheadPos); + if (targetScrollAmount < GRID_SIZE) { - targetScrollPosition += GRID_SIZE * 4 * Conductor.beatsPerMeasure; + targetScrollPosition += GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.beatsPerMeasure; } - scrollAmount = targetScrollPosition - scrollPositionInPixels; + scrollAmount = targetScrollPosition - playheadPos; shouldPause = true; } diff --git a/source/funkin/ui/haxeui/components/FunkinMenuBar.hx b/source/funkin/ui/haxeui/components/FunkinMenuBar.hx index 1dcfb770c..393372d74 100644 --- a/source/funkin/ui/haxeui/components/FunkinMenuBar.hx +++ b/source/funkin/ui/haxeui/components/FunkinMenuBar.hx @@ -18,15 +18,7 @@ class FunkinMenuBar extends MenuBar registerListeners(); } - private function registerListeners():Void - { - var builder = cast(this._compositeBuilder, MenuBar.Builder); - for (button in builder._buttons) - { - button.onMouseOver = handleMouseOver; - button.onMouseOut = handleMouseOut; - } - } + private function registerListeners():Void {} private function handleMouseOver(event:MouseEvent) { From e02dee2601f728ab3a40a1125400465bb8b0fa94 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 12 Sep 2023 22:34:49 -0400 Subject: [PATCH 15/16] fixed N -> O typo? --- source/funkin/ui/debug/charting/ChartEditorState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index e51a94177..45a9c98a7 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2906,7 +2906,7 @@ class ChartEditorState extends HaxeUIState } // CTRL + O = Open Chart - if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.N) + if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.O) { ChartEditorDialogHandler.openBrowseWizard(this, true); } From 35239909d0fec6173a5a010d584ea02f33e24c35 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 18 Sep 2023 15:48:40 -0400 Subject: [PATCH 16/16] woops fix conditional --- source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx index 0a4e68ad1..a32aed862 100644 --- a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx @@ -653,8 +653,6 @@ class ChartEditorDialogHandler expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); - #if FILE_DROP_SUPPORTED - vocalsEntryLabel.text = 'Vocals for $charName (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; #else vocalsEntryLabel.text = 'Vocals for $charName (click to browse)\n${path.file}.${path.ext}';