Skip to content

Commit 37e0d1a

Browse files
committed
Chart editor - select above/below playhead
1 parent 9f9a692 commit 37e0d1a

File tree

4 files changed

+333
-6
lines changed

4 files changed

+333
-6
lines changed

source/funkin/ui/debug/charting/ChartEditorState.hx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,14 +1879,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
18791879
var menubarItemSelectRegion:MenuItem;
18801880

18811881
/**
1882-
* The `Edit -> Select Before Cursor` menu item.
1882+
* The `Edit -> Select Before Playhead` menu item.
18831883
*/
1884-
var menubarItemSelectBeforeCursor:MenuItem;
1884+
var menubarItemSelectBeforePlayhead:MenuItem;
18851885

18861886
/**
1887-
* The `Edit -> Select After Cursor` menu item.
1887+
* The `Edit -> Select After Playhead` menu item.
18881888
*/
1889-
var menubarItemSelectAfterCursor:MenuItem;
1889+
var menubarItemSelectAfterPlayhead:MenuItem;
18901890

18911891
/**
18921892
* The `Edit -> Decrease Note Snap Precision` menu item.
@@ -3114,6 +3114,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
31143114

31153115
menubarItemSelectNone.onClick = _ -> performCommand(new DeselectAllItemsCommand());
31163116

3117+
menubarItemSelectBeforePlayhead.onClick = _ -> performCommand(new SelectAllItemsBetweenTimeCommand(scrollPositionInMs + playheadPositionInMs, true, true, true));
3118+
3119+
menubarItemSelectAfterPlayhead.onClick = _ -> performCommand(new SelectAllItemsBetweenTimeCommand(scrollPositionInMs + playheadPositionInMs, false, true, true));
3120+
31173121
menubarItemPlaytestFull.onClick = _ -> testSongInPlayState(false);
31183122
menubarItemPlaytestMinimal.onClick = _ -> testSongInPlayState(true);
31193123

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package funkin.ui.debug.charting.commands;
2+
3+
import funkin.data.song.SongData.SongNoteData;
4+
import funkin.data.song.SongData.SongEventData;
5+
import funkin.data.song.SongDataUtils;
6+
7+
/**
8+
* Command that deselects all selected notes and/or events above or past the time given in the chart editor.
9+
*/
10+
@:nullSafety
11+
@:access(funkin.ui.debug.charting.ChartEditorState)
12+
class DeselectAllItemsBetweenTimeCommand implements ChartEditorCommand
13+
{
14+
var time:Float;
15+
var above:Bool;
16+
17+
var notes:Array<SongNoteData>;
18+
var events:Array<SongEventData>;
19+
20+
var shouldDeselectNotes:Bool;
21+
var shouldDeselectEvents:Bool;
22+
23+
public function new(time:Float, above:Bool, shouldDeselectNotes:Bool, shouldDeselectEvents:Bool)
24+
{
25+
this.time = time;
26+
this.above = above;
27+
28+
this.notes = [];
29+
this.events = [];
30+
31+
this.shouldDeselectNotes = shouldDeselectNotes;
32+
this.shouldDeselectEvents = shouldDeselectEvents;
33+
}
34+
35+
public function execute(state:ChartEditorState):Void
36+
{
37+
if (above)
38+
{
39+
if (shouldDeselectNotes)
40+
{
41+
for (i in 0...state.currentSongChartNoteData.length)
42+
{
43+
if (state.currentSongChartNoteData[i].time < time)
44+
notes.push(state.currentSongChartNoteData[i]);
45+
else
46+
// We've reached the end of the notes above this time,
47+
// there's no reason to waste our time running this loop to completion
48+
break;
49+
}
50+
}
51+
if (shouldDeselectEvents)
52+
{
53+
for (i in 0...state.currentSongChartEventData.length)
54+
{
55+
if (state.currentSongChartEventData[i].time < time)
56+
events.push(state.currentSongChartEventData[i]);
57+
else
58+
break;
59+
}
60+
}
61+
}
62+
else // Deselecting below the time given
63+
{
64+
if (shouldDeselectNotes)
65+
{
66+
for (i in 0...state.currentSongChartNoteData.length)
67+
{
68+
// Backwards for loop (kinda). Neat!
69+
if (state.currentSongChartNoteData[state.currentSongChartNoteData.length - i - 1].time > time)
70+
notes.push(state.currentSongChartNoteData[state.currentSongChartNoteData.length - i - 1]);
71+
else
72+
// We've reached the end of the notes below this time,
73+
// there's no reason to waste our time running this loop to completion
74+
break;
75+
}
76+
}
77+
if (shouldDeselectEvents)
78+
{
79+
for (i in 0...state.currentSongChartEventData.length)
80+
{
81+
if (state.currentSongChartEventData[state.currentSongChartEventData.length - i - 1].time > time)
82+
events.push(state.currentSongChartEventData[state.currentSongChartEventData.length- i - 1]);
83+
else
84+
break;
85+
}
86+
}
87+
}
88+
89+
state.currentNoteSelection = SongDataUtils.subtractNotes(state.currentNoteSelection, this.notes);
90+
state.currentEventSelection = SongDataUtils.subtractEvents(state.currentEventSelection, this.events);
91+
92+
state.noteDisplayDirty = true;
93+
state.notePreviewDirty = true;
94+
}
95+
96+
public function undo(state:ChartEditorState):Void
97+
{
98+
for (note in this.notes)
99+
{
100+
state.currentNoteSelection.push(note);
101+
}
102+
103+
for (event in this.events)
104+
{
105+
state.currentEventSelection.push(event);
106+
}
107+
108+
state.noteDisplayDirty = true;
109+
state.notePreviewDirty = true;
110+
}
111+
112+
public function shouldAddToHistory(state:ChartEditorState):Bool
113+
{
114+
// This command is undoable. Add to the history if we actually performed an action.
115+
return (notes.length > 0 || events.length > 0);
116+
}
117+
118+
public function toString():String
119+
{
120+
var isPlural = (notes.length + events.length) > 1;
121+
var notesOnly = (notes.length > 0 && events.length == 0);
122+
var eventsOnly = (notes.length == 0 && events.length > 0);
123+
124+
if (notesOnly)
125+
{
126+
return 'Deselect ${notes.length} ${isPlural ? 'Notes' : 'Note'}';
127+
}
128+
else if (eventsOnly)
129+
{
130+
return 'Deselect ${events.length} ${isPlural ? 'Events' : 'Event'}';
131+
}
132+
133+
return 'Deselect ${notes.length + events.length} Items';
134+
}
135+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package funkin.ui.debug.charting.commands;
2+
3+
import funkin.data.song.SongData.SongNoteData;
4+
import funkin.data.song.SongData.SongEventData;
5+
import funkin.data.song.SongDataUtils;
6+
7+
/**
8+
* Command that selects all notes and/or events above or past the time given in the chart editor.
9+
*/
10+
@:nullSafety
11+
@:access(funkin.ui.debug.charting.ChartEditorState)
12+
class SelectAllItemsBetweenTimeCommand implements ChartEditorCommand
13+
{
14+
var time:Float;
15+
var above:Bool;
16+
17+
var notes:Array<SongNoteData>;
18+
var events:Array<SongEventData>;
19+
20+
var shouldSelectNotes:Bool;
21+
var shouldSelectEvents:Bool;
22+
23+
public function new(time:Float, above:Bool, shouldSelectNotes:Bool, shouldSelectEvents:Bool)
24+
{
25+
this.time = time;
26+
this.above = above;
27+
28+
this.notes = [];
29+
this.events = [];
30+
31+
this.shouldSelectNotes = shouldSelectNotes;
32+
this.shouldSelectEvents = shouldSelectEvents;
33+
}
34+
35+
public function execute(state:ChartEditorState):Void
36+
{
37+
if(above)
38+
{
39+
if (shouldSelectNotes)
40+
{
41+
for (i in 0...state.currentSongChartNoteData.length)
42+
{
43+
if (state.currentSongChartNoteData[i].time < time)
44+
notes.push(state.currentSongChartNoteData[i]);
45+
else
46+
//We've reached the end of the notes above this time,
47+
// there's no reason to waste our time running this loop to completion
48+
break;
49+
}
50+
}
51+
if (shouldSelectEvents)
52+
{
53+
for (i in 0...state.currentSongChartEventData.length)
54+
{
55+
if (state.currentSongChartEventData[i].time < time)
56+
events.push(state.currentSongChartEventData[i]);
57+
58+
else
59+
break;
60+
}
61+
}
62+
}
63+
else // Selecting below the time given
64+
{
65+
if (shouldSelectNotes)
66+
{
67+
for (i in 0...state.currentSongChartNoteData.length)
68+
{
69+
// Backwards for loop (kinda). Neat!
70+
if (state.currentSongChartNoteData[state.currentSongChartNoteData.length - i - 1].time > time)
71+
notes.push(state.currentSongChartNoteData[state.currentSongChartNoteData.length - i- 1]);
72+
else
73+
// We've reached the end of the notes below this time,
74+
// there's no reason to waste our time running this loop to completion
75+
break;
76+
}
77+
}
78+
if (shouldSelectEvents)
79+
{
80+
for (i in 0...state.currentSongChartEventData.length)
81+
{
82+
if (state.currentSongChartEventData[state.currentSongChartEventData.length - i - 1].time > time)
83+
events.push(state.currentSongChartEventData[state.currentSongChartEventData.length- i- 1]);
84+
else
85+
break;
86+
}
87+
}
88+
}
89+
90+
// Add the notes and events to the selection
91+
for (note in this.notes)
92+
{
93+
state.currentNoteSelection.push(note);
94+
}
95+
96+
for (event in this.events)
97+
{
98+
state.currentEventSelection.push(event);
99+
}
100+
101+
102+
// I don't think it's neccesary to copy this code in, but someone will make an issue out of this if I don't, I'm sure.
103+
// If we just selected one or more events (and no notes), then we should make the event data toolbox display the event data for the selected event.
104+
if (this.notes.length == 0 && this.events.length == 1)
105+
{
106+
var eventSelected = this.events[0];
107+
108+
state.eventKindToPlace = eventSelected.eventKind;
109+
110+
// This code is here to parse event data that's not built as a struct for some reason.
111+
// TODO: Clean this up or get rid of it.
112+
var eventSchema = eventSelected.getSchema();
113+
var defaultKey = null;
114+
if (eventSchema == null)
115+
{
116+
trace('[WARNING] Event schema not found for event ${eventSelected.eventKind}.');
117+
}
118+
else
119+
{
120+
defaultKey = eventSchema.getFirstField()?.name;
121+
}
122+
var eventData = eventSelected.valueAsStruct(defaultKey);
123+
124+
state.eventDataToPlace = eventData;
125+
126+
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT);
127+
}
128+
129+
// If we just selected one or more notes (and no events), then we should make the note data toolbox display the note data for the selected note.
130+
if (this.events.length == 0 && this.notes.length == 1)
131+
{
132+
var noteSelected = this.notes[0];
133+
134+
state.noteKindToPlace = noteSelected.kind;
135+
136+
// This code is here to parse note data that's not built as a struct for some reason.
137+
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_NOTE_DATA_LAYOUT);
138+
}
139+
140+
state.noteDisplayDirty = true;
141+
state.notePreviewDirty = true;
142+
}
143+
144+
public function undo(state:ChartEditorState):Void
145+
{
146+
state.currentNoteSelection = SongDataUtils.subtractNotes(state.currentNoteSelection, this.notes);
147+
state.currentEventSelection = SongDataUtils.subtractEvents(state.currentEventSelection, this.events);
148+
149+
state.noteDisplayDirty = true;
150+
state.notePreviewDirty = true;
151+
}
152+
153+
public function shouldAddToHistory(state:ChartEditorState):Bool
154+
{
155+
// This command is undoable. Add to the history if we actually performed an action.
156+
return (notes.length > 0 || events.length > 0);
157+
}
158+
159+
public function toString():String
160+
{
161+
var len:Int = notes.length + events.length;
162+
163+
if (notes.length == 0)
164+
{
165+
if (events.length == 1)
166+
{
167+
return 'Select Event';
168+
}
169+
else
170+
{
171+
return 'Select ${events.length} Events';
172+
}
173+
}
174+
else if (events.length == 0)
175+
{
176+
if (notes.length == 1)
177+
{
178+
return 'Select Note';
179+
}
180+
else
181+
{
182+
return 'Select ${notes.length} Notes';
183+
}
184+
}
185+
186+
return 'Select ${len} Items';
187+
}
188+
}

source/funkin/ui/debug/charting/handlers/ChartEditorShortcutHandler.hx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class ChartEditorShortcutHandler
2626
state.menubarItemSelectAllEvents.shortcutText = ctrlOrCmd(alt('A'));
2727
state.menubarItemSelectInverse.shortcutText = ctrlOrCmd('I');
2828
state.menubarItemSelectNone.shortcutText = ctrlOrCmd('D');
29-
state.menubarItemSelectBeforeCursor.shortcutText = shift('Home');
30-
state.menubarItemSelectAfterCursor.shortcutText = shift('End');
29+
state.menubarItemSelectBeforePlayhead.shortcutText = shift('Home');
30+
state.menubarItemSelectAfterPlayhead.shortcutText = shift('End');
3131

3232
state.menubarItemDifficultyDown.shortcutText = ctrlOrCmd('');
3333
state.menubarItemDifficultyUp.shortcutText = ctrlOrCmd('');

0 commit comments

Comments
 (0)