mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2025-03-13 16:43:57 -04:00
Redo icon dialog and add support to metadata dialog.
This commit is contained in:
parent
b33f27f097
commit
52f16ce457
12 changed files with 511 additions and 391 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit 32364eacf09940cdba39457a2bb32ac1bca958be
|
||||
Subproject commit e591e9acc12b9aba6124332c4d66453f1f83368c
|
|
@ -280,6 +280,9 @@ class CharacterDataParser
|
|||
return characterCache.keys().array();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Hardcode this.
|
||||
*/
|
||||
public static function getCharPixelIconAsset(char:String):String
|
||||
{
|
||||
var icon:String = char;
|
||||
|
@ -290,9 +293,9 @@ class CharacterDataParser
|
|||
icon = "bf";
|
||||
case "monster-christmas":
|
||||
icon = "monster";
|
||||
case "mom-car":
|
||||
case "mom" | "mom-car":
|
||||
icon = "mommy";
|
||||
case "pico-blazin":
|
||||
case "pico-blazin" | "pico-playable" | "pico-speaker":
|
||||
icon = "pico";
|
||||
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen":
|
||||
icon = "gf";
|
||||
|
|
|
@ -110,6 +110,7 @@ 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;
|
||||
|
@ -2087,131 +2088,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
healthIconBF.flipX = true;
|
||||
add(healthIconBF);
|
||||
healthIconBF.zIndex = 30;
|
||||
|
||||
FlxMouseEvent.add(healthIconDad, function(_) {
|
||||
if (!isCursorOverHaxeUI) createAndOpenCharSelect(1);
|
||||
});
|
||||
|
||||
FlxMouseEvent.add(healthIconBF, function(_) {
|
||||
if (!isCursorOverHaxeUI) createAndOpenCharSelect(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param charType 0 == BF, 1 == Dad
|
||||
*/
|
||||
function createAndOpenCharSelect(charType:Int = 0):Void
|
||||
{
|
||||
var charData = currentSongMetadata.playData.characters;
|
||||
var currentChar:String = switch (charType)
|
||||
{
|
||||
case 0: charData.player;
|
||||
case 1: charData.opponent;
|
||||
default: throw 'Invalid charType: ' + charType;
|
||||
};
|
||||
var toolbox:CollapsibleDialog = cast haxe.ui.RuntimeComponentBuilder.fromAsset(Paths.ui('chart-editor/toolbox/iconselector'));
|
||||
|
||||
toolbox.x = FlxG.mouse.screenX - toolbox.width / 2;
|
||||
toolbox.y = FlxG.mouse.screenY - 16;
|
||||
|
||||
toolbox.title += " - " + switch (charType)
|
||||
{
|
||||
case 0: "Player";
|
||||
case 1: "Opponent";
|
||||
default: throw 'Invalid charType: ' + charType;
|
||||
};
|
||||
|
||||
var _overlay = new Component();
|
||||
|
||||
var hideCharIconPicker = function() {
|
||||
FlxTween.tween(_overlay, {opacity: 0}, 0.05,
|
||||
{
|
||||
ease: FlxEase.quartOut
|
||||
});
|
||||
FlxTween.tween(toolbox, {opacity: 0, y: toolbox.y + 10}, 0.1,
|
||||
{
|
||||
ease: FlxEase.quartOut,
|
||||
onComplete: function(_) {
|
||||
toolbox.hideDialog(haxe.ui.containers.dialogs.Dialog.DialogButton.CANCEL);
|
||||
Screen.instance.removeComponent(_overlay);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_overlay.id = "modal-background";
|
||||
_overlay.addClass("modal-background");
|
||||
_overlay.percentWidth = _overlay.percentHeight = 100;
|
||||
_overlay.opacity = 0;
|
||||
FlxTween.tween(_overlay, {opacity: 0.2}, 0.1, {ease: FlxEase.quartOut});
|
||||
_overlay.onClick = function(_) {
|
||||
hideCharIconPicker();
|
||||
};
|
||||
|
||||
Screen.instance.addComponent(_overlay);
|
||||
|
||||
toolbox.showDialog(false);
|
||||
toolbox.opacity = 0;
|
||||
FlxTween.tween(toolbox, {opacity: 1, y: toolbox.y + 10}, 0.1, {ease: FlxEase.quartOut});
|
||||
toolbox.closable = false;
|
||||
var scrollView:ScrollView = cast toolbox.findComponent('charSelectScroll');
|
||||
|
||||
var hbox = new Grid();
|
||||
hbox.columns = 5;
|
||||
hbox.width = 100;
|
||||
scrollView.addComponent(hbox);
|
||||
|
||||
var charIds:Array<String> = CharacterDataParser.listCharacterIds();
|
||||
|
||||
charIds.sort(function(a, b) {
|
||||
var result:Int = 0;
|
||||
|
||||
if (a < b)
|
||||
{
|
||||
result = -1;
|
||||
}
|
||||
else if (a > b)
|
||||
{
|
||||
result = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
for (ind => char in charIds)
|
||||
{
|
||||
var image = new haxe.ui.components.Button();
|
||||
image.width = 70;
|
||||
image.height = 70;
|
||||
image.iconPosition = "top";
|
||||
image.text = char;
|
||||
|
||||
if (char == currentChar)
|
||||
{
|
||||
scrollView.hscrollPos = Math.floor(ind / 5) * 80;
|
||||
image.selected = true;
|
||||
}
|
||||
|
||||
image.icon = CharacterDataParser.getCharPixelIconAsset(char);
|
||||
image.onClick = _ -> {
|
||||
healthIconsDirty = true;
|
||||
switch (charType)
|
||||
{
|
||||
case 0: currentSongMetadata.playData.characters.player = char;
|
||||
case 1: currentSongMetadata.playData.characters.opponent = char;
|
||||
default: throw 'Invalid charType: ' + charType;
|
||||
};
|
||||
hideCharIconPicker();
|
||||
|
||||
// var label = toolbox.findComponent('charIconName');
|
||||
// label.text = char;
|
||||
};
|
||||
|
||||
image.onMouseOver = _ -> {
|
||||
var label = toolbox.findComponent('charIconName');
|
||||
label.text = char;
|
||||
};
|
||||
hbox.addComponent(image);
|
||||
}
|
||||
}
|
||||
|
||||
function buildNotePreview():Void
|
||||
|
@ -2406,6 +2282,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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2446,13 +2337,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.
|
||||
|
@ -3509,6 +3400,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)
|
||||
|
@ -3528,11 +3421,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 && 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);
|
||||
|
@ -4220,6 +4113,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
targetCursorMode = Cell;
|
||||
}
|
||||
else if (overlapsHealthIcons)
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4250,7 +4147,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');
|
||||
|
@ -4297,7 +4194,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.
|
||||
|
@ -4333,7 +4230,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.
|
||||
|
@ -5042,7 +4939,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
Conductor.mapTimeChanges(this.currentSongMetadata.timeChanges);
|
||||
|
||||
refreshDifficultyTreeSelection();
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5051,7 +4948,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
selectedDifficulty = prevDifficulty;
|
||||
|
||||
refreshDifficultyTreeSelection();
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -5070,7 +4967,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
selectedDifficulty = nextDifficulty;
|
||||
|
||||
refreshDifficultyTreeSelection();
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5079,7 +4976,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
selectedDifficulty = nextDifficulty;
|
||||
|
||||
refreshDifficultyTreeSelection();
|
||||
refreshMetadataToolbox();
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5192,7 +5089,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');
|
||||
|
@ -5212,7 +5109,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');
|
||||
|
@ -5256,8 +5153,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':
|
||||
|
@ -5266,82 +5162,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,12 @@ class ChartEditorBaseDialog extends Dialog
|
|||
this.onDialogClosed = event -> onClose(event);
|
||||
}
|
||||
|
||||
private override function onReady():Void
|
||||
{
|
||||
_overlay.opacity = 0;
|
||||
fadeInDialogOverlay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the dialog is closed.
|
||||
* Override this to add custom behavior.
|
||||
|
@ -54,6 +64,31 @@ class ChartEditorBaseDialog extends Dialog
|
|||
|
||||
this.closable = params.closable ?? false;
|
||||
}
|
||||
|
||||
static final OVERLAY_EASE_DURATION:Float = 5.0;
|
||||
static final OVERLAY_EASE_TYPE:String = "linear";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
var builder = new AnimationBuilder(_overlay, OVERLAY_EASE_DURATION, "linear");
|
||||
builder.setPosition(0, "opacity", 0, true); // 0% absolute
|
||||
builder.setPosition(100, "opacity", 0.80, true);
|
||||
|
||||
trace('Fading in dialog overlay...');
|
||||
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})');
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -83,7 +84,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:
|
||||
|
@ -136,6 +138,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 {}
|
||||
|
@ -205,7 +223,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);
|
||||
|
||||
|
@ -214,7 +244,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>
|
||||
|
@ -505,180 +535,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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue