1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-02-09 13:07:10 +00:00

Several UI tweaks and usability fixes. (#224)

Co-authored-by: Cameron Taylor <cameron.taylor.ninja@gmail.com>
This commit is contained in:
Eric 2023-11-21 02:12:34 -05:00 committed by GitHub
parent e2250b18ff
commit 95b43cce55
5 changed files with 143 additions and 38 deletions

2
assets

@ -1 +1 @@
Subproject commit c3ce920f162ad53cb510557b3bc69ab9805f48d7
Subproject commit 4ed2b3084d54899e10d10a97eaafe210158768be

View file

@ -1,5 +1,6 @@
package funkin.ui.debug.charting;
import haxe.ui.containers.menus.MenuBar;
import flixel.addons.display.FlxSliceSprite;
import flixel.addons.display.FlxTiledSprite;
import flixel.addons.transition.FlxTransitionableState;
@ -565,6 +566,23 @@ class ChartEditorState extends HaxeUIState
return FocusManager.instance.focus != null;
}
/**
* Whether the user's mouse cursor is hovering over a SOLID component of the HaxeUI.
* If so, we can ignore certain mouse events underneath.
*/
var isCursorOverHaxeUI(get, never):Bool;
function get_isCursorOverHaxeUI():Bool
{
return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
}
/**
* The value of `isCursorOverHaxeUI` from the previous frame.
* This is useful because we may have just clicked a menu item, causing the menu to disappear.
*/
var wasCursorOverHaxeUI:Bool = false;
/**
* Set by ChartEditorDialogHandler, used to prevent background interaction while the dialog is open.
*/
@ -1009,7 +1027,7 @@ class ChartEditorState extends HaxeUIState
{
// Initialize to the default value if not set.
result = [];
trace('Initializing blank note data for difficulty ' + selectedDifficulty);
trace('Initializing blank chart for difficulty ' + selectedDifficulty);
currentSongChartData.notes.set(selectedDifficulty, result);
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
return result;
@ -1941,8 +1959,10 @@ class ChartEditorState extends HaxeUIState
playbarHead.onDragEnd = function(_:DragEvent) {
playbarHeadDragging = false;
var value:Null<Float> = playbarHead?.value;
// Set the song position to where the playhead was moved to.
scrollPositionInPixels = songLengthInPixels * (playbarHead?.value ?? 0 / 100);
scrollPositionInPixels = songLengthInPixels * ((value ?? 0.0) / 100);
// Update the conductor and audio tracks to match.
moveSongToScrollPosition();
@ -1963,6 +1983,10 @@ class ChartEditorState extends HaxeUIState
menubarItemSaveChart = findComponent('menubarItemSaveChart', MenuItem);
if (menubarItemSaveChart == null) throw "Could not find menubarItemSaveChart!";
var menubar = findComponent('menubar', MenuBar);
if (menubar == null) throw "Could not find menubar!";
if (!Preferences.debugDisplay) menubar.paddingLeft = null;
// Setup notifications.
@:privateAccess
NotificationManager.GUTTER_SIZE = 20;
@ -2280,6 +2304,8 @@ class ChartEditorState extends HaxeUIState
#if debug
handleQuickWatch();
#end
handlePostUpdate();
}
/**
@ -2724,7 +2750,7 @@ class ChartEditorState extends HaxeUIState
function handleScrollKeybinds():Void
{
// Don't scroll when the user is interacting with the UI, unless a playbar button (the << >> ones) is pressed.
if (isHaxeUIFocused && playbarButtonPressed == null) return;
if ((isHaxeUIFocused || 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.
@ -2925,7 +2951,7 @@ class ChartEditorState extends HaxeUIState
if (FlxG.mouse.justReleased) FlxG.sound.play(Paths.sound("chartingSounds/ClickUp"));
// Note: If a menu is open in HaxeUI, don't handle cursor behavior.
var shouldHandleCursor:Bool = !isHaxeUIFocused
var shouldHandleCursor:Bool = !(isHaxeUIFocused || playbarHeadDragging)
|| (selectionBoxStartPos != null)
|| (dragTargetNote != null || dragTargetEvent != null);
var eventColumn:Int = (STRUMLINE_SIZE * 2 + 1) - 1;
@ -3095,9 +3121,9 @@ class ChartEditorState extends HaxeUIState
if (!FlxG.keys.pressed.CONTROL)
{
// Deselect all items.
if (currentNoteSelection.length > 0 || currentEventSelection.length > 0)
var shouldDeselect:Bool = !wasCursorOverHaxeUI && (currentNoteSelection.length > 0 || currentEventSelection.length > 0);
if (shouldDeselect)
{
trace('Clicked and dragged outside grid, deselecting all items.');
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
}
}
@ -3213,7 +3239,11 @@ class ChartEditorState extends HaxeUIState
else
{
// Click on an empty space to deselect everything.
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
var shouldDeselect:Bool = !wasCursorOverHaxeUI && (currentNoteSelection.length > 0 || currentEventSelection.length > 0);
if (shouldDeselect)
{
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
}
}
}
}
@ -3224,9 +3254,9 @@ class ChartEditorState extends HaxeUIState
if (!FlxG.keys.pressed.CONTROL)
{
// Deselect all items.
if (currentNoteSelection.length > 0 || currentEventSelection.length > 0)
var shouldDeselect:Bool = !wasCursorOverHaxeUI && (currentNoteSelection.length > 0 || currentEventSelection.length > 0);
if (shouldDeselect)
{
trace('Clicked outside grid, deselecting all items.');
performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection));
}
}
@ -3295,7 +3325,6 @@ class ChartEditorState extends HaxeUIState
if (FlxG.mouse.screenY < MENU_BAR_HEIGHT)
{
// Scroll up.
trace('Scroll up!');
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.screenY;
scrollPositionInPixels -= diff * 0.5; // Too fast!
moveSongToScrollPosition();
@ -3303,7 +3332,6 @@ class ChartEditorState extends HaxeUIState
else if (FlxG.mouse.screenY > (playbarHeadLayout?.y ?? 0.0))
{
// Scroll down.
trace('Scroll down!');
var diff:Float = FlxG.mouse.screenY - (playbarHeadLayout?.y ?? 0.0);
scrollPositionInPixels += diff * 0.5; // Too fast!
moveSongToScrollPosition();
@ -3454,7 +3482,6 @@ class ChartEditorState extends HaxeUIState
if (isNoteSelected(highlightedNote.noteData))
{
// Clicked a selected event, start dragging.
trace('Ready to drag!');
dragTargetNote = highlightedNote;
}
else
@ -3468,7 +3495,6 @@ class ChartEditorState extends HaxeUIState
if (isEventSelected(highlightedEvent.eventData))
{
// Clicked a selected event, start dragging.
trace('Ready to drag!');
dragTargetEvent = highlightedEvent;
}
else
@ -3682,6 +3708,7 @@ class ChartEditorState extends HaxeUIState
for (curVariation in availableVariations)
{
trace('DIFFICULTY TOOLBOX: Variation ${curVariation}');
var variationMetadata:Null<SongMetadata> = songMetadata.get(curVariation);
if (variationMetadata == null) continue;
@ -3696,6 +3723,7 @@ class ChartEditorState extends HaxeUIState
for (difficulty in difficultyList)
{
trace('DIFFICULTY TOOLBOX: Difficulty ${curVariation}_$difficulty');
var _treeDifficulty:TreeViewNode = treeVariation.addNode(
{
id: 'stv_difficulty_${curVariation}_$difficulty',
@ -4042,6 +4070,13 @@ class ChartEditorState extends HaxeUIState
}
}
// CTRL + F = Flip Notes
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.F)
{
// Flip selected notes.
performCommand(new FlipNotesCommand(currentNoteSelection));
}
// CTRL + A = Select All
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.A)
{
@ -4119,6 +4154,11 @@ class ChartEditorState extends HaxeUIState
FlxG.watch.addQuick("eventsSelected", currentEventSelection.length);
}
function handlePostUpdate():Void
{
wasCursorOverHaxeUI = isCursorOverHaxeUI;
}
/**
* PLAYTEST FUNCTIONS
*/

View file

@ -153,8 +153,8 @@ class ChartEditorDialogHandler
#end
// Create New Song "Easy/Normal/Hard"
var linkCreateBasic:Null<Link> = dialog.findComponent('splashCreateFromSongBasic', Link);
if (linkCreateBasic == null) throw 'Could not locate splashCreateFromSongBasic link in Welcome dialog';
var linkCreateBasic:Null<Link> = dialog.findComponent('splashCreateFromSongBasicOnly', Link);
if (linkCreateBasic == null) throw 'Could not locate splashCreateFromSongBasicOnly link in Welcome dialog';
linkCreateBasic.onClick = function(_event) {
// Hide the welcome dialog
dialog.hideDialog(DialogButton.CANCEL);
@ -163,12 +163,12 @@ class ChartEditorDialogHandler
//
// Create Song Wizard
//
openCreateSongWizardBasic(state, false);
openCreateSongWizardBasicOnly(state, false);
}
// Create New Song "Erect/Nightmare"
var linkCreateErect:Null<Link> = dialog.findComponent('splashCreateFromSongErect', Link);
if (linkCreateErect == null) throw 'Could not locate splashCreateFromSongErect link in Welcome dialog';
var linkCreateErect:Null<Link> = dialog.findComponent('splashCreateFromSongErectOnly', Link);
if (linkCreateErect == null) throw 'Could not locate splashCreateFromSongErectOnly link in Welcome dialog';
linkCreateErect.onClick = function(_event) {
// Hide the welcome dialog
dialog.hideDialog(DialogButton.CANCEL);
@ -176,7 +176,20 @@ class ChartEditorDialogHandler
//
// Create Song Wizard
//
openCreateSongWizardErect(state, false);
openCreateSongWizardErectOnly(state, false);
}
// Create New Song "Easy/Normal/Hard/Erect/Nightmare"
var linkCreateErect:Null<Link> = dialog.findComponent('splashCreateFromSongBasicErect', Link);
if (linkCreateErect == null) throw 'Could not locate splashCreateFromSongBasicErect link in Welcome dialog';
linkCreateErect.onClick = function(_event) {
// Hide the welcome dialog
dialog.hideDialog(DialogButton.CANCEL);
//
// Create Song Wizard
//
openCreateSongWizardBasicErect(state, false);
}
var linkImportChartLegacy:Null<Link> = dialog.findComponent('splashImportChartLegacy', Link);
@ -458,10 +471,10 @@ class ChartEditorDialogHandler
};
}
public static function openCreateSongWizardBasic(state:ChartEditorState, closable:Bool):Void
public static function openCreateSongWizardBasicOnly(state:ChartEditorState, closable:Bool):Void
{
// Step 1. Song Metadata
var songMetadataDialog:Dialog = openSongMetadataDialog(state);
var songMetadataDialog:Dialog = openSongMetadataDialog(state, false, Constants.DEFAULT_VARIATION);
songMetadataDialog.onDialogClosed = function(_event) {
state.isHaxeUIDialogOpen = false;
if (_event.button == DialogButton.APPLY)
@ -497,10 +510,49 @@ class ChartEditorDialogHandler
};
}
public static function openCreateSongWizardErect(state:ChartEditorState, closable:Bool):Void
public static function openCreateSongWizardErectOnly(state:ChartEditorState, closable:Bool):Void
{
// Step 1. Song Metadata
var songMetadataDialog:Dialog = openSongMetadataDialog(state);
var songMetadataDialog:Dialog = openSongMetadataDialog(state, true, Constants.DEFAULT_VARIATION);
songMetadataDialog.onDialogClosed = function(_event) {
state.isHaxeUIDialogOpen = false;
if (_event.button == DialogButton.APPLY)
{
// Step 2. Upload Instrumental
var uploadInstDialog:Dialog = openUploadInstDialog(state, closable);
uploadInstDialog.onDialogClosed = function(_event) {
state.isHaxeUIDialogOpen = false;
if (_event.button == DialogButton.APPLY)
{
// Step 3. Upload Vocals
// NOTE: Uploading vocals is optional, so we don't need to check if the user cancelled the wizard.
var uploadVocalsDialog:Dialog = openUploadVocalsDialog(state, closable); // var uploadVocalsDialog:Dialog
uploadVocalsDialog.onDialogClosed = function(_event) {
state.isHaxeUIDialogOpen = false;
state.currentWorkingFilePath = null; // New file, so no path.
state.switchToCurrentInstrumental();
state.postLoadInstrumental();
}
}
else
{
// User cancelled the wizard at Step 2! Back to the welcome dialog.
openWelcomeDialog(state);
}
};
}
else
{
// User cancelled the wizard at Step 1! Back to the welcome dialog.
openWelcomeDialog(state);
}
};
}
public static function openCreateSongWizardBasicErect(state:ChartEditorState, closable:Bool):Void
{
// Step 1. Song Metadata
var songMetadataDialog:Dialog = openSongMetadataDialog(state, false, Constants.DEFAULT_VARIATION);
songMetadataDialog.onDialogClosed = function(_event) {
state.isHaxeUIDialogOpen = false;
if (_event.button == DialogButton.APPLY)
@ -517,7 +569,7 @@ class ChartEditorDialogHandler
uploadVocalsDialog.onDialogClosed = function(_event) {
state.switchToCurrentInstrumental();
// Step 4. Song Metadata (Erect)
var songMetadataDialogErect:Dialog = openSongMetadataDialog(state, 'erect');
var songMetadataDialogErect:Dialog = openSongMetadataDialog(state, true, 'erect');
songMetadataDialogErect.onDialogClosed = function(_event) {
state.isHaxeUIDialogOpen = false;
if (_event.button == DialogButton.APPLY)
@ -699,10 +751,8 @@ class ChartEditorDialogHandler
* @return The dialog to open.
*/
@:haxe.warning("-WVarInit")
public static function openSongMetadataDialog(state:ChartEditorState, ?targetVariation:String):Dialog
public static function openSongMetadataDialog(state:ChartEditorState, erect:Bool, targetVariation:String):Dialog
{
if (targetVariation == null) targetVariation = Constants.DEFAULT_VARIATION;
var dialog:Null<Dialog> = openDialog(state, CHART_EDITOR_DIALOG_SONG_METADATA_LAYOUT, true, false);
if (dialog == null) throw 'Could not locate Song Metadata dialog';
@ -719,13 +769,10 @@ class ChartEditorDialogHandler
dialog.hideDialog(DialogButton.CANCEL);
}
var newSongMetadata:SongMetadata = new SongMetadata('', '', 'default');
var newSongMetadata:SongMetadata = new SongMetadata('', '', Constants.DEFAULT_VARIATION);
newSongMetadata.playData.difficulties = switch (targetVariation)
{
case 'erect': ['erect', 'nightmare'];
default: ['easy', 'normal', 'hard'];
};
newSongMetadata.variation = targetVariation;
newSongMetadata.playData.difficulties = (erect) ? ['erect', 'nightmare'] : ['easy', 'normal', 'hard'];
var inputSongName:Null<TextField> = dialog.findComponent('inputSongName', TextField);
if (inputSongName == null) throw 'Could not locate inputSongName TextField in Song Metadata dialog';
@ -830,11 +877,14 @@ class ChartEditorDialogHandler
var dialogContinue:Null<Button> = dialog.findComponent('dialogContinue', Button);
if (dialogContinue == null) throw 'Could not locate dialogContinue button in Song Metadata dialog';
dialogContinue.onClick = (_event) -> {
state.songMetadata.clear();
if (targetVariation == Constants.DEFAULT_VARIATION) state.songMetadata.clear();
state.songMetadata.set(targetVariation, newSongMetadata);
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
state.difficultySelectDirty = true;
dialog.hideDialog(DialogButton.APPLY);
}

View file

@ -375,14 +375,15 @@ class ChartEditorImportExportHandler
// We have to force write because the program will die before the save dialog is closed.
trace('Force exporting to $targetPath...');
FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode);
// state.saveDataDirty = false; // Don't edit the saveData flag because the app might be closing.
}
else
{
// Force writing to the specific path (user pressed CTRL-SHIFT-S)
// Force write since we know what file the user wants to overwrite.
trace('Force exporting to $targetPath...');
FileUtil.saveFilesAsZIPToPath(zipEntries, targetPath, targetMode);
state.saveDataDirty = false; // Don't edit the saveData flag because the app might be closing.
state.saveDataDirty = false;
}
}
else

View file

@ -155,6 +155,20 @@ class HaxeUIState extends MusicBeatState
}
}
function addTooltip(key:String, text:String):Void
{
var target:Component = findComponent(key);
if (target == null)
{
// Gracefully handle the case where the item can't be located.
trace('WARN: Could not locate menu item: $key');
}
else
{
target.tooltip = text;
}
}
/**
* Add an onChange listener to a HaxeUI input component such as a slider or text field.
*/