diff --git a/assets b/assets index f2e37de1f..8104d43e5 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f2e37de1ff308eeaf65babad2a4089096c40cedb +Subproject commit 8104d43e584a1f25e574438d7b21a7e671358969 diff --git a/source/funkin/data/song/SongDataUtils.hx b/source/funkin/data/song/SongDataUtils.hx index 4b9318df2..ee3dfe98c 100644 --- a/source/funkin/data/song/SongDataUtils.hx +++ b/source/funkin/data/song/SongDataUtils.hx @@ -21,11 +21,21 @@ class SongDataUtils * @param notes The notes to modify. * @param offset The time difference to apply in milliseconds. */ - public static function offsetSongNoteData(notes:Array, offset:Int):Array + public static function offsetSongNoteData(notes:Array, offset:Float):Array { - return notes.map(function(note:SongNoteData):SongNoteData { - return new SongNoteData(note.time + offset, note.data, note.length, note.kind); - }); + var offsetNote = function(note:SongNoteData):SongNoteData { + var time:Float = note.time + offset; + var data:Int = note.data; + var length:Float = note.length; + var kind:String = note.kind; + return new SongNoteData(time, data, length, kind); + }; + + trace(notes); + trace(notes[0]); + var result = [for (i in 0...notes.length) offsetNote(notes[i])]; + trace(result); + return result; } /** @@ -36,7 +46,7 @@ class SongDataUtils * @param events The events to modify. * @param offset The time difference to apply in milliseconds. */ - public static function offsetSongEventData(events:Array, offset:Int):Array + public static function offsetSongEventData(events:Array, offset:Float):Array { return events.map(function(event:SongEventData):SongEventData { return new SongEventData(event.time + offset, event.event, event.value); @@ -152,7 +162,8 @@ class SongDataUtils */ public static function writeItemsToClipboard(data:SongClipboardItems):Void { - var dataString = SerializerUtil.toJSON(data); + var writer = new json2object.JsonWriter(); + var dataString:String = writer.write(data, ' '); ClipboardUtil.setClipboard(dataString); @@ -170,19 +181,24 @@ class SongDataUtils trace('Read ${notesString.length} characters from clipboard.'); - var data:SongClipboardItems = notesString.parseJSON(); - - if (data == null) + var parser = new json2object.JsonParser(); + parser.fromJson(notesString, 'clipboard'); + if (parser.errors.length > 0) { - trace('Failed to parse notes from clipboard.'); + trace('[SongDataUtils] Error parsing note JSON data from clipboard.'); + for (error in parser.errors) + DataError.printError(error); return { + valid: false, notes: [], events: [] }; } else { + var data:SongClipboardItems = parser.value; trace('Parsed ' + data.notes.length + ' notes and ' + data.events.length + ' from clipboard.'); + data.valid = true; return data; } } @@ -230,6 +246,7 @@ class SongDataUtils typedef SongClipboardItems = { + ?valid:Bool, notes:Array, events:Array } diff --git a/source/funkin/ui/debug/charting/ChartEditorCommand.hx b/source/funkin/ui/debug/charting/ChartEditorCommand.hx index c358c1d3d..3328336e6 100644 --- a/source/funkin/ui/debug/charting/ChartEditorCommand.hx +++ b/source/funkin/ui/debug/charting/ChartEditorCommand.hx @@ -1,5 +1,7 @@ package funkin.ui.debug.charting; +import haxe.ui.notifications.NotificationType; +import haxe.ui.notifications.NotificationManager; import funkin.data.song.SongData.SongEventData; import funkin.data.song.SongData.SongNoteData; import funkin.data.song.SongDataUtils; @@ -760,6 +762,22 @@ class PasteItemsCommand implements ChartEditorCommand { var currentClipboard:SongClipboardItems = SongDataUtils.readItemsFromClipboard(); + if (currentClipboard.valid != true) + { + #if !mac + NotificationManager.instance.addNotification( + { + title: 'Failed to Paste', + body: 'Could not parse clipboard contents.', + type: NotificationType.Error, + expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME + }); + #end + return; + } + + trace(currentClipboard.notes); + addedNotes = SongDataUtils.offsetSongNoteData(currentClipboard.notes, Std.int(targetTimestamp)); addedEvents = SongDataUtils.offsetSongEventData(currentClipboard.events, Std.int(targetTimestamp)); @@ -773,6 +791,16 @@ class PasteItemsCommand implements ChartEditorCommand state.notePreviewDirty = true; state.sortChartData(); + + #if !mac + NotificationManager.instance.addNotification( + { + title: 'Paste Successful', + body: 'Successfully pasted clipboard contents.', + type: NotificationType.Success, + expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME + }); + #end } public function undo(state:ChartEditorState):Void diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 2f5222cd5..b4c8c3483 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1547,11 +1547,11 @@ class ChartEditorState extends HaxeUIState renderedEvents.setPosition(gridTiledSprite.x, gridTiledSprite.y); add(renderedEvents); - renderedNotes.zIndex = 25; + renderedEvents.zIndex = 25; renderedSelectionSquares.setPosition(gridTiledSprite.x, gridTiledSprite.y); add(renderedSelectionSquares); - renderedNotes.zIndex = 26; + renderedSelectionSquares.zIndex = 26; } function buildAdditionalUI():Void @@ -1662,7 +1662,18 @@ class ChartEditorState extends HaxeUIState addUIClickListener('menubarItemCut', _ -> performCommand(new CutItemsCommand(currentNoteSelection, currentEventSelection))); - addUIClickListener('menubarItemPaste', _ -> performCommand(new PasteItemsCommand(scrollPositionInMs + playheadPositionInMs))); + addUIClickListener('menubarItemPaste', _ -> { + var targetMs:Float = scrollPositionInMs + playheadPositionInMs; + var targetStep:Float = Conductor.getTimeInSteps(targetMs); + var targetSnappedStep:Float = Math.floor(targetStep / noteSnapRatio) * noteSnapRatio; + var targetSnappedMs:Float = Conductor.getStepTimeInMs(targetSnappedStep); + performCommand(new PasteItemsCommand(targetSnappedMs)); + }); + + addUIClickListener('menubarItemPasteUnsnapped', _ -> { + var targetMs:Float = scrollPositionInMs + playheadPositionInMs; + performCommand(new PasteItemsCommand(targetMs)); + }); addUIClickListener('menubarItemDelete', function(_) { if (currentNoteSelection.length > 0 && currentEventSelection.length > 0) @@ -2335,7 +2346,6 @@ class ChartEditorState extends HaxeUIState // Scroll up. var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.screenY; scrollPositionInPixels -= diff * 0.5; // Too fast! - trace('Scroll up: ' + diff); moveSongToScrollPosition(); } else if (FlxG.mouse.screenY > (playbarHeadLayout?.y ?? 0.0)) @@ -2343,7 +2353,6 @@ class ChartEditorState extends HaxeUIState // Scroll down. var diff:Float = FlxG.mouse.screenY - (playbarHeadLayout?.y ?? 0.0); scrollPositionInPixels += diff * 0.5; // Too fast! - trace('Scroll down: ' + diff); moveSongToScrollPosition(); } @@ -2968,8 +2977,8 @@ class ChartEditorState extends HaxeUIState // Set the position and size (because we might be recycling one with bad values). selectionSquare.x = noteSprite.x; selectionSquare.y = noteSprite.y; - selectionSquare.width = noteSprite.width; - selectionSquare.height = noteSprite.height; + selectionSquare.width = GRID_SIZE; + selectionSquare.height = GRID_SIZE; } } @@ -3000,6 +3009,8 @@ class ChartEditorState extends HaxeUIState FlxG.watch.addQuick("tapNotesRendered", renderedNotes.members.length); FlxG.watch.addQuick("holdNotesRendered", renderedHoldNotes.members.length); FlxG.watch.addQuick("eventsRendered", renderedEvents.members.length); + FlxG.watch.addQuick("notesSelected", currentNoteSelection.length); + FlxG.watch.addQuick("eventsSelected", currentEventSelection.length); } /** @@ -3029,6 +3040,8 @@ class ChartEditorState extends HaxeUIState if (selectionSquareBitmap == null) throw "ERROR: Tried to build selection square, but selectionSquareBitmap is null! Check ChartEditorThemeHandler.updateSelectionSquare()"; + FlxG.bitmapLog.add(selectionSquareBitmap, "selectionSquareBitmap"); + return new FlxSprite().loadGraphic(selectionSquareBitmap); } @@ -3148,8 +3161,20 @@ class ChartEditorState extends HaxeUIState // CTRL + V = Paste if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.V) { - // Paste notes from clipboard, at the playhead. - performCommand(new PasteItemsCommand(scrollPositionInMs + playheadPositionInMs)); + // CTRL + SHIFT + V = Paste Unsnapped. + var targetMs:Float = if (FlxG.keys.pressed.SHIFT) + { + scrollPositionInMs + playheadPositionInMs; + } + else + { + var targetMs:Float = scrollPositionInMs + playheadPositionInMs; + var targetStep:Float = Conductor.getTimeInSteps(targetMs); + var targetSnappedStep:Float = Math.floor(targetStep / noteSnapRatio) * noteSnapRatio; + var targetSnappedMs:Float = Conductor.getStepTimeInMs(targetSnappedStep); + targetSnappedMs; + } + performCommand(new PasteItemsCommand(targetMs)); } // DELETE = Delete