mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-08-19 23:15:15 +00:00
Merge pull request #275 from FunkinCrew/feature/chart-editor-drag-edit-holds
Click and drag on a sustain to edit it.
This commit is contained in:
commit
772c1b2626
4
hmm.json
4
hmm.json
|
@ -54,14 +54,14 @@
|
|||
"name": "haxeui-core",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "5086e59e7551d775ed4d1fb0188e31de22d1312b",
|
||||
"ref": "2561076c5abeee0a60f3a2a65a8ecb7832a6a62a",
|
||||
"url": "https://github.com/haxeui/haxeui-core"
|
||||
},
|
||||
{
|
||||
"name": "haxeui-flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "2b9cff727999b53ed292b1675ac1c9089ac77600",
|
||||
"ref": "9c8ab039524086f5a8c8f35b9fb14538b5bfba5d",
|
||||
"url": "https://github.com/haxeui/haxeui-flixel"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -826,7 +826,13 @@ class SongNoteDataRaw implements ICloneable<SongNoteDataRaw>
|
|||
@:alias("l")
|
||||
@:default(0)
|
||||
@:optional
|
||||
public var length:Float;
|
||||
public var length(default, set):Float;
|
||||
|
||||
function set_length(value:Float):Float
|
||||
{
|
||||
_stepLength = null;
|
||||
return length = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The kind of the note.
|
||||
|
@ -883,6 +889,11 @@ class SongNoteDataRaw implements ICloneable<SongNoteDataRaw>
|
|||
return _stepTime = Conductor.instance.getTimeInSteps(this.time);
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the note, if applicable, in steps.
|
||||
* Calculated from the length and the BPM.
|
||||
* Cached for performance. Set to `null` to recalculate.
|
||||
*/
|
||||
@:jignored
|
||||
var _stepLength:Null<Float> = null;
|
||||
|
||||
|
@ -907,9 +918,14 @@ class SongNoteDataRaw implements ICloneable<SongNoteDataRaw>
|
|||
}
|
||||
else
|
||||
{
|
||||
var lengthMs:Float = Conductor.instance.getStepTimeInMs(value) - this.time;
|
||||
var endStep:Float = getStepTime() + value;
|
||||
var endMs:Float = Conductor.instance.getStepTimeInMs(endStep);
|
||||
var lengthMs:Float = endMs - this.time;
|
||||
|
||||
this.length = lengthMs;
|
||||
}
|
||||
|
||||
// Recalculate the step length next time it's requested.
|
||||
_stepLength = null;
|
||||
}
|
||||
|
||||
|
@ -980,6 +996,10 @@ abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw
|
|||
@:op(A == B)
|
||||
public function op_equals(other:SongNoteData):Bool
|
||||
{
|
||||
// Handle the case where one value is null.
|
||||
if (this == null) return other == null;
|
||||
if (other == null) return false;
|
||||
|
||||
if (this.kind == '')
|
||||
{
|
||||
if (other.kind != '' && other.kind != 'normal') return false;
|
||||
|
@ -995,6 +1015,10 @@ abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw
|
|||
@:op(A != B)
|
||||
public function op_notEquals(other:SongNoteData):Bool
|
||||
{
|
||||
// Handle the case where one value is null.
|
||||
if (this == null) return other == null;
|
||||
if (other == null) return false;
|
||||
|
||||
if (this.kind == '')
|
||||
{
|
||||
if (other.kind != '' && other.kind != 'normal') return true;
|
||||
|
@ -1010,24 +1034,32 @@ abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw
|
|||
@:op(A > B)
|
||||
public function op_greaterThan(other:SongNoteData):Bool
|
||||
{
|
||||
if (other == null) return false;
|
||||
|
||||
return this.time > other.time;
|
||||
}
|
||||
|
||||
@:op(A < B)
|
||||
public function op_lessThan(other:SongNoteData):Bool
|
||||
{
|
||||
if (other == null) return false;
|
||||
|
||||
return this.time < other.time;
|
||||
}
|
||||
|
||||
@:op(A >= B)
|
||||
public function op_greaterThanOrEquals(other:SongNoteData):Bool
|
||||
{
|
||||
if (other == null) return false;
|
||||
|
||||
return this.time >= other.time;
|
||||
}
|
||||
|
||||
@:op(A <= B)
|
||||
public function op_lessThanOrEquals(other:SongNoteData):Bool
|
||||
{
|
||||
if (other == null) return false;
|
||||
|
||||
return this.time <= other.time;
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,9 @@ class SustainTrail extends FlxSprite
|
|||
|
||||
public var isPixel:Bool;
|
||||
|
||||
var graphicWidth:Float = 0;
|
||||
var graphicHeight:Float = 0;
|
||||
|
||||
/**
|
||||
* Normally you would take strumTime:Float, noteData:Int, sustainLength:Float, parentNote:Note (?)
|
||||
* @param NoteData
|
||||
|
@ -110,8 +113,8 @@ class SustainTrail extends FlxSprite
|
|||
zoom *= 0.7;
|
||||
|
||||
// CALCULATE SIZE
|
||||
width = graphic.width / 8 * zoom; // amount of notes * 2
|
||||
height = sustainHeight(sustainLength, getScrollSpeed());
|
||||
graphicWidth = graphic.width / 8 * zoom; // amount of notes * 2
|
||||
graphicHeight = sustainHeight(sustainLength, getScrollSpeed());
|
||||
// instead of scrollSpeed, PlayState.SONG.speed
|
||||
|
||||
flipY = Preferences.downscroll;
|
||||
|
@ -148,12 +151,21 @@ class SustainTrail extends FlxSprite
|
|||
|
||||
if (sustainLength == s) return s;
|
||||
|
||||
height = sustainHeight(s, getScrollSpeed());
|
||||
graphicHeight = sustainHeight(s, getScrollSpeed());
|
||||
this.sustainLength = s;
|
||||
updateClipping();
|
||||
updateHitbox();
|
||||
return this.sustainLength;
|
||||
}
|
||||
|
||||
public override function updateHitbox():Void
|
||||
{
|
||||
width = graphicWidth;
|
||||
height = graphicHeight;
|
||||
offset.set(0, 0);
|
||||
origin.set(width * 0.5, height * 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up new vertex and UV data to clip the trail.
|
||||
* If flipY is true, top and bottom bounds swap places.
|
||||
|
@ -161,7 +173,7 @@ class SustainTrail extends FlxSprite
|
|||
*/
|
||||
public function updateClipping(songTime:Float = 0):Void
|
||||
{
|
||||
var clipHeight:Float = FlxMath.bound(sustainHeight(sustainLength - (songTime - strumTime), getScrollSpeed()), 0, height);
|
||||
var clipHeight:Float = FlxMath.bound(sustainHeight(sustainLength - (songTime - strumTime), getScrollSpeed()), 0, graphicHeight);
|
||||
if (clipHeight <= 0.1)
|
||||
{
|
||||
visible = false;
|
||||
|
@ -178,10 +190,10 @@ class SustainTrail extends FlxSprite
|
|||
// ===HOLD VERTICES==
|
||||
// Top left
|
||||
vertices[0 * 2] = 0.0; // Inline with left side
|
||||
vertices[0 * 2 + 1] = flipY ? clipHeight : height - clipHeight;
|
||||
vertices[0 * 2 + 1] = flipY ? clipHeight : graphicHeight - clipHeight;
|
||||
|
||||
// Top right
|
||||
vertices[1 * 2] = width;
|
||||
vertices[1 * 2] = graphicWidth;
|
||||
vertices[1 * 2 + 1] = vertices[0 * 2 + 1]; // Inline with top left vertex
|
||||
|
||||
// Bottom left
|
||||
|
@ -197,7 +209,7 @@ class SustainTrail extends FlxSprite
|
|||
}
|
||||
|
||||
// Bottom right
|
||||
vertices[3 * 2] = width;
|
||||
vertices[3 * 2] = graphicWidth;
|
||||
vertices[3 * 2 + 1] = vertices[2 * 2 + 1]; // Inline with bottom left vertex
|
||||
|
||||
// ===HOLD UVs===
|
||||
|
@ -233,7 +245,7 @@ class SustainTrail extends FlxSprite
|
|||
|
||||
// Bottom left
|
||||
vertices[6 * 2] = vertices[2 * 2]; // Inline with left side
|
||||
vertices[6 * 2 + 1] = flipY ? (graphic.height * (-bottomClip + endOffset) * zoom) : (height + graphic.height * (bottomClip - endOffset) * zoom);
|
||||
vertices[6 * 2 + 1] = flipY ? (graphic.height * (-bottomClip + endOffset) * zoom) : (graphicHeight + graphic.height * (bottomClip - endOffset) * zoom);
|
||||
|
||||
// Bottom right
|
||||
vertices[7 * 2] = vertices[3 * 2]; // Inline with right side
|
||||
|
@ -277,6 +289,10 @@ class SustainTrail extends FlxSprite
|
|||
getScreenPosition(_point, camera).subtractPoint(offset);
|
||||
camera.drawTriangles(processedGraphic, vertices, indices, uvtData, null, _point, blend, true, antialiasing);
|
||||
}
|
||||
|
||||
#if FLX_DEBUG
|
||||
if (FlxG.debugger.drawDebug) drawDebug();
|
||||
#end
|
||||
}
|
||||
|
||||
public override function kill():Void
|
||||
|
|
|
@ -750,7 +750,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
* `null` if the user isn't currently placing a note.
|
||||
* As the user drags, we will update this note's sustain length, and finalize the note when they release.
|
||||
*/
|
||||
var currentPlaceNoteData:Null<SongNoteData> = null;
|
||||
var currentPlaceNoteData(default, set):Null<SongNoteData> = null;
|
||||
|
||||
function set_currentPlaceNoteData(value:Null<SongNoteData>):Null<SongNoteData>
|
||||
{
|
||||
noteDisplayDirty = true;
|
||||
|
||||
return currentPlaceNoteData = value;
|
||||
}
|
||||
|
||||
// Note Movement
|
||||
|
||||
|
@ -2321,7 +2328,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
bounds.height = MIN_HEIGHT;
|
||||
}
|
||||
|
||||
trace('Note preview viewport bounds: ' + bounds.toString());
|
||||
// trace('Note preview viewport bounds: ' + bounds.toString());
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
@ -3172,8 +3179,16 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
if (holdNoteSprite == null || holdNoteSprite.noteData == null || !holdNoteSprite.exists || !holdNoteSprite.visible) continue;
|
||||
|
||||
if (!holdNoteSprite.isHoldNoteVisible(FlxG.height - PLAYBAR_HEIGHT, MENU_BAR_HEIGHT))
|
||||
if (holdNoteSprite.noteData == currentPlaceNoteData)
|
||||
{
|
||||
// This hold note is for the note we are currently dragging.
|
||||
// It will be displayed by gridGhostHoldNoteSprite instead.
|
||||
holdNoteSprite.kill();
|
||||
}
|
||||
else if (!holdNoteSprite.isHoldNoteVisible(FlxG.height - MENU_BAR_HEIGHT, GRID_TOP_PAD))
|
||||
{
|
||||
// This hold note is off-screen.
|
||||
// Kill the hold note sprite and recycle it.
|
||||
holdNoteSprite.kill();
|
||||
}
|
||||
else if (!currentSongChartNoteData.fastContains(holdNoteSprite.noteData) || holdNoteSprite.noteData.length == 0)
|
||||
|
@ -3191,7 +3206,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
else
|
||||
{
|
||||
displayedHoldNoteData.push(holdNoteSprite.noteData);
|
||||
// Update the event sprite's position.
|
||||
// Update the event sprite's height and position.
|
||||
// var holdNoteHeight = holdNoteSprite.noteData.getStepLength() * GRID_SIZE;
|
||||
// holdNoteSprite.setHeightDirectly(holdNoteHeight);
|
||||
holdNoteSprite.updateHoldNotePosition(renderedNotes);
|
||||
}
|
||||
}
|
||||
|
@ -3269,7 +3286,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
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)
|
||||
if (noteSprite.noteData != null
|
||||
&& noteSprite.noteData.length > 0
|
||||
&& displayedHoldNoteData.indexOf(noteSprite.noteData) == -1
|
||||
&& noteSprite.noteData != currentPlaceNoteData)
|
||||
{
|
||||
var holdNoteSprite:ChartEditorHoldNoteSprite = renderedHoldNotes.recycle(() -> new ChartEditorHoldNoteSprite(this));
|
||||
// trace('Creating new HoldNote... (${renderedHoldNotes.members.length})');
|
||||
|
@ -3282,6 +3302,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
holdNoteSprite.setHeightDirectly(noteLengthPixels);
|
||||
|
||||
holdNoteSprite.updateHoldNotePosition(renderedHoldNotes);
|
||||
|
||||
trace(holdNoteSprite.x + ', ' + holdNoteSprite.y + ', ' + holdNoteSprite.width + ', ' + holdNoteSprite.height);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3320,6 +3342,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Is the note a hold note?
|
||||
if (noteData == null || noteData.length <= 0) continue;
|
||||
|
||||
// Is the note the one we are dragging? If so, ghostHoldNoteSprite will handle it.
|
||||
if (noteData == currentPlaceNoteData) continue;
|
||||
|
||||
// Is the hold note rendered already?
|
||||
if (displayedHoldNoteData.indexOf(noteData) != -1) continue;
|
||||
|
||||
|
@ -3409,7 +3434,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
selectionSquare.x = noteSprite.x;
|
||||
selectionSquare.y = noteSprite.y;
|
||||
selectionSquare.width = GRID_SIZE;
|
||||
selectionSquare.height = GRID_SIZE;
|
||||
|
||||
var stepLength = noteSprite.noteData.getStepLength();
|
||||
selectionSquare.height = (stepLength <= 0) ? GRID_SIZE : ((stepLength + 1) * GRID_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3688,6 +3715,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
|
||||
var overlapsGrid:Bool = FlxG.mouse.overlaps(gridTiledSprite);
|
||||
|
||||
var overlapsRenderedNotes:Bool = FlxG.mouse.overlaps(renderedNotes);
|
||||
var overlapsRenderedHoldNotes:Bool = FlxG.mouse.overlaps(renderedHoldNotes);
|
||||
var overlapsRenderedEvents:Bool = FlxG.mouse.overlaps(renderedEvents);
|
||||
|
||||
// Cursor position relative to the grid.
|
||||
var cursorX:Float = FlxG.mouse.screenX - gridTiledSprite.x;
|
||||
var cursorY:Float = FlxG.mouse.screenY - gridTiledSprite.y;
|
||||
|
@ -3929,12 +3960,18 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
return event.alive && FlxG.mouse.overlaps(event);
|
||||
});
|
||||
}
|
||||
var highlightedHoldNote:Null<ChartEditorHoldNoteSprite> = null;
|
||||
if (highlightedNote == null && highlightedEvent == null)
|
||||
{
|
||||
highlightedHoldNote = renderedHoldNotes.members.find(function(holdNote:ChartEditorHoldNoteSprite):Bool {
|
||||
return holdNote.alive && FlxG.mouse.overlaps(holdNote);
|
||||
});
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
if (highlightedNote != null && highlightedNote.noteData != null)
|
||||
{
|
||||
// TODO: Handle the case of clicking on a sustain piece.
|
||||
// Control click to select/deselect an individual note.
|
||||
if (isNoteSelected(highlightedNote.noteData))
|
||||
{
|
||||
|
@ -3957,6 +3994,18 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
performCommand(new SelectItemsCommand([], [highlightedEvent.eventData]));
|
||||
}
|
||||
}
|
||||
else if (highlightedHoldNote != null && highlightedHoldNote.noteData != null)
|
||||
{
|
||||
// Control click to select/deselect an individual note.
|
||||
if (isNoteSelected(highlightedNote.noteData))
|
||||
{
|
||||
performCommand(new DeselectItemsCommand([highlightedHoldNote.noteData], []));
|
||||
}
|
||||
else
|
||||
{
|
||||
performCommand(new SelectItemsCommand([highlightedHoldNote.noteData], []));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do nothing if you control-clicked on an empty space.
|
||||
|
@ -3974,6 +4023,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Click an event to select it.
|
||||
performCommand(new SetItemSelectionCommand([], [highlightedEvent.eventData]));
|
||||
}
|
||||
else if (highlightedHoldNote != null && highlightedHoldNote.noteData != null)
|
||||
{
|
||||
// Click a hold note to select it.
|
||||
performCommand(new SetItemSelectionCommand([highlightedHoldNote.noteData], []));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Click on an empty space to deselect everything.
|
||||
|
@ -4126,7 +4180,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
var dragLengthMs:Float = dragLengthSteps * Conductor.instance.stepLengthMs;
|
||||
var dragLengthPixels:Float = dragLengthSteps * GRID_SIZE;
|
||||
|
||||
if (gridGhostNote != null && gridGhostNote.noteData != null && gridGhostHoldNote != null)
|
||||
if (gridGhostHoldNote != null)
|
||||
{
|
||||
if (dragLengthSteps > 0)
|
||||
{
|
||||
|
@ -4139,8 +4193,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
}
|
||||
|
||||
gridGhostHoldNote.visible = true;
|
||||
gridGhostHoldNote.noteData = gridGhostNote.noteData;
|
||||
gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection();
|
||||
gridGhostHoldNote.noteData = currentPlaceNoteData;
|
||||
gridGhostHoldNote.noteDirection = currentPlaceNoteData.getDirection();
|
||||
|
||||
gridGhostHoldNote.setHeightDirectly(dragLengthPixels, true);
|
||||
|
||||
|
@ -4161,6 +4215,15 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Apply the new length.
|
||||
performCommand(new ExtendNoteLengthCommand(currentPlaceNoteData, dragLengthMs));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply the new (zero) length if we are changing the length.
|
||||
if (currentPlaceNoteData.length > 0)
|
||||
{
|
||||
this.playSound(Paths.sound('chartingSounds/stretchSNAP_UI'));
|
||||
performCommand(new ExtendNoteLengthCommand(currentPlaceNoteData, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// Finished dragging. Release the note.
|
||||
currentPlaceNoteData = null;
|
||||
|
@ -4193,6 +4256,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
return event.alive && FlxG.mouse.overlaps(event);
|
||||
});
|
||||
}
|
||||
var highlightedHoldNote:Null<ChartEditorHoldNoteSprite> = null;
|
||||
if (highlightedNote == null && highlightedEvent == null)
|
||||
{
|
||||
highlightedHoldNote = renderedHoldNotes.members.find(function(holdNote:ChartEditorHoldNoteSprite):Bool {
|
||||
// If holdNote.alive is false, the holdNote is dead and awaiting recycling.
|
||||
return holdNote.alive && FlxG.mouse.overlaps(holdNote);
|
||||
});
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
|
@ -4219,6 +4290,17 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
performCommand(new SelectItemsCommand([], [highlightedEvent.eventData]));
|
||||
}
|
||||
}
|
||||
else if (highlightedHoldNote != null && highlightedHoldNote.noteData != null)
|
||||
{
|
||||
if (isNoteSelected(highlightedNote.noteData))
|
||||
{
|
||||
performCommand(new DeselectItemsCommand([highlightedHoldNote.noteData], []));
|
||||
}
|
||||
else
|
||||
{
|
||||
performCommand(new SelectItemsCommand([highlightedHoldNote.noteData], []));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do nothing when control clicking nothing.
|
||||
|
@ -4252,6 +4334,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
performCommand(new SetItemSelectionCommand([], [highlightedEvent.eventData]));
|
||||
}
|
||||
}
|
||||
else if (highlightedHoldNote != null && highlightedHoldNote.noteData != null)
|
||||
{
|
||||
// Clicked a hold note, start dragging TO EXTEND NOTE LENGTH.
|
||||
currentPlaceNoteData = highlightedHoldNote.noteData;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Click a blank space to place a note and select it.
|
||||
|
@ -4301,6 +4388,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
return event.alive && FlxG.mouse.overlaps(event);
|
||||
});
|
||||
}
|
||||
var highlightedHoldNote:Null<ChartEditorHoldNoteSprite> = null;
|
||||
if (highlightedNote == null && highlightedEvent == null)
|
||||
{
|
||||
highlightedHoldNote = renderedHoldNotes.members.find(function(holdNote:ChartEditorHoldNoteSprite):Bool {
|
||||
// If holdNote.alive is false, the holdNote is dead and awaiting recycling.
|
||||
return holdNote.alive && FlxG.mouse.overlaps(holdNote);
|
||||
});
|
||||
}
|
||||
|
||||
if (highlightedNote != null && highlightedNote.noteData != null)
|
||||
{
|
||||
|
@ -4352,13 +4447,40 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
performCommand(new RemoveEventsCommand([highlightedEvent.eventData]));
|
||||
}
|
||||
}
|
||||
else if (highlightedHoldNote != null && highlightedHoldNote.noteData != null)
|
||||
{
|
||||
if (FlxG.keys.pressed.SHIFT)
|
||||
{
|
||||
// Shift + Right click opens the context menu.
|
||||
// If we are clicking a large selection, open the Selection context menu, otherwise open the Note context menu.
|
||||
var isHighlightedNoteSelected:Bool = isNoteSelected(highlightedHoldNote.noteData);
|
||||
var useSingleNoteContextMenu:Bool = (!isHighlightedNoteSelected)
|
||||
|| (isHighlightedNoteSelected && currentNoteSelection.length == 1);
|
||||
// Show the context menu connected to the note.
|
||||
if (useSingleNoteContextMenu)
|
||||
{
|
||||
this.openHoldNoteContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedHoldNote.noteData);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Right click removes hold from the note.
|
||||
this.playSound(Paths.sound('chartingSounds/stretchSNAP_UI'));
|
||||
performCommand(new ExtendNoteLengthCommand(highlightedHoldNote.noteData, 0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Right clicked on nothing.
|
||||
}
|
||||
}
|
||||
|
||||
var isOrWillSelect = overlapsSelection || dragTargetNote != null || dragTargetEvent != null;
|
||||
var isOrWillSelect = overlapsSelection || dragTargetNote != null || dragTargetEvent != null || overlapsRenderedNotes || overlapsRenderedHoldNotes
|
||||
|| overlapsRenderedEvents;
|
||||
// Handle grid cursor.
|
||||
if (!isCursorOverHaxeUI && overlapsGrid && !isOrWillSelect && !overlapsSelectionBorder && !gridPlayheadScrollAreaPressed)
|
||||
{
|
||||
|
@ -4449,6 +4571,18 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
targetCursorMode = Crosshair;
|
||||
}
|
||||
else if (overlapsRenderedNotes)
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
else if (overlapsRenderedHoldNotes)
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
else if (overlapsRenderedEvents)
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
else if (overlapsGrid)
|
||||
{
|
||||
targetCursorMode = Cell;
|
||||
|
@ -5177,7 +5311,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
throw "ERROR: Tried to build selection square, but selectionSquareBitmap is null! Check ChartEditorThemeHandler.updateSelectionSquare()";
|
||||
|
||||
// FlxG.bitmapLog.add(selectionSquareBitmap, "selectionSquareBitmap");
|
||||
var result = new ChartEditorSelectionSquareSprite();
|
||||
var result = new ChartEditorSelectionSquareSprite(this);
|
||||
result.loadGraphic(selectionSquareBitmap);
|
||||
return result;
|
||||
}
|
||||
|
@ -5510,7 +5644,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
/**
|
||||
* HAXEUI FUNCTIONS
|
||||
*/
|
||||
// ====================
|
||||
// ==================
|
||||
|
||||
/**
|
||||
* Set the currently selected item in the Difficulty tree view to the node representing the current difficulty.
|
||||
|
@ -5601,7 +5735,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
/**
|
||||
* STATIC FUNCTIONS
|
||||
*/
|
||||
// ====================
|
||||
// ==================
|
||||
|
||||
function handleNotePreview():Void
|
||||
{
|
||||
|
|
|
@ -13,17 +13,25 @@ class ExtendNoteLengthCommand implements ChartEditorCommand
|
|||
var note:SongNoteData;
|
||||
var oldLength:Float;
|
||||
var newLength:Float;
|
||||
var unit:Unit;
|
||||
|
||||
public function new(note:SongNoteData, newLength:Float)
|
||||
public function new(note:SongNoteData, newLength:Float, unit:Unit = MILLISECONDS)
|
||||
{
|
||||
this.note = note;
|
||||
this.oldLength = note.length;
|
||||
this.newLength = newLength;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public function execute(state:ChartEditorState):Void
|
||||
{
|
||||
note.length = newLength;
|
||||
switch (unit)
|
||||
{
|
||||
case MILLISECONDS:
|
||||
this.note.length = newLength;
|
||||
case STEPS:
|
||||
this.note.setStepLength(newLength);
|
||||
}
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -36,7 +44,8 @@ class ExtendNoteLengthCommand implements ChartEditorCommand
|
|||
{
|
||||
state.playSound(Paths.sound('chartingSounds/undo'));
|
||||
|
||||
note.length = oldLength;
|
||||
// Always use milliseconds for undoing
|
||||
this.note.length = oldLength;
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -53,6 +62,23 @@ class ExtendNoteLengthCommand implements ChartEditorCommand
|
|||
|
||||
public function toString():String
|
||||
{
|
||||
return 'Extend Note Length';
|
||||
if (oldLength == 0)
|
||||
{
|
||||
return 'Add Hold to Note';
|
||||
}
|
||||
else if (newLength == 0)
|
||||
{
|
||||
return 'Remove Hold from Note';
|
||||
}
|
||||
else
|
||||
{
|
||||
return 'Extend Hold Note Length';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Unit
|
||||
{
|
||||
MILLISECONDS;
|
||||
STEPS;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,17 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
setup();
|
||||
}
|
||||
|
||||
public override function updateHitbox():Void
|
||||
{
|
||||
// Expand the clickable hitbox to the full column width, then nudge to the left to re-center it.
|
||||
width = ChartEditorState.GRID_SIZE;
|
||||
height = graphicHeight;
|
||||
|
||||
var xOffset = (ChartEditorState.GRID_SIZE - graphicWidth) / 2;
|
||||
offset.set(-xOffset, 0);
|
||||
origin.set(width * 0.5, height * 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the height directly, to a value in pixels.
|
||||
* @param h The desired height in pixels.
|
||||
|
@ -52,6 +63,23 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
fullSustainLength = sustainLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to override how debug bounding boxes are drawn for this sprite.
|
||||
*/
|
||||
public override function drawDebugOnCamera(camera:flixel.FlxCamera):Void
|
||||
{
|
||||
if (!camera.visible || !camera.exists || !isOnScreen(camera)) return;
|
||||
|
||||
var rect = getBoundingBox(camera);
|
||||
trace('hold note bounding box: ' + rect.x + ', ' + rect.y + ', ' + rect.width + ', ' + rect.height);
|
||||
|
||||
var gfx = beginDrawDebug(camera);
|
||||
debugBoundingBoxColor = 0xffFF66FF;
|
||||
gfx.lineStyle(2, color, 0.5); // thickness, color, alpha
|
||||
gfx.drawRect(rect.x, rect.y, rect.width, rect.height);
|
||||
endDrawDebug(camera);
|
||||
}
|
||||
|
||||
function setup():Void
|
||||
{
|
||||
strumTime = 999999999;
|
||||
|
@ -60,7 +88,9 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
active = true;
|
||||
visible = true;
|
||||
alpha = 1.0;
|
||||
width = graphic.width / 8 * zoom; // amount of notes * 2
|
||||
graphicWidth = graphic.width / 8 * zoom; // amount of notes * 2
|
||||
|
||||
updateHitbox();
|
||||
}
|
||||
|
||||
public override function revive():Void
|
||||
|
@ -154,7 +184,7 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
}
|
||||
|
||||
this.x += ChartEditorState.GRID_SIZE / 2;
|
||||
this.x -= this.width / 2;
|
||||
this.x -= this.graphicWidth / 2;
|
||||
|
||||
this.y += ChartEditorState.GRID_SIZE / 2;
|
||||
|
||||
|
@ -163,5 +193,8 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
this.x += origin.x;
|
||||
this.y += origin.y;
|
||||
}
|
||||
|
||||
// Account for expanded clickable hitbox.
|
||||
this.x += this.offset.x;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,33 @@
|
|||
package funkin.ui.debug.charting.components;
|
||||
|
||||
import flixel.addons.display.FlxSliceSprite;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.data.song.SongData.SongNoteData;
|
||||
import flixel.math.FlxRect;
|
||||
import funkin.data.song.SongData.SongEventData;
|
||||
import funkin.data.song.SongData.SongNoteData;
|
||||
import funkin.ui.debug.charting.handlers.ChartEditorThemeHandler;
|
||||
|
||||
/**
|
||||
* A sprite that can be used to display a square over a selected note or event in the chart.
|
||||
* Designed to be used and reused efficiently. Has no gameplay functionality.
|
||||
*/
|
||||
class ChartEditorSelectionSquareSprite extends FlxSprite
|
||||
@:nullSafety
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
class ChartEditorSelectionSquareSprite extends FlxSliceSprite
|
||||
{
|
||||
public var noteData:Null<SongNoteData>;
|
||||
public var eventData:Null<SongEventData>;
|
||||
|
||||
public function new()
|
||||
public function new(chartEditorState:ChartEditorState)
|
||||
{
|
||||
super();
|
||||
super(chartEditorState.selectionSquareBitmap,
|
||||
new FlxRect(ChartEditorThemeHandler.SELECTION_SQUARE_BORDER_WIDTH
|
||||
+ 4, ChartEditorThemeHandler.SELECTION_SQUARE_BORDER_WIDTH
|
||||
+ 4,
|
||||
ChartEditorState.GRID_SIZE
|
||||
- (2 * ChartEditorThemeHandler.SELECTION_SQUARE_BORDER_WIDTH + 8),
|
||||
ChartEditorState.GRID_SIZE
|
||||
- (2 * ChartEditorThemeHandler.SELECTION_SQUARE_BORDER_WIDTH + 8)),
|
||||
32, 32);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package funkin.ui.debug.charting.contextmenus;
|
||||
|
||||
import haxe.ui.containers.menus.Menu;
|
||||
import haxe.ui.containers.menus.MenuItem;
|
||||
import haxe.ui.core.Screen;
|
||||
import funkin.data.song.SongData.SongNoteData;
|
||||
import funkin.ui.debug.charting.commands.FlipNotesCommand;
|
||||
import funkin.ui.debug.charting.commands.RemoveNotesCommand;
|
||||
import funkin.ui.debug.charting.commands.ExtendNoteLengthCommand;
|
||||
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/context-menus/hold-note.xml"))
|
||||
class ChartEditorHoldNoteContextMenu extends ChartEditorBaseContextMenu
|
||||
{
|
||||
var contextmenuFlip:MenuItem;
|
||||
var contextmenuDelete:MenuItem;
|
||||
|
||||
var data:SongNoteData;
|
||||
|
||||
public function new(chartEditorState2:ChartEditorState, xPos2:Float = 0, yPos2:Float = 0, data:SongNoteData)
|
||||
{
|
||||
super(chartEditorState2, xPos2, yPos2);
|
||||
this.data = data;
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
function initialize():Void
|
||||
{
|
||||
// NOTE: Remember to use commands here to ensure undo/redo works properly
|
||||
contextmenuFlip.onClick = function(_) {
|
||||
chartEditorState.performCommand(new FlipNotesCommand([data]));
|
||||
}
|
||||
|
||||
contextmenuRemoveHold.onClick = function(_) {
|
||||
chartEditorState.performCommand(new ExtendNoteLengthCommand(data, 0));
|
||||
}
|
||||
|
||||
contextmenuDelete.onClick = function(_) {
|
||||
chartEditorState.performCommand(new RemoveNotesCommand([data]));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import haxe.ui.core.Screen;
|
|||
import funkin.data.song.SongData.SongNoteData;
|
||||
import funkin.ui.debug.charting.commands.FlipNotesCommand;
|
||||
import funkin.ui.debug.charting.commands.RemoveNotesCommand;
|
||||
import funkin.ui.debug.charting.commands.ExtendNoteLengthCommand;
|
||||
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/context-menus/note.xml"))
|
||||
|
@ -31,6 +32,10 @@ class ChartEditorNoteContextMenu extends ChartEditorBaseContextMenu
|
|||
chartEditorState.performCommand(new FlipNotesCommand([data]));
|
||||
}
|
||||
|
||||
contextmenuAddHold.onClick = function(_) {
|
||||
chartEditorState.performCommand(new ExtendNoteLengthCommand(data, 4, STEPS));
|
||||
}
|
||||
|
||||
contextmenuDelete.onClick = function(_) {
|
||||
chartEditorState.performCommand(new RemoveNotesCommand([data]));
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package funkin.ui.debug.charting.handlers;
|
|||
|
||||
import funkin.ui.debug.charting.contextmenus.ChartEditorDefaultContextMenu;
|
||||
import funkin.ui.debug.charting.contextmenus.ChartEditorEventContextMenu;
|
||||
import funkin.ui.debug.charting.contextmenus.ChartEditorHoldNoteContextMenu;
|
||||
import funkin.ui.debug.charting.contextmenus.ChartEditorNoteContextMenu;
|
||||
import funkin.ui.debug.charting.contextmenus.ChartEditorSelectionContextMenu;
|
||||
import haxe.ui.containers.menus.Menu;
|
||||
|
@ -23,16 +24,33 @@ class ChartEditorContextMenuHandler
|
|||
displayMenu(state, new ChartEditorDefaultContextMenu(state, xPos, yPos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opened when shift+right-clicking a selection of multiple items.
|
||||
*/
|
||||
public static function openSelectionContextMenu(state:ChartEditorState, xPos:Float, yPos:Float)
|
||||
{
|
||||
displayMenu(state, new ChartEditorSelectionContextMenu(state, xPos, yPos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opened when shift+right-clicking a single note.
|
||||
*/
|
||||
public static function openNoteContextMenu(state:ChartEditorState, xPos:Float, yPos:Float, data:SongNoteData)
|
||||
{
|
||||
displayMenu(state, new ChartEditorNoteContextMenu(state, xPos, yPos, data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opened when shift+right-clicking a single hold note.
|
||||
*/
|
||||
public static function openHoldNoteContextMenu(state:ChartEditorState, xPos:Float, yPos:Float, data:SongNoteData)
|
||||
{
|
||||
displayMenu(state, new ChartEditorHoldNoteContextMenu(state, xPos, yPos, data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opened when shift+right-clicking a single event.
|
||||
*/
|
||||
public static function openEventContextMenu(state:ChartEditorState, xPos:Float, yPos:Float, data:SongEventData)
|
||||
{
|
||||
displayMenu(state, new ChartEditorEventContextMenu(state, xPos, yPos, data));
|
||||
|
|
|
@ -52,7 +52,7 @@ class ChartEditorThemeHandler
|
|||
// 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;
|
||||
static final SELECTION_SQUARE_BORDER_WIDTH:Int = 1;
|
||||
public static final SELECTION_SQUARE_BORDER_WIDTH:Int = 1;
|
||||
|
||||
// Fill on the square highlighting selected notes.
|
||||
// Make sure this is transparent so you can see the notes underneath.
|
||||
|
|
Loading…
Reference in a new issue