mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-23 16:17:53 -05:00
Merge pull request #246 from FunkinCrew/icon-charswitcher
Icon charswitcher
This commit is contained in:
commit
9d9cda9ab1
15 changed files with 564 additions and 273 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit 42b4bee68600bfb9c31831ccdd0579c00930d771
|
||||
Subproject commit 7d59681870d2b73417a9f5b553720e8b7120bbde
|
4
hmm.json
4
hmm.json
|
@ -49,14 +49,14 @@
|
|||
"name": "haxeui-core",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "5d4ac180f85b39e72624f4b8d17925d91ebe4278",
|
||||
"ref": "032192e849cdb7d1070c0a3241c58ee555ffaccc",
|
||||
"url": "https://github.com/haxeui/haxeui-core"
|
||||
},
|
||||
{
|
||||
"name": "haxeui-flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "89a4cf621e5c204922f7a12fbde5d1d84f8b47f5",
|
||||
"ref": "d90758b229d05206400df867d333c79d9fdbd478",
|
||||
"url": "https://github.com/haxeui/haxeui-flixel"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -280,6 +280,36 @@ class CharacterDataParser
|
|||
return characterCache.keys().array();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Hardcode this.
|
||||
*/
|
||||
public static function getCharPixelIconAsset(char:String):String
|
||||
{
|
||||
var icon:String = char;
|
||||
|
||||
switch (icon)
|
||||
{
|
||||
case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf":
|
||||
icon = "bf";
|
||||
case "monster-christmas":
|
||||
icon = "monster";
|
||||
case "mom" | "mom-car":
|
||||
icon = "mommy";
|
||||
case "pico-blazin" | "pico-playable" | "pico-speaker":
|
||||
icon = "pico";
|
||||
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen":
|
||||
icon = "gf";
|
||||
case "dad":
|
||||
icon = "daddy";
|
||||
case "darnell-blazin":
|
||||
icon = "darnell";
|
||||
case "senpai-angry":
|
||||
icon = "senpai";
|
||||
}
|
||||
|
||||
return Paths.image("freeplay/icons/" + icon + "pixel");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the character data cache.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package funkin.ui.debug.charting;
|
||||
|
||||
import funkin.util.logging.CrashHandler;
|
||||
import haxe.ui.containers.HBox;
|
||||
import haxe.ui.containers.Grid;
|
||||
import haxe.ui.containers.ScrollView;
|
||||
import haxe.ui.containers.menus.MenuBar;
|
||||
import flixel.addons.display.FlxSliceSprite;
|
||||
import flixel.addons.display.FlxTiledSprite;
|
||||
|
@ -106,6 +109,8 @@ import haxe.ui.containers.menus.MenuItem;
|
|||
import haxe.ui.containers.menus.MenuCheckBox;
|
||||
import haxe.ui.containers.TreeView;
|
||||
import haxe.ui.containers.TreeViewNode;
|
||||
import haxe.ui.components.Image;
|
||||
import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox;
|
||||
import haxe.ui.core.Component;
|
||||
import haxe.ui.core.Screen;
|
||||
import haxe.ui.events.DragEvent;
|
||||
|
@ -114,6 +119,7 @@ import haxe.ui.events.UIEvent;
|
|||
import haxe.ui.events.UIEvent;
|
||||
import haxe.ui.focus.FocusManager;
|
||||
import openfl.display.BitmapData;
|
||||
import flixel.input.mouse.FlxMouseEvent;
|
||||
import flixel.text.FlxText;
|
||||
|
||||
using Lambda;
|
||||
|
@ -2310,6 +2316,21 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
if (!Preferences.debugDisplay) menubar.paddingLeft = null;
|
||||
|
||||
this.setupNotifications();
|
||||
|
||||
// Setup character dropdowns.
|
||||
FlxMouseEvent.add(healthIconDad, function(_) {
|
||||
if (!isCursorOverHaxeUI)
|
||||
{
|
||||
this.openCharacterDropdown(CharacterType.DAD, true);
|
||||
}
|
||||
});
|
||||
|
||||
FlxMouseEvent.add(healthIconBF, function(_) {
|
||||
if (!isCursorOverHaxeUI)
|
||||
{
|
||||
this.openCharacterDropdown(CharacterType.BF, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2350,13 +2371,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
else
|
||||
{
|
||||
Conductor.currentTimeChange.bpm += 1;
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
}
|
||||
|
||||
playbarBPM.onRightClick = _ -> {
|
||||
Conductor.currentTimeChange.bpm -= 1;
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
|
||||
// Add functionality to the menu items.
|
||||
|
@ -2622,7 +2643,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
* Open the backups folder in the file explorer.
|
||||
* Don't call this on HTML5.
|
||||
*/
|
||||
function openBackupsFolder():Void
|
||||
function openBackupsFolder(?_):Void
|
||||
{
|
||||
#if sys
|
||||
// TODO: Is there a way to open a folder and highlight a file in it?
|
||||
|
@ -3411,6 +3432,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
|
||||
var overlapsSelection:Bool = FlxG.mouse.overlaps(renderedSelectionSquares);
|
||||
|
||||
var overlapsHealthIcons:Bool = FlxG.mouse.overlaps(healthIconBF) || FlxG.mouse.overlaps(healthIconDad);
|
||||
|
||||
if (FlxG.mouse.justPressedMiddle)
|
||||
{
|
||||
if (scrollAnchorScreenPos == null)
|
||||
|
@ -3430,11 +3453,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
scrollAnchorScreenPos = null;
|
||||
}
|
||||
else if (gridPlayheadScrollArea != null && FlxG.mouse.overlaps(gridPlayheadScrollArea))
|
||||
else if (gridPlayheadScrollArea != null && FlxG.mouse.overlaps(gridPlayheadScrollArea) && !isCursorOverHaxeUI)
|
||||
{
|
||||
gridPlayheadScrollAreaPressed = true;
|
||||
}
|
||||
else if (notePreview != null && !isCursorOverHaxeUI && FlxG.mouse.overlaps(notePreview))
|
||||
else if (notePreview != null && FlxG.mouse.overlaps(notePreview) && !isCursorOverHaxeUI)
|
||||
{
|
||||
// Clicked note preview
|
||||
notePreviewScrollAreaStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
|
||||
|
@ -4125,6 +4148,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
targetCursorMode = Cell;
|
||||
}
|
||||
}
|
||||
else if (overlapsHealthIcons)
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4155,7 +4182,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
difficultySelectDirty = false;
|
||||
|
||||
// Manage the Select Difficulty tree view.
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
if (difficultyToolbox == null) return;
|
||||
|
||||
var treeView:Null<TreeView> = difficultyToolbox.findComponent('difficultyToolboxTree');
|
||||
|
@ -4202,7 +4229,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
function handlePlayerPreviewToolbox():Void
|
||||
{
|
||||
// Manage the Select Difficulty tree view.
|
||||
var charPreviewToolbox:Null<CollapsibleDialog> = this.getToolbox(CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT);
|
||||
var charPreviewToolbox:Null<CollapsibleDialog> = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT);
|
||||
if (charPreviewToolbox == null) return;
|
||||
|
||||
// TODO: Re-enable the player preview once we figure out the performance issues.
|
||||
|
@ -4238,7 +4265,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
function handleOpponentPreviewToolbox():Void
|
||||
{
|
||||
// Manage the Select Difficulty tree view.
|
||||
var charPreviewToolbox:Null<CollapsibleDialog> = this.getToolbox(CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT);
|
||||
var charPreviewToolbox:Null<CollapsibleDialog> = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT);
|
||||
if (charPreviewToolbox == null) return;
|
||||
|
||||
// TODO: Re-enable the player preview once we figure out the performance issues.
|
||||
|
@ -4995,7 +5022,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
Conductor.mapTimeChanges(this.currentSongMetadata.timeChanges);
|
||||
|
||||
refreshDifficultyTreeSelection();
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5004,7 +5031,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
selectedDifficulty = prevDifficulty;
|
||||
|
||||
refreshDifficultyTreeSelection();
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -5023,7 +5050,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
selectedDifficulty = nextDifficulty;
|
||||
|
||||
refreshDifficultyTreeSelection();
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5032,7 +5059,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
selectedDifficulty = nextDifficulty;
|
||||
|
||||
refreshDifficultyTreeSelection();
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5145,7 +5172,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
if (treeView == null)
|
||||
{
|
||||
// Manage the Select Difficulty tree view.
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
if (difficultyToolbox == null) return;
|
||||
|
||||
treeView = difficultyToolbox.findComponent('difficultyToolboxTree');
|
||||
|
@ -5165,7 +5192,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
if (treeView == null)
|
||||
{
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
if (difficultyToolbox == null) return null;
|
||||
|
||||
treeView = difficultyToolbox.findComponent('difficultyToolboxTree');
|
||||
|
@ -5209,8 +5236,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
trace('Changing difficulty to "$variation:$difficulty"');
|
||||
selectedVariation = variation;
|
||||
selectedDifficulty = difficulty;
|
||||
// refreshDifficultyTreeSelection(treeView);
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
// case 'song':
|
||||
// case 'variation':
|
||||
|
@ -5219,82 +5245,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
trace('Selected wrong node type, resetting selection.');
|
||||
var currentTreeDifficultyNode = getCurrentTreeDifficultyNode(treeView);
|
||||
if (currentTreeDifficultyNode != null) treeView.selectedNode = currentTreeDifficultyNode;
|
||||
refreshMetadataToolbox();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the difficulty changes, update the song metadata toolbox to reflect the new data.
|
||||
*/
|
||||
function refreshMetadataToolbox():Void
|
||||
{
|
||||
var toolbox:Null<CollapsibleDialog> = this.getToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
if (toolbox == null) return;
|
||||
|
||||
var inputSongName:Null<TextField> = toolbox.findComponent('inputSongName', TextField);
|
||||
if (inputSongName != null) inputSongName.value = currentSongMetadata.songName;
|
||||
|
||||
var inputSongArtist:Null<TextField> = toolbox.findComponent('inputSongArtist', TextField);
|
||||
if (inputSongArtist != null) inputSongArtist.value = currentSongMetadata.artist;
|
||||
|
||||
var inputStage:Null<DropDown> = toolbox.findComponent('inputStage', DropDown);
|
||||
if (inputStage != null) inputStage.value = currentSongMetadata.playData.stage;
|
||||
|
||||
var inputNoteStyle:Null<DropDown> = toolbox.findComponent('inputNoteStyle', DropDown);
|
||||
if (inputNoteStyle != null) inputNoteStyle.value = currentSongMetadata.playData.noteStyle;
|
||||
|
||||
var inputBPM:Null<NumberStepper> = toolbox.findComponent('inputBPM', NumberStepper);
|
||||
if (inputBPM != null) inputBPM.value = currentSongMetadata.timeChanges[0].bpm;
|
||||
|
||||
var labelScrollSpeed:Null<Label> = toolbox.findComponent('labelScrollSpeed', Label);
|
||||
if (labelScrollSpeed != null) labelScrollSpeed.text = 'Scroll Speed: ${currentSongChartScrollSpeed}x';
|
||||
|
||||
var inputScrollSpeed:Null<Slider> = toolbox.findComponent('inputScrollSpeed', Slider);
|
||||
if (inputScrollSpeed != null) inputScrollSpeed.value = currentSongChartScrollSpeed;
|
||||
|
||||
var frameVariation:Null<Frame> = toolbox.findComponent('frameVariation', Frame);
|
||||
if (frameVariation != null) frameVariation.text = 'Variation: ${selectedVariation.toTitleCase()}';
|
||||
var frameDifficulty:Null<Frame> = toolbox.findComponent('frameDifficulty', Frame);
|
||||
if (frameDifficulty != null) frameDifficulty.text = 'Difficulty: ${selectedDifficulty.toTitleCase()}';
|
||||
|
||||
var inputStage:Null<DropDown> = toolbox.findComponent('inputStage', DropDown);
|
||||
var stageId:String = currentSongMetadata.playData.stage;
|
||||
var stageData:Null<StageData> = StageDataParser.parseStageData(stageId);
|
||||
if (inputStage != null)
|
||||
{
|
||||
inputStage.value = (stageData != null) ?
|
||||
{id: stageId, text: stageData.name} :
|
||||
{id: "mainStage", text: "Main Stage"};
|
||||
}
|
||||
|
||||
var inputCharacterPlayer:Null<DropDown> = toolbox.findComponent('inputCharacterPlayer', DropDown);
|
||||
var charIdPlayer:String = currentSongMetadata.playData.characters.player;
|
||||
var charDataPlayer:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdPlayer);
|
||||
if (inputCharacterPlayer != null)
|
||||
{
|
||||
inputCharacterPlayer.value = (charDataPlayer != null) ?
|
||||
{id: charIdPlayer, text: charDataPlayer.name} :
|
||||
{id: "bf", text: "Boyfriend"};
|
||||
}
|
||||
|
||||
var inputCharacterOpponent:Null<DropDown> = toolbox.findComponent('inputCharacterOpponent', DropDown);
|
||||
var charIdOpponent:String = currentSongMetadata.playData.characters.opponent;
|
||||
var charDataOpponent:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdOpponent);
|
||||
if (inputCharacterOpponent != null)
|
||||
{
|
||||
inputCharacterOpponent.value = (charDataOpponent != null) ?
|
||||
{id: charIdOpponent, text: charDataOpponent.name} :
|
||||
{id: "dad", text: "Dad"};
|
||||
}
|
||||
|
||||
var inputCharacterGirlfriend:Null<DropDown> = toolbox.findComponent('inputCharacterGirlfriend', DropDown);
|
||||
var charIdGirlfriend:String = currentSongMetadata.playData.characters.girlfriend;
|
||||
var charDataGirlfriend:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdGirlfriend);
|
||||
if (inputCharacterGirlfriend != null)
|
||||
{
|
||||
inputCharacterGirlfriend.value = (charDataGirlfriend != null) ?
|
||||
{id: charIdGirlfriend, text: charDataGirlfriend.name} :
|
||||
{id: "none", text: "None"};
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,12 @@ package funkin.ui.debug.charting.dialogs;
|
|||
|
||||
import haxe.ui.containers.dialogs.Dialog;
|
||||
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
|
||||
import haxe.ui.animation.AnimationBuilder;
|
||||
import haxe.ui.styles.EasingFunction;
|
||||
import haxe.ui.core.Component;
|
||||
|
||||
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
class ChartEditorBaseDialog extends Dialog
|
||||
{
|
||||
|
@ -25,6 +29,18 @@ class ChartEditorBaseDialog extends Dialog
|
|||
this.onDialogClosed = event -> onClose(event);
|
||||
}
|
||||
|
||||
public override function showDialog(modal:Bool = true):Void
|
||||
{
|
||||
super.showDialog(modal);
|
||||
fadeInComponent(this, 1);
|
||||
}
|
||||
|
||||
private override function onReady():Void
|
||||
{
|
||||
_overlay.opacity = 0;
|
||||
fadeInDialogOverlay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the dialog is closed.
|
||||
* Override this to add custom behavior.
|
||||
|
@ -54,6 +70,36 @@ class ChartEditorBaseDialog extends Dialog
|
|||
|
||||
this.closable = params.closable ?? false;
|
||||
}
|
||||
|
||||
static final OVERLAY_EASE_DURATION:Float = 0.2;
|
||||
static final OVERLAY_EASE_TYPE:String = "easeOut";
|
||||
|
||||
function fadeInDialogOverlay():Void
|
||||
{
|
||||
if (!modal)
|
||||
{
|
||||
trace('Dialog is not modal, skipping overlay fade...');
|
||||
return;
|
||||
}
|
||||
|
||||
if (_overlay == null)
|
||||
{
|
||||
trace('[WARN] Dialog overlay is null, skipping overlay fade...');
|
||||
return;
|
||||
}
|
||||
|
||||
fadeInComponent(_overlay, 0.5);
|
||||
}
|
||||
|
||||
function fadeInComponent(component:Component, fadeTo:Float = 1):Void
|
||||
{
|
||||
var builder = new AnimationBuilder(component, OVERLAY_EASE_DURATION, OVERLAY_EASE_TYPE);
|
||||
builder.setPosition(0, "opacity", 0, true); // 0% absolute
|
||||
builder.setPosition(100, "opacity", fadeTo, true);
|
||||
|
||||
trace('Fading in dialog component...');
|
||||
builder.play();
|
||||
}
|
||||
}
|
||||
|
||||
typedef DialogParams =
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package funkin.ui.debug.charting.dialogs;
|
||||
|
||||
import haxe.ui.containers.dialogs.Dialog;
|
||||
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
|
||||
import haxe.ui.animation.AnimationBuilder;
|
||||
import haxe.ui.styles.EasingFunction;
|
||||
import haxe.ui.core.Component;
|
||||
import haxe.ui.containers.menus.Menu;
|
||||
|
||||
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
class ChartEditorBaseMenu extends Menu
|
||||
{
|
||||
var state:ChartEditorState;
|
||||
|
||||
public function new(state:ChartEditorState)
|
||||
{
|
||||
super();
|
||||
|
||||
this.state = state;
|
||||
|
||||
// this.destroyOnClose = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package funkin.ui.debug.charting.dialogs;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.components.HealthIcon;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams;
|
||||
import funkin.util.SortUtil;
|
||||
import haxe.ui.components.Label;
|
||||
import haxe.ui.containers.Grid;
|
||||
import haxe.ui.containers.HBox;
|
||||
import haxe.ui.containers.ScrollView;
|
||||
import haxe.ui.containers.ScrollView;
|
||||
import haxe.ui.core.Screen;
|
||||
|
||||
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/dialogs/character-icon-selector.xml"))
|
||||
class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
|
||||
{
|
||||
public var charSelectScroll:ScrollView;
|
||||
public var charIconName:Label;
|
||||
|
||||
public function new(state2:ChartEditorState, charType:CharacterType, lockPosition:Bool = false)
|
||||
{
|
||||
super(state2);
|
||||
|
||||
initialize(charType, lockPosition);
|
||||
}
|
||||
|
||||
function initialize(charType:CharacterType, lockPosition:Bool)
|
||||
{
|
||||
var currentCharId:String = switch (charType)
|
||||
{
|
||||
case BF: state.currentSongMetadata.playData.characters.player;
|
||||
case GF: state.currentSongMetadata.playData.characters.girlfriend;
|
||||
case DAD: state.currentSongMetadata.playData.characters.opponent;
|
||||
default: throw 'Invalid charType: ' + charType;
|
||||
};
|
||||
|
||||
// Position this menu.
|
||||
var targetHealthIcon:Null<HealthIcon> = switch (charType)
|
||||
{
|
||||
case BF: state.healthIconBF;
|
||||
case DAD: state.healthIconDad;
|
||||
default: null;
|
||||
};
|
||||
|
||||
if (lockPosition && targetHealthIcon != null)
|
||||
{
|
||||
var healthIconBottomCenter:FlxPoint = new FlxPoint(targetHealthIcon.x + targetHealthIcon.width / 2, targetHealthIcon.y + targetHealthIcon.height);
|
||||
|
||||
this.x = healthIconBottomCenter.x - this.width / 2;
|
||||
this.y = healthIconBottomCenter.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.x = Screen.instance.currentMouseX;
|
||||
this.y = Screen.instance.currentMouseY;
|
||||
}
|
||||
|
||||
var charGrid = new Grid();
|
||||
charGrid.columns = 5;
|
||||
charGrid.width = 100;
|
||||
charSelectScroll.addComponent(charGrid);
|
||||
|
||||
var charIds:Array<String> = CharacterDataParser.listCharacterIds();
|
||||
charIds.sort(SortUtil.alphabetically);
|
||||
|
||||
var defaultText:String = '(choose a character)';
|
||||
|
||||
for (charIndex => charId in charIds)
|
||||
{
|
||||
var charData:CharacterData = CharacterDataParser.fetchCharacterData(charId);
|
||||
|
||||
var charButton = new haxe.ui.components.Button();
|
||||
charButton.width = 70;
|
||||
charButton.height = 70;
|
||||
charButton.padding = 8;
|
||||
charButton.iconPosition = "top";
|
||||
|
||||
if (charId == currentCharId)
|
||||
{
|
||||
// Scroll to the character if it is already selected.
|
||||
charSelectScroll.hscrollPos = Math.floor(charIndex / 5) * 80;
|
||||
charButton.selected = true;
|
||||
|
||||
defaultText = '${charData.name} [${charId}]';
|
||||
}
|
||||
|
||||
var LIMIT = 6;
|
||||
charButton.icon = CharacterDataParser.getCharPixelIconAsset(charId);
|
||||
charButton.text = charData.name.length > LIMIT ? '${charData.name.substr(0, LIMIT)}.' : '${charData.name}';
|
||||
|
||||
charButton.onClick = _ -> {
|
||||
switch (charType)
|
||||
{
|
||||
case BF: state.currentSongMetadata.playData.characters.player = charId;
|
||||
case GF: state.currentSongMetadata.playData.characters.girlfriend = charId;
|
||||
case DAD: state.currentSongMetadata.playData.characters.opponent = charId;
|
||||
default: throw 'Invalid charType: ' + charType;
|
||||
};
|
||||
|
||||
state.healthIconsDirty = true;
|
||||
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
};
|
||||
|
||||
charButton.onMouseOver = _ -> {
|
||||
charIconName.text = '${charData.name} [${charId}]';
|
||||
};
|
||||
charButton.onMouseOut = _ -> {
|
||||
charIconName.text = defaultText;
|
||||
};
|
||||
charGrid.addComponent(charButton);
|
||||
}
|
||||
|
||||
charIconName.text = defaultText;
|
||||
}
|
||||
|
||||
public static function build(state2:ChartEditorState, charType:CharacterType, lockPosition:Bool = false):ChartEditorCharacterIconSelectorMenu
|
||||
{
|
||||
var menu = new ChartEditorCharacterIconSelectorMenu(state2, charType, lockPosition);
|
||||
|
||||
Screen.instance.addComponent(menu);
|
||||
|
||||
return menu;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import haxe.ui.containers.dialogs.Dialogs;
|
|||
import haxe.ui.notifications.NotificationManager;
|
||||
import haxe.ui.notifications.NotificationType;
|
||||
|
||||
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/dialogs/upload-chart.xml"))
|
||||
class ChartEditorUploadChartDialog extends ChartEditorBaseDialog
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ import funkin.play.song.Song;
|
|||
import funkin.play.stage.StageData;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorAboutDialog;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogDropTarget;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorCharacterIconSelectorMenu;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorUploadChartDialog;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorWelcomeDialog;
|
||||
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||
|
@ -39,6 +40,7 @@ import haxe.ui.containers.dialogs.Dialog;
|
|||
import haxe.ui.containers.dialogs.Dialog.DialogButton;
|
||||
import haxe.ui.containers.dialogs.Dialogs;
|
||||
import haxe.ui.containers.Form;
|
||||
import haxe.ui.containers.menus.Menu;
|
||||
import haxe.ui.containers.VBox;
|
||||
import haxe.ui.core.Component;
|
||||
import haxe.ui.events.UIEvent;
|
||||
|
@ -286,6 +288,15 @@ class ChartEditorDialogHandler
|
|||
};
|
||||
}
|
||||
|
||||
public static function openCharacterDropdown(state:ChartEditorState, charType:CharacterType, lockPosition:Bool = false):Null<Menu>
|
||||
{
|
||||
var menu = ChartEditorCharacterIconSelectorMenu.build(state, charType, lockPosition);
|
||||
|
||||
menu.zIndex = 1000;
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
public static function openCreateSongWizardBasicOnly(state:ChartEditorState, closable:Bool):Void
|
||||
{
|
||||
// Step 1. Song Metadata
|
||||
|
|
|
@ -99,7 +99,7 @@ class ChartEditorImportExportHandler
|
|||
state.switchToCurrentInstrumental();
|
||||
state.postLoadInstrumental();
|
||||
|
||||
state.refreshMetadataToolbox();
|
||||
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
|
||||
state.success('Success', 'Loaded song (${rawSongMetadata[0].songName})');
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import haxe.ui.containers.HBox;
|
|||
import haxe.ui.notifications.Notification;
|
||||
import haxe.ui.notifications.NotificationManager;
|
||||
import haxe.ui.notifications.NotificationType;
|
||||
import haxe.ui.notifications.NotificationData.NotificationActionData;
|
||||
|
||||
class ChartEditorNotificationHandler
|
||||
{
|
||||
|
@ -77,7 +78,7 @@ class ChartEditorNotificationHandler
|
|||
* @param actions The actions to add to the notification.
|
||||
* @return The notification that was sent.
|
||||
*/
|
||||
public static function infoWithActions(state:ChartEditorState, title:String, body:String, actions:Array<NotificationAction>):Notification
|
||||
public static function infoWithActions(state:ChartEditorState, title:String, body:String, actions:Array<NotificationActionData>):Notification
|
||||
{
|
||||
return sendNotification(state, title, body, NotificationType.Info, actions);
|
||||
}
|
||||
|
@ -101,7 +102,8 @@ class ChartEditorNotificationHandler
|
|||
NotificationManager.instance.removeNotification(notif);
|
||||
}
|
||||
|
||||
static function sendNotification(state:ChartEditorState, title:String, body:String, ?type:NotificationType, ?actions:Array<NotificationAction>):Notification
|
||||
static function sendNotification(state:ChartEditorState, title:String, body:String, ?type:NotificationType,
|
||||
?actions:Array<NotificationActionData>):Notification
|
||||
{
|
||||
var actionNames:Array<String> = actions == null ? [] : actions.map(action -> action.text);
|
||||
|
||||
|
@ -111,10 +113,10 @@ class ChartEditorNotificationHandler
|
|||
body: body,
|
||||
type: type ?? NotificationType.Default,
|
||||
expiryMs: Constants.NOTIFICATION_DISMISS_TIME,
|
||||
actions: actionNames
|
||||
actions: actions
|
||||
});
|
||||
|
||||
if (actionNames.length > 0)
|
||||
if (actions != null && actions.length > 0)
|
||||
{
|
||||
// TODO: Tell Ian that this is REALLY dumb.
|
||||
var actionsContainer:HBox = notif.findComponent('actionsContainer', HBox);
|
||||
|
@ -122,13 +124,13 @@ class ChartEditorNotificationHandler
|
|||
if (Std.isOfType(component, Button))
|
||||
{
|
||||
var button:Button = cast component;
|
||||
var action:Null<NotificationAction> = actions.find(action -> action.text == button.text);
|
||||
var action:Null<NotificationActionData> = actions.find(action -> action.text == button.text);
|
||||
if (action != null && action.callback != null)
|
||||
{
|
||||
button.onClick = function(_) {
|
||||
// Don't allow actions to be clicked while the playtest is open.
|
||||
if (state.subState != null) return;
|
||||
action.callback();
|
||||
action.callback(action);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import funkin.play.character.BaseCharacter.CharacterType;
|
|||
import funkin.play.event.SongEvent;
|
||||
import funkin.data.event.SongEventData;
|
||||
import funkin.data.song.SongData.SongTimeChange;
|
||||
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
|
@ -35,6 +34,8 @@ import haxe.ui.containers.Box;
|
|||
import haxe.ui.containers.dialogs.CollapsibleDialog;
|
||||
import haxe.ui.containers.dialogs.Dialog.DialogButton;
|
||||
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
|
||||
import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox;
|
||||
import funkin.ui.debug.charting.toolboxes.ChartEditorMetadataToolbox;
|
||||
import haxe.ui.containers.Frame;
|
||||
import haxe.ui.containers.Grid;
|
||||
import haxe.ui.containers.TreeView;
|
||||
|
@ -85,7 +86,8 @@ class ChartEditorToolboxHandler
|
|||
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
|
||||
onShowToolboxDifficulty(state, toolbox);
|
||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
|
||||
onShowToolboxMetadata(state, toolbox);
|
||||
// TODO: Fix this.
|
||||
cast(toolbox, ChartEditorBaseToolbox).refresh();
|
||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
|
||||
onShowToolboxPlayerPreview(state, toolbox);
|
||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:
|
||||
|
@ -140,6 +142,22 @@ class ChartEditorToolboxHandler
|
|||
}
|
||||
}
|
||||
|
||||
public static function refreshToolbox(state:ChartEditorState, id:String):Void
|
||||
{
|
||||
var toolbox:Null<ChartEditorBaseToolbox> = cast state.activeToolboxes.get(id);
|
||||
|
||||
if (toolbox == null) return;
|
||||
|
||||
if (toolbox != null)
|
||||
{
|
||||
toolbox.refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('ChartEditorToolboxHandler.refreshToolbox() - Could not retrieve toolbox: $id');
|
||||
}
|
||||
}
|
||||
|
||||
public static function rememberOpenToolboxes(state:ChartEditorState):Void {}
|
||||
|
||||
public static function openRememberedToolboxes(state:ChartEditorState):Void {}
|
||||
|
@ -211,7 +229,19 @@ class ChartEditorToolboxHandler
|
|||
* @param id The asset ID of the toolbox layout.
|
||||
* @return The toolbox.
|
||||
*/
|
||||
public static function getToolbox(state:ChartEditorState, id:String):Null<CollapsibleDialog>
|
||||
public static function getToolbox_OLD(state:ChartEditorState, id:String):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||
|
||||
// Initialize the toolbox without showing it.
|
||||
if (toolbox == null) toolbox = initToolbox(state, id);
|
||||
|
||||
if (toolbox == null) throw 'ChartEditorToolboxHandler.getToolbox_OLD() - Could not retrieve or build toolbox: $id';
|
||||
|
||||
return toolbox;
|
||||
}
|
||||
|
||||
public static function getToolbox(state:ChartEditorState, id:String):Null<ChartEditorBaseToolbox>
|
||||
{
|
||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||
|
||||
|
@ -220,7 +250,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
if (toolbox == null) throw 'ChartEditorToolboxHandler.getToolbox() - Could not retrieve or build toolbox: $id';
|
||||
|
||||
return toolbox;
|
||||
return cast toolbox;
|
||||
}
|
||||
|
||||
static function buildToolboxNoteDataLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
|
@ -548,180 +578,15 @@ class ChartEditorToolboxHandler
|
|||
|
||||
static function onHideToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||
|
||||
static function buildToolboxMetadataLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
static function buildToolboxMetadataLayout(state:ChartEditorState):Null<ChartEditorBaseToolbox>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast RuntimeComponentBuilder.fromAsset(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
var toolbox:ChartEditorBaseToolbox = ChartEditorMetadataToolbox.build(state);
|
||||
|
||||
if (toolbox == null) return null;
|
||||
|
||||
// Starting position.
|
||||
toolbox.x = 150;
|
||||
toolbox.y = 250;
|
||||
|
||||
toolbox.onDialogClosed = function(event:UIEvent) {
|
||||
state.menubarItemToggleToolboxMetadata.selected = false;
|
||||
}
|
||||
|
||||
var inputSongName:Null<TextField> = toolbox.findComponent('inputSongName', TextField);
|
||||
if (inputSongName == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputSongName component.';
|
||||
inputSongName.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
if (valid)
|
||||
{
|
||||
inputSongName.removeClass('invalid-value');
|
||||
state.currentSongMetadata.songName = event.target.text;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.currentSongMetadata.songName = '';
|
||||
}
|
||||
};
|
||||
inputSongName.value = state.currentSongMetadata.songName;
|
||||
|
||||
var inputSongArtist:Null<TextField> = toolbox.findComponent('inputSongArtist', TextField);
|
||||
if (inputSongArtist == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputSongArtist component.';
|
||||
inputSongArtist.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
if (valid)
|
||||
{
|
||||
inputSongArtist.removeClass('invalid-value');
|
||||
state.currentSongMetadata.artist = event.target.text;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.currentSongMetadata.artist = '';
|
||||
}
|
||||
};
|
||||
inputSongArtist.value = state.currentSongMetadata.artist;
|
||||
|
||||
var inputStage:Null<DropDown> = toolbox.findComponent('inputStage', DropDown);
|
||||
if (inputStage == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputStage component.';
|
||||
inputStage.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.data != null && event.data.id != null;
|
||||
|
||||
if (valid)
|
||||
{
|
||||
state.currentSongMetadata.playData.stage = event.data.id;
|
||||
}
|
||||
};
|
||||
var startingValueStage = ChartEditorDropdowns.populateDropdownWithStages(inputStage, state.currentSongMetadata.playData.stage);
|
||||
inputStage.value = startingValueStage;
|
||||
|
||||
var inputNoteStyle:Null<DropDown> = toolbox.findComponent('inputNoteStyle', DropDown);
|
||||
if (inputNoteStyle == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputNoteStyle component.';
|
||||
inputNoteStyle.onChange = function(event:UIEvent) {
|
||||
if (event.data?.id == null) return;
|
||||
state.currentSongNoteStyle = event.data.id;
|
||||
};
|
||||
inputNoteStyle.value = state.currentSongNoteStyle;
|
||||
|
||||
// By using this flag, we prevent the dropdown value from changing while it is being populated.
|
||||
|
||||
var inputCharacterPlayer:Null<DropDown> = toolbox.findComponent('inputCharacterPlayer', DropDown);
|
||||
if (inputCharacterPlayer == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputCharacterPlayer component.';
|
||||
inputCharacterPlayer.onChange = function(event:UIEvent) {
|
||||
if (event.data?.id == null) return;
|
||||
state.currentSongMetadata.playData.characters.player = event.data.id;
|
||||
};
|
||||
var startingValuePlayer = ChartEditorDropdowns.populateDropdownWithCharacters(inputCharacterPlayer, CharacterType.BF,
|
||||
state.currentSongMetadata.playData.characters.player);
|
||||
inputCharacterPlayer.value = startingValuePlayer;
|
||||
|
||||
var inputCharacterOpponent:Null<DropDown> = toolbox.findComponent('inputCharacterOpponent', DropDown);
|
||||
if (inputCharacterOpponent == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputCharacterOpponent component.';
|
||||
inputCharacterOpponent.onChange = function(event:UIEvent) {
|
||||
if (event.data?.id == null) return;
|
||||
state.currentSongMetadata.playData.characters.opponent = event.data.id;
|
||||
};
|
||||
var startingValueOpponent = ChartEditorDropdowns.populateDropdownWithCharacters(inputCharacterOpponent, CharacterType.DAD,
|
||||
state.currentSongMetadata.playData.characters.opponent);
|
||||
inputCharacterOpponent.value = startingValueOpponent;
|
||||
|
||||
var inputCharacterGirlfriend:Null<DropDown> = toolbox.findComponent('inputCharacterGirlfriend', DropDown);
|
||||
if (inputCharacterGirlfriend == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputCharacterGirlfriend component.';
|
||||
inputCharacterGirlfriend.onChange = function(event:UIEvent) {
|
||||
if (event.data?.id == null) return;
|
||||
state.currentSongMetadata.playData.characters.girlfriend = event.data.id == "none" ? "" : event.data.id;
|
||||
};
|
||||
var startingValueGirlfriend = ChartEditorDropdowns.populateDropdownWithCharacters(inputCharacterGirlfriend, CharacterType.GF,
|
||||
state.currentSongMetadata.playData.characters.girlfriend);
|
||||
inputCharacterGirlfriend.value = startingValueGirlfriend;
|
||||
|
||||
var inputBPM:Null<NumberStepper> = toolbox.findComponent('inputBPM', NumberStepper);
|
||||
if (inputBPM == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputBPM component.';
|
||||
inputBPM.onChange = function(event:UIEvent) {
|
||||
if (event.value == null || event.value <= 0) return;
|
||||
|
||||
// Use a command so we can undo/redo this action.
|
||||
var startingBPM = state.currentSongMetadata.timeChanges[0].bpm;
|
||||
if (event.value != startingBPM)
|
||||
{
|
||||
state.performCommand(new ChangeStartingBPMCommand(event.value));
|
||||
}
|
||||
};
|
||||
inputBPM.value = state.currentSongMetadata.timeChanges[0].bpm;
|
||||
|
||||
var inputOffsetInst:Null<NumberStepper> = toolbox.findComponent('inputOffsetInst', NumberStepper);
|
||||
if (inputOffsetInst == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputOffsetInst component.';
|
||||
inputOffsetInst.onChange = function(event:UIEvent) {
|
||||
if (event.value == null) return;
|
||||
|
||||
state.currentInstrumentalOffset = event.value;
|
||||
Conductor.instrumentalOffset = event.value;
|
||||
// Update song length.
|
||||
state.songLengthInMs = (state.audioInstTrack?.length ?? 1000.0) + Conductor.instrumentalOffset;
|
||||
};
|
||||
inputOffsetInst.value = state.currentInstrumentalOffset;
|
||||
|
||||
var inputOffsetVocal:Null<NumberStepper> = toolbox.findComponent('inputOffsetVocal', NumberStepper);
|
||||
if (inputOffsetVocal == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputOffsetVocal component.';
|
||||
inputOffsetVocal.onChange = function(event:UIEvent) {
|
||||
if (event.value == null) return;
|
||||
|
||||
state.currentSongMetadata.offsets.setVocalOffset(state.currentSongMetadata.playData.characters.player, event.value);
|
||||
};
|
||||
inputOffsetVocal.value = state.currentSongMetadata.offsets.getVocalOffset(state.currentSongMetadata.playData.characters.player);
|
||||
|
||||
var labelScrollSpeed:Null<Label> = toolbox.findComponent('labelScrollSpeed', Label);
|
||||
if (labelScrollSpeed == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find labelScrollSpeed component.';
|
||||
|
||||
var inputScrollSpeed:Null<Slider> = toolbox.findComponent('inputScrollSpeed', Slider);
|
||||
if (inputScrollSpeed == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputScrollSpeed component.';
|
||||
inputScrollSpeed.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.value != null && event.target.value > 0;
|
||||
|
||||
if (valid)
|
||||
{
|
||||
inputScrollSpeed.removeClass('invalid-value');
|
||||
state.currentSongChartScrollSpeed = event.target.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.currentSongChartScrollSpeed = 1.0;
|
||||
}
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
|
||||
};
|
||||
inputScrollSpeed.value = state.currentSongChartScrollSpeed;
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
|
||||
|
||||
var frameVariation:Null<Frame> = toolbox.findComponent('frameVariation', Frame);
|
||||
if (frameVariation == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find frameVariation component.';
|
||||
frameVariation.text = 'Variation: ${state.selectedVariation.toTitleCase()}';
|
||||
|
||||
var frameDifficulty:Null<Frame> = toolbox.findComponent('frameDifficulty', Frame);
|
||||
if (frameDifficulty == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find frameDifficulty component.';
|
||||
frameDifficulty.text = 'Difficulty: ${state.selectedDifficulty.toTitleCase()}';
|
||||
|
||||
return toolbox;
|
||||
}
|
||||
|
||||
static function onShowToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void
|
||||
{
|
||||
state.refreshMetadataToolbox();
|
||||
}
|
||||
|
||||
static function onHideToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||
|
||||
static function buildToolboxPlayerPreviewLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package funkin.ui.debug.charting.toolboxes;
|
||||
|
||||
import haxe.ui.containers.dialogs.Dialog;
|
||||
import haxe.ui.containers.dialogs.CollapsibleDialog;
|
||||
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
|
||||
import haxe.ui.core.Component;
|
||||
|
||||
/**
|
||||
* The base class for the Toolboxes (manipulatable, arrangeable control windows) in the Chart Editor.
|
||||
*/
|
||||
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
class ChartEditorBaseToolbox extends CollapsibleDialog
|
||||
{
|
||||
var state:ChartEditorState;
|
||||
|
||||
private function new(state:ChartEditorState)
|
||||
{
|
||||
super();
|
||||
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to implement this.
|
||||
*/
|
||||
public function refresh() {}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
package funkin.ui.debug.charting.toolboxes;
|
||||
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||
import haxe.ui.components.Button;
|
||||
import haxe.ui.components.CheckBox;
|
||||
import haxe.ui.components.DropDown;
|
||||
import haxe.ui.components.HorizontalSlider;
|
||||
import haxe.ui.components.Label;
|
||||
import haxe.ui.components.NumberStepper;
|
||||
import haxe.ui.components.Slider;
|
||||
import haxe.ui.components.TextField;
|
||||
import haxe.ui.containers.Box;
|
||||
import haxe.ui.containers.Frame;
|
||||
import haxe.ui.events.UIEvent;
|
||||
|
||||
/**
|
||||
* The toolbox which allows modifying information like Song Title, Scroll Speed, Characters/Stages, and starting BPM.
|
||||
*/
|
||||
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/toolboxes/metadata.xml"))
|
||||
class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
||||
{
|
||||
var inputSongName:TextField;
|
||||
var inputSongArtist:TextField;
|
||||
var inputStage:DropDown;
|
||||
var inputNoteStyle:DropDown;
|
||||
var buttonCharacterPlayer:Button;
|
||||
var buttonCharacterGirlfriend:Button;
|
||||
var buttonCharacterOpponent:Button;
|
||||
var inputBPM:NumberStepper;
|
||||
var inputOffsetInst:NumberStepper;
|
||||
var inputOffsetVocal:NumberStepper;
|
||||
var labelScrollSpeed:Label;
|
||||
var inputScrollSpeed:Slider;
|
||||
var frameVariation:Frame;
|
||||
var frameDifficulty:Frame;
|
||||
|
||||
public function new(state2:ChartEditorState)
|
||||
{
|
||||
super(state2);
|
||||
|
||||
initialize();
|
||||
|
||||
this.onDialogClosed = onClose;
|
||||
}
|
||||
|
||||
function onClose(event:UIEvent)
|
||||
{
|
||||
state.menubarItemToggleToolboxMetadata.selected = false;
|
||||
}
|
||||
|
||||
function initialize():Void
|
||||
{
|
||||
// Starting position.
|
||||
// TODO: Save and load this.
|
||||
this.x = 150;
|
||||
this.y = 250;
|
||||
|
||||
inputSongName.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
if (valid)
|
||||
{
|
||||
inputSongName.removeClass('invalid-value');
|
||||
state.currentSongMetadata.songName = event.target.text;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.currentSongMetadata.songName = '';
|
||||
}
|
||||
};
|
||||
|
||||
inputSongArtist.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
if (valid)
|
||||
{
|
||||
inputSongArtist.removeClass('invalid-value');
|
||||
state.currentSongMetadata.artist = event.target.text;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.currentSongMetadata.artist = '';
|
||||
}
|
||||
};
|
||||
|
||||
inputStage.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.data != null && event.data.id != null;
|
||||
|
||||
if (valid)
|
||||
{
|
||||
state.currentSongMetadata.playData.stage = event.data.id;
|
||||
}
|
||||
};
|
||||
var startingValueStage = ChartEditorDropdowns.populateDropdownWithStages(inputStage, state.currentSongMetadata.playData.stage);
|
||||
inputStage.value = startingValueStage;
|
||||
|
||||
inputNoteStyle.onChange = function(event:UIEvent) {
|
||||
if (event.data?.id == null) return;
|
||||
state.currentSongNoteStyle = event.data.id;
|
||||
};
|
||||
|
||||
inputBPM.onChange = function(event:UIEvent) {
|
||||
if (event.value == null || event.value <= 0) return;
|
||||
|
||||
// Use a command so we can undo/redo this action.
|
||||
var startingBPM = state.currentSongMetadata.timeChanges[0].bpm;
|
||||
if (event.value != startingBPM)
|
||||
{
|
||||
state.performCommand(new ChangeStartingBPMCommand(event.value));
|
||||
}
|
||||
};
|
||||
|
||||
inputOffsetInst.onChange = function(event:UIEvent) {
|
||||
if (event.value == null) return;
|
||||
|
||||
state.currentInstrumentalOffset = event.value;
|
||||
Conductor.instrumentalOffset = event.value;
|
||||
// Update song length.
|
||||
state.songLengthInMs = (state.audioInstTrack?.length ?? 1000.0) + Conductor.instrumentalOffset;
|
||||
};
|
||||
|
||||
inputOffsetVocal.onChange = function(event:UIEvent) {
|
||||
if (event.value == null) return;
|
||||
|
||||
state.currentSongMetadata.offsets.setVocalOffset(state.currentSongMetadata.playData.characters.player, event.value);
|
||||
};
|
||||
inputScrollSpeed.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.value != null && event.target.value > 0;
|
||||
|
||||
if (valid)
|
||||
{
|
||||
inputScrollSpeed.removeClass('invalid-value');
|
||||
state.currentSongChartScrollSpeed = event.target.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.currentSongChartScrollSpeed = 1.0;
|
||||
}
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
|
||||
};
|
||||
|
||||
buttonCharacterOpponent.onClick = function(_) {
|
||||
state.openCharacterDropdown(CharacterType.DAD, false);
|
||||
};
|
||||
|
||||
buttonCharacterGirlfriend.onClick = function(_) {
|
||||
state.openCharacterDropdown(CharacterType.GF, false);
|
||||
};
|
||||
|
||||
buttonCharacterPlayer.onClick = function(_) {
|
||||
state.openCharacterDropdown(CharacterType.BF, false);
|
||||
};
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
public override function refresh():Void
|
||||
{
|
||||
inputSongName.value = state.currentSongMetadata.songName;
|
||||
inputSongArtist.value = state.currentSongMetadata.artist;
|
||||
inputStage.value = state.currentSongMetadata.playData.stage;
|
||||
inputNoteStyle.value = state.currentSongMetadata.playData.noteStyle;
|
||||
inputBPM.value = state.currentSongMetadata.timeChanges[0].bpm;
|
||||
inputScrollSpeed.value = state.currentSongChartScrollSpeed;
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
|
||||
frameVariation.text = 'Variation: ${state.selectedVariation.toTitleCase()}';
|
||||
frameDifficulty.text = 'Difficulty: ${state.selectedDifficulty.toTitleCase()}';
|
||||
|
||||
var stageId:String = state.currentSongMetadata.playData.stage;
|
||||
var stageData:Null<StageData> = StageDataParser.parseStageData(stageId);
|
||||
if (inputStage != null)
|
||||
{
|
||||
inputStage.value = (stageData != null) ?
|
||||
{id: stageId, text: stageData.name} :
|
||||
{id: "mainStage", text: "Main Stage"};
|
||||
}
|
||||
|
||||
var LIMIT = 6;
|
||||
|
||||
var charDataOpponent:CharacterData = CharacterDataParser.fetchCharacterData(state.currentSongMetadata.playData.characters.opponent);
|
||||
buttonCharacterOpponent.icon = CharacterDataParser.getCharPixelIconAsset(state.currentSongMetadata.playData.characters.opponent);
|
||||
buttonCharacterOpponent.text = charDataOpponent.name.length > LIMIT ? '${charDataOpponent.name.substr(0, LIMIT)}.' : '${charDataOpponent.name}';
|
||||
|
||||
var charDataGirlfriend:CharacterData = CharacterDataParser.fetchCharacterData(state.currentSongMetadata.playData.characters.girlfriend);
|
||||
buttonCharacterGirlfriend.icon = CharacterDataParser.getCharPixelIconAsset(state.currentSongMetadata.playData.characters.girlfriend);
|
||||
buttonCharacterGirlfriend.text = charDataGirlfriend.name.length > LIMIT ? '${charDataGirlfriend.name.substr(0, LIMIT)}.' : '${charDataGirlfriend.name}';
|
||||
|
||||
var charDataPlayer:CharacterData = CharacterDataParser.fetchCharacterData(state.currentSongMetadata.playData.characters.player);
|
||||
buttonCharacterPlayer.icon = CharacterDataParser.getCharPixelIconAsset(state.currentSongMetadata.playData.characters.player);
|
||||
buttonCharacterPlayer.text = charDataPlayer.name.length > LIMIT ? '${charDataPlayer.name.substr(0, LIMIT)}.' : '${charDataPlayer.name}';
|
||||
}
|
||||
|
||||
public static function build(state:ChartEditorState):ChartEditorMetadataToolbox
|
||||
{
|
||||
return new ChartEditorMetadataToolbox(state);
|
||||
}
|
||||
}
|
|
@ -175,6 +175,7 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
trace(char);
|
||||
|
||||
// TODO: Put this in the character metadata where it belongs.
|
||||
// TODO: Also, can use CharacterDataParser.getCharPixelIconAsset()
|
||||
switch (char)
|
||||
{
|
||||
case "monster-christmas":
|
||||
|
|
Loading…
Reference in a new issue