mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-11-10 16:55:17 +00:00
584 lines
20 KiB
Haxe
584 lines
20 KiB
Haxe
package funkin.ui.debug.charting;
|
|
|
|
import flixel.util.FlxTimer;
|
|
import funkin.input.Cursor;
|
|
import funkin.play.character.BaseCharacter;
|
|
import funkin.play.character.CharacterData.CharacterDataParser;
|
|
import funkin.play.song.Song;
|
|
import funkin.play.song.SongData.SongDataParser;
|
|
import funkin.play.song.SongData.SongPlayableChar;
|
|
import funkin.play.song.SongData.SongTimeChange;
|
|
import haxe.io.Path;
|
|
import haxe.ui.components.Button;
|
|
import haxe.ui.components.DropDown;
|
|
import haxe.ui.components.Label;
|
|
import haxe.ui.components.Link;
|
|
import haxe.ui.components.NumberStepper;
|
|
import haxe.ui.components.TextField;
|
|
import haxe.ui.containers.Box;
|
|
import haxe.ui.containers.dialogs.Dialog;
|
|
import haxe.ui.containers.dialogs.Dialogs;
|
|
import haxe.ui.containers.properties.PropertyGrid;
|
|
import haxe.ui.containers.properties.PropertyGroup;
|
|
import haxe.ui.containers.VBox;
|
|
import haxe.ui.core.Component;
|
|
import haxe.ui.events.UIEvent;
|
|
import haxe.ui.notifications.NotificationManager;
|
|
import haxe.ui.notifications.NotificationType;
|
|
|
|
using Lambda;
|
|
|
|
/**
|
|
* Handles dialogs for the new Chart Editor.
|
|
*/
|
|
class ChartEditorDialogHandler
|
|
{
|
|
static final CHART_EDITOR_DIALOG_ABOUT_LAYOUT:String = Paths.ui('chart-editor/dialogs/about');
|
|
static final CHART_EDITOR_DIALOG_WELCOME_LAYOUT:String = Paths.ui('chart-editor/dialogs/welcome');
|
|
static final CHART_EDITOR_DIALOG_UPLOAD_INST_LAYOUT:String = Paths.ui('chart-editor/dialogs/upload-inst');
|
|
static final CHART_EDITOR_DIALOG_SONG_METADATA_LAYOUT:String = Paths.ui('chart-editor/dialogs/song-metadata');
|
|
static final CHART_EDITOR_DIALOG_SONG_METADATA_CHARGROUP_LAYOUT:String = Paths.ui('chart-editor/dialogs/song-metadata-chargroup');
|
|
static final CHART_EDITOR_DIALOG_UPLOAD_VOCALS_LAYOUT:String = Paths.ui('chart-editor/dialogs/upload-vocals');
|
|
static final CHART_EDITOR_DIALOG_UPLOAD_VOCALS_ENTRY_LAYOUT:String = Paths.ui('chart-editor/dialogs/upload-vocals-entry');
|
|
static final CHART_EDITOR_DIALOG_USER_GUIDE_LAYOUT:String = Paths.ui('chart-editor/dialogs/user-guide');
|
|
|
|
/**
|
|
* Builds and opens a dialog giving brief credits for the chart editor.
|
|
* @param state The current chart editor state.
|
|
* @return The dialog that was opened.
|
|
*/
|
|
public static inline function openAboutDialog(state:ChartEditorState):Dialog
|
|
{
|
|
return openDialog(state, CHART_EDITOR_DIALOG_ABOUT_LAYOUT, true, true);
|
|
}
|
|
|
|
/**
|
|
* Builds and opens a dialog letting the user create a new chart, open a recent chart, or load from a template.
|
|
* @param state The current chart editor state.
|
|
* @param closable Whether the dialog can be closed by the user.
|
|
* @return The dialog that was opened.
|
|
*/
|
|
public static function openWelcomeDialog(state:ChartEditorState, closable:Bool = true):Dialog
|
|
{
|
|
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_WELCOME_LAYOUT, true, closable);
|
|
|
|
// Add handlers to the "Create From Song" section.
|
|
var linkCreateBasic:Link = dialog.findComponent('splashCreateFromSongBasic', Link);
|
|
linkCreateBasic.onClick = function(_event) {
|
|
// Hide the welcome dialog
|
|
dialog.hideDialog(DialogButton.CANCEL);
|
|
|
|
//
|
|
// Create Song Wizard
|
|
//
|
|
|
|
// Step 1. Upload Instrumental
|
|
var uploadInstDialog:Dialog = openUploadInstDialog(state, false);
|
|
uploadInstDialog.onDialogClosed = function(_event) {
|
|
state.isHaxeUIDialogOpen = false;
|
|
if (_event.button == DialogButton.APPLY)
|
|
{
|
|
// Step 2. Song Metadata
|
|
var songMetadataDialog:Dialog = openSongMetadataDialog(state);
|
|
songMetadataDialog.onDialogClosed = function(_event) {
|
|
state.isHaxeUIDialogOpen = false;
|
|
if (_event.button == DialogButton.APPLY)
|
|
{
|
|
// Step 3. Upload Vocals
|
|
// NOTE: Uploading vocals is optional, so we don't need to check if the user cancelled the wizard.
|
|
openUploadVocalsDialog(state, false); // var uploadVocalsDialog:Dialog
|
|
}
|
|
else
|
|
{
|
|
// User cancelled the wizard! Back to the welcome dialog.
|
|
openWelcomeDialog(state);
|
|
}
|
|
};
|
|
}
|
|
else
|
|
{
|
|
// User cancelled the wizard! Back to the welcome dialog.
|
|
openWelcomeDialog(state);
|
|
}
|
|
};
|
|
}
|
|
|
|
var splashTemplateContainer:VBox = dialog.findComponent('splashTemplateContainer', VBox);
|
|
|
|
var songList:Array<String> = SongDataParser.listSongIds();
|
|
|
|
for (targetSongId in songList)
|
|
{
|
|
var songData:Song = SongDataParser.fetchSong(targetSongId);
|
|
|
|
if (songData == null) continue;
|
|
|
|
var songName:String = songData.getDifficulty().songName;
|
|
|
|
var linkTemplateSong:Link = new Link();
|
|
linkTemplateSong.text = songName;
|
|
linkTemplateSong.onClick = function(_event) {
|
|
dialog.hideDialog(DialogButton.CANCEL);
|
|
|
|
// Load song from template
|
|
state.loadSongAsTemplate(targetSongId);
|
|
}
|
|
|
|
splashTemplateContainer.addComponent(linkTemplateSong);
|
|
}
|
|
|
|
return dialog;
|
|
}
|
|
|
|
/**
|
|
* Builds and opens a dialog where the user uploads an instrumental for the current song.
|
|
* @param state The current chart editor state.
|
|
* @param closable Whether the dialog can be closed by the user.
|
|
* @return The dialog that was opened.
|
|
*/
|
|
public static function openUploadInstDialog(state:ChartEditorState, ?closable:Bool = true):Dialog
|
|
{
|
|
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_UPLOAD_INST_LAYOUT, true, closable);
|
|
|
|
var buttonCancel:Button = dialog.findComponent('dialogCancel', Button);
|
|
|
|
buttonCancel.onClick = function(_event) {
|
|
dialog.hideDialog(DialogButton.CANCEL);
|
|
}
|
|
|
|
var instrumentalBox:Box = dialog.findComponent('instrumentalBox', Box);
|
|
|
|
instrumentalBox.onMouseOver = function(_event) {
|
|
instrumentalBox.swapClass('upload-bg', 'upload-bg-hover');
|
|
Cursor.cursorMode = Pointer;
|
|
}
|
|
|
|
instrumentalBox.onMouseOut = function(_event) {
|
|
instrumentalBox.swapClass('upload-bg-hover', 'upload-bg');
|
|
Cursor.cursorMode = Default;
|
|
}
|
|
|
|
var onDropFile:String->Void;
|
|
|
|
instrumentalBox.onClick = function(_event) {
|
|
Dialogs.openBinaryFile('Open Instrumental', [
|
|
{label: 'Audio File (.ogg)', extension: 'ogg'}], function(selectedFile:SelectedFileInfo) {
|
|
if (selectedFile != null)
|
|
{
|
|
if (state.loadInstrumentalFromBytes(selectedFile.bytes))
|
|
{
|
|
trace('Selected file: ' + selectedFile.fullPath);
|
|
NotificationManager.instance.addNotification(
|
|
{
|
|
title: 'Success',
|
|
body: 'Loaded instrumental track (${selectedFile.name})',
|
|
type: NotificationType.Success,
|
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
|
});
|
|
|
|
dialog.hideDialog(DialogButton.APPLY);
|
|
removeDropHandler(onDropFile);
|
|
}
|
|
else
|
|
{
|
|
trace('Failed to load instrumental (${selectedFile.fullPath})');
|
|
|
|
NotificationManager.instance.addNotification(
|
|
{
|
|
title: 'Failure',
|
|
body: 'Failed to load instrumental track (${selectedFile.name})',
|
|
type: NotificationType.Error,
|
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
onDropFile = function(pathStr:String) {
|
|
var path:Path = new Path(pathStr);
|
|
trace('Dropped file (${path})');
|
|
if (state.loadInstrumentalFromPath(path))
|
|
{
|
|
// Tell the user the load was successful.
|
|
NotificationManager.instance.addNotification(
|
|
{
|
|
title: 'Success',
|
|
body: 'Loaded instrumental track (${path.file}.${path.ext})',
|
|
type: NotificationType.Success,
|
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
|
});
|
|
|
|
dialog.hideDialog(DialogButton.APPLY);
|
|
removeDropHandler(onDropFile);
|
|
}
|
|
else
|
|
{
|
|
// Tell the user the load was successful.
|
|
NotificationManager.instance.addNotification(
|
|
{
|
|
title: 'Failure',
|
|
body: 'Failed to load instrumental track (${path.file}.${path.ext})',
|
|
type: NotificationType.Error,
|
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
|
});
|
|
}
|
|
};
|
|
|
|
addDropHandler(instrumentalBox, onDropFile);
|
|
|
|
return dialog;
|
|
}
|
|
|
|
static var dropHandlers:Array<
|
|
{
|
|
component:Component,
|
|
handler:(String->Void)
|
|
}> = [];
|
|
|
|
static function addDropHandler(component:Component, handler:String->Void):Void
|
|
{
|
|
#if desktop
|
|
if (!FlxG.stage.window.onDropFile.has(onDropFile)) FlxG.stage.window.onDropFile.add(onDropFile);
|
|
|
|
dropHandlers.push(
|
|
{
|
|
component: component,
|
|
handler: handler
|
|
});
|
|
#else
|
|
trace('addDropHandler not implemented for this platform');
|
|
#end
|
|
}
|
|
|
|
static function removeDropHandler(handler:String->Void):Void
|
|
{
|
|
#if desktop
|
|
FlxG.stage.window.onDropFile.remove(handler);
|
|
#end
|
|
}
|
|
|
|
static function clearDropHandlers():Void
|
|
{
|
|
#if desktop
|
|
dropHandlers = [];
|
|
FlxG.stage.window.onDropFile.remove(onDropFile);
|
|
#end
|
|
}
|
|
|
|
static function onDropFile(path:String):Void
|
|
{
|
|
// a VERY short timer to wait for the mouse position to update
|
|
new FlxTimer().start(0.01, function(_) {
|
|
for (handler in dropHandlers)
|
|
{
|
|
if (handler.component.hitTest(FlxG.mouse.screenX, FlxG.mouse.screenY))
|
|
{
|
|
handler.handler(path);
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Opens the dialog in the wizard where the user can set song metadata like name and artist and BPM.
|
|
* @param state The ChartEditorState instance.
|
|
* @return The dialog to open.
|
|
*/
|
|
public static function openSongMetadataDialog(state:ChartEditorState):Dialog
|
|
{
|
|
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_SONG_METADATA_LAYOUT, true, false);
|
|
|
|
var buttonCancel:Button = dialog.findComponent('dialogCancel', Button);
|
|
|
|
buttonCancel.onClick = function(_event) {
|
|
dialog.hideDialog(DialogButton.CANCEL);
|
|
}
|
|
|
|
var dialogSongName:TextField = dialog.findComponent('dialogSongName', TextField);
|
|
dialogSongName.onChange = function(event:UIEvent) {
|
|
var valid:Bool = event.target.text != null && event.target.text != '';
|
|
|
|
if (valid)
|
|
{
|
|
dialogSongName.removeClass('invalid-value');
|
|
state.currentSongMetadata.songName = event.target.text;
|
|
}
|
|
else
|
|
{
|
|
state.currentSongMetadata.songName = null;
|
|
}
|
|
};
|
|
state.currentSongMetadata.songName = null;
|
|
|
|
var dialogSongArtist:TextField = dialog.findComponent('dialogSongArtist', TextField);
|
|
dialogSongArtist.onChange = function(event:UIEvent) {
|
|
var valid:Bool = event.target.text != null && event.target.text != '';
|
|
|
|
if (valid)
|
|
{
|
|
dialogSongArtist.removeClass('invalid-value');
|
|
state.currentSongMetadata.artist = event.target.text;
|
|
}
|
|
else
|
|
{
|
|
state.currentSongMetadata.artist = null;
|
|
}
|
|
};
|
|
state.currentSongMetadata.artist = null;
|
|
|
|
var dialogStage:DropDown = dialog.findComponent('dialogStage', DropDown);
|
|
dialogStage.onChange = function(event:UIEvent) {
|
|
if (event.data == null && event.data.id == null) return;
|
|
state.currentSongMetadata.playData.stage = event.data.id;
|
|
};
|
|
state.currentSongMetadata.playData.stage = null;
|
|
|
|
var dialogNoteSkin:DropDown = dialog.findComponent('dialogNoteSkin', DropDown);
|
|
dialogNoteSkin.onChange = function(event:UIEvent) {
|
|
if (event.data.id == null) return;
|
|
state.currentSongMetadata.playData.noteSkin = event.data.id;
|
|
};
|
|
state.currentSongMetadata.playData.noteSkin = null;
|
|
|
|
var dialogBPM:NumberStepper = dialog.findComponent('dialogBPM', NumberStepper);
|
|
dialogBPM.onChange = function(event:UIEvent) {
|
|
if (event.value == null || event.value <= 0) return;
|
|
|
|
var timeChanges:Array<SongTimeChange> = state.currentSongMetadata.timeChanges;
|
|
if (timeChanges == null || timeChanges.length == 0)
|
|
{
|
|
timeChanges = [new SongTimeChange(-1, 0, event.value, 4, 4, [4, 4, 4, 4])];
|
|
}
|
|
else
|
|
{
|
|
timeChanges[0].bpm = event.value;
|
|
}
|
|
|
|
Conductor.forceBPM(event.value);
|
|
|
|
state.currentSongMetadata.timeChanges = timeChanges;
|
|
};
|
|
|
|
var dialogCharGrid:PropertyGrid = dialog.findComponent('dialogCharGrid', PropertyGrid);
|
|
var dialogCharAdd:Button = dialog.findComponent('dialogCharAdd', Button);
|
|
dialogCharAdd.onClick = function(event:UIEvent) {
|
|
var charGroup:PropertyGroup;
|
|
charGroup = buildCharGroup(state, null, () -> dialogCharGrid.removeComponent(charGroup));
|
|
dialogCharGrid.addComponent(charGroup);
|
|
};
|
|
|
|
// Empty the character list.
|
|
state.currentSongMetadata.playData.playableChars = {};
|
|
// Add at least one character group with no Remove button.
|
|
dialogCharGrid.addComponent(buildCharGroup(state, 'bf', null));
|
|
|
|
var dialogContinue:Button = dialog.findComponent('dialogContinue', Button);
|
|
dialogContinue.onClick = (_event) -> dialog.hideDialog(DialogButton.APPLY);
|
|
|
|
return dialog;
|
|
}
|
|
|
|
static function buildCharGroup(state:ChartEditorState, key:String = null, removeFunc:Void->Void):PropertyGroup
|
|
{
|
|
var groupKey:String = key;
|
|
|
|
var getCharData:Void->SongPlayableChar = function() {
|
|
if (groupKey == null) groupKey = 'newChar${state.currentSongMetadata.playData.playableChars.keys().count()}';
|
|
|
|
var result = state.currentSongMetadata.playData.playableChars.get(groupKey);
|
|
if (result == null)
|
|
{
|
|
result = new SongPlayableChar('', 'dad');
|
|
state.currentSongMetadata.playData.playableChars.set(groupKey, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
var moveCharGroup:String->Void = function(target:String) {
|
|
var charData = getCharData();
|
|
state.currentSongMetadata.playData.playableChars.remove(groupKey);
|
|
state.currentSongMetadata.playData.playableChars.set(target, charData);
|
|
groupKey = target;
|
|
}
|
|
|
|
var removeGroup:Void->Void = function() {
|
|
state.currentSongMetadata.playData.playableChars.remove(groupKey);
|
|
removeFunc();
|
|
}
|
|
|
|
var charData:SongPlayableChar = getCharData();
|
|
|
|
var charGroup:PropertyGroup = cast state.buildComponent(CHART_EDITOR_DIALOG_SONG_METADATA_CHARGROUP_LAYOUT);
|
|
|
|
var charGroupPlayer:DropDown = charGroup.findComponent('charGroupPlayer', DropDown);
|
|
charGroupPlayer.onChange = function(event:UIEvent) {
|
|
charGroup.text = event.data.text;
|
|
moveCharGroup(event.data.id);
|
|
};
|
|
|
|
if (key == null)
|
|
{
|
|
// Find the next available player character.
|
|
trace(charGroupPlayer.dataSource.data);
|
|
}
|
|
|
|
var charGroupOpponent:DropDown = charGroup.findComponent('charGroupOpponent', DropDown);
|
|
charGroupOpponent.onChange = function(event:UIEvent) {
|
|
charData.opponent = event.data.id;
|
|
};
|
|
charGroupOpponent.value = getCharData().opponent;
|
|
|
|
var charGroupGirlfriend:DropDown = charGroup.findComponent('charGroupGirlfriend', DropDown);
|
|
charGroupGirlfriend.onChange = function(event:UIEvent) {
|
|
charData.girlfriend = event.data.id;
|
|
};
|
|
charGroupGirlfriend.value = getCharData().girlfriend;
|
|
|
|
var charGroupRemove:Button = charGroup.findComponent('charGroupRemove', Button);
|
|
charGroupRemove.onClick = function(event:UIEvent) {
|
|
removeGroup();
|
|
};
|
|
|
|
if (removeFunc == null) charGroupRemove.hidden = true;
|
|
|
|
return charGroup;
|
|
}
|
|
|
|
/**
|
|
* Builds and opens a dialog where the user uploads vocals for the current song.
|
|
* @param state The current chart editor state.
|
|
* @param closable Whether the dialog can be closed by the user.
|
|
* @return The dialog that was opened.
|
|
*/
|
|
public static function openUploadVocalsDialog(state:ChartEditorState, ?closable:Bool = true):Dialog
|
|
{
|
|
var charIdsForVocals:Array<String> = [];
|
|
|
|
for (charKey in state.currentSongMetadata.playData.playableChars.keys())
|
|
{
|
|
var charData:SongPlayableChar = state.currentSongMetadata.playData.playableChars.get(charKey);
|
|
charIdsForVocals.push(charKey);
|
|
if (charData.opponent != null) charIdsForVocals.push(charData.opponent);
|
|
}
|
|
|
|
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_UPLOAD_VOCALS_LAYOUT, true, closable);
|
|
|
|
var dialogContainer:Component = dialog.findComponent('vocalContainer');
|
|
|
|
var buttonCancel:Button = dialog.findComponent('dialogCancel', Button);
|
|
buttonCancel.onClick = function(_event) {
|
|
dialog.hideDialog(DialogButton.CANCEL);
|
|
}
|
|
|
|
var dialogNoVocals:Button = dialog.findComponent('dialogNoVocals', Button);
|
|
dialogNoVocals.onClick = function(_event) {
|
|
// Dismiss
|
|
dialog.hideDialog(DialogButton.APPLY);
|
|
};
|
|
|
|
for (charKey in charIdsForVocals)
|
|
{
|
|
trace('Adding vocal upload for character ${charKey}');
|
|
var charMetadata:BaseCharacter = CharacterDataParser.fetchCharacter(charKey);
|
|
var charName:String = charMetadata.characterName;
|
|
|
|
var vocalsEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_UPLOAD_VOCALS_ENTRY_LAYOUT);
|
|
|
|
var vocalsEntryLabel:Label = vocalsEntry.findComponent('vocalsEntryLabel', Label);
|
|
vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.';
|
|
|
|
var onDropFile:String->Void = function(pathStr:String) {
|
|
trace('Selected file: $pathStr');
|
|
var path:Path = new Path(pathStr);
|
|
|
|
if (state.loadVocalsFromPath(path, charKey))
|
|
{
|
|
// Tell the user the load was successful.
|
|
NotificationManager.instance.addNotification(
|
|
{
|
|
title: 'Success',
|
|
body: 'Loaded vocal track for $charName (${path.file}.${path.ext})',
|
|
type: NotificationType.Success,
|
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
|
});
|
|
vocalsEntryLabel.text = 'Vocals for $charName (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}';
|
|
dialogNoVocals.hidden = true;
|
|
removeDropHandler(onDropFile);
|
|
}
|
|
else
|
|
{
|
|
// Vocals failed to load.
|
|
NotificationManager.instance.addNotification(
|
|
{
|
|
title: 'Failure',
|
|
body: 'Failed to load vocal track (${path.file}.${path.ext})',
|
|
type: NotificationType.Error,
|
|
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
|
});
|
|
|
|
vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.';
|
|
}
|
|
};
|
|
|
|
vocalsEntry.onClick = function(_event) {
|
|
Dialogs.openBinaryFile('Open $charName Vocals', [
|
|
{label: 'Audio File (.ogg)', extension: 'ogg'}], function(selectedFile) {
|
|
if (selectedFile != null)
|
|
{
|
|
trace('Selected file: ' + selectedFile.name);
|
|
vocalsEntryLabel.text = 'Vocals for $charName (click to browse)\n${selectedFile.name}';
|
|
state.loadVocalsFromBytes(selectedFile.bytes, charKey);
|
|
dialogNoVocals.hidden = true;
|
|
removeDropHandler(onDropFile);
|
|
}
|
|
});
|
|
}
|
|
|
|
// onDropFile
|
|
addDropHandler(vocalsEntry, onDropFile);
|
|
dialogContainer.addComponent(vocalsEntry);
|
|
}
|
|
|
|
var dialogContinue:Button = dialog.findComponent('dialogContinue', Button);
|
|
dialogContinue.onClick = function(_event) {
|
|
// Dismiss
|
|
dialog.hideDialog(DialogButton.APPLY);
|
|
};
|
|
|
|
return dialog;
|
|
}
|
|
|
|
/**
|
|
* Builds and opens a dialog displaying the user guide, providing guidance and help on how to use the chart editor.
|
|
*
|
|
* @param state The current chart editor state.
|
|
* @return The dialog that was opened.
|
|
*/
|
|
public static inline function openUserGuideDialog(state:ChartEditorState):Dialog
|
|
{
|
|
return openDialog(state, CHART_EDITOR_DIALOG_USER_GUIDE_LAYOUT, true, true);
|
|
}
|
|
|
|
/**
|
|
* Builds and opens a dialog from a given layout path.
|
|
* @param modal Makes the background uninteractable while the dialog is open.
|
|
* @param closable Hides the close button on the dialog, preventing it from being closed unless the user interacts with the dialog.
|
|
*/
|
|
static function openDialog(state:ChartEditorState, key:String, modal:Bool = true, closable:Bool = true):Dialog
|
|
{
|
|
var dialog:Dialog = cast state.buildComponent(key);
|
|
dialog.destroyOnClose = true;
|
|
dialog.closable = closable;
|
|
dialog.showDialog(modal);
|
|
|
|
state.isHaxeUIDialogOpen = true;
|
|
dialog.onDialogClosed = function(event:UIEvent) {
|
|
state.isHaxeUIDialogOpen = false;
|
|
};
|
|
|
|
return dialog;
|
|
}
|
|
}
|