1
0
forked from 0ad/0ad

Make messageBox return a promise

Ofthen the `messageBox` is only used for confirmation like: "Do you
really want to overwrite the savegame?"
With this patch the code paths of "overwrite savegame" and "make new
savegame" can be combined.

In some cases there is nothing done when one button is pressed. That is
changed to an early return.

Comments by: @Stan
Differential Revision: https://code.wildfiregames.com/D5274
This was SVN commit r28191.
This commit is contained in:
phosit 2024-08-11 13:41:32 +00:00
parent a1bf7d5d23
commit d655455304
13 changed files with 209 additions and 259 deletions

View File

@ -1,24 +1,19 @@
async function messageBox(mbWidth, mbHeight, mbMessage, mbTitle, mbButtonCaptions, mbBtnCode,
mbCallbackArgs)
function messageBox(width, height, message, title, buttonCaptions)
{
const btnCode = await Engine.PushGuiPage(
return Engine.PushGuiPage(
"page_msgbox.xml",
{
"width": mbWidth,
"height": mbHeight,
"message": mbMessage,
"title": mbTitle,
"buttonCaptions": mbButtonCaptions
"width": width,
"height": height,
"message": message,
"title": title,
"buttonCaptions": buttonCaptions
});
if (mbBtnCode !== undefined && mbBtnCode[btnCode])
mbBtnCode[btnCode](mbCallbackArgs ? mbCallbackArgs[btnCode] : undefined);
}
async function timedConfirmation(width, height, message, timeParameter, timeout, title, buttonCaptions,
btnCode, callbackArgs)
function timedConfirmation(width, height, message, timeParameter, timeout, title, buttonCaptions)
{
const button = await Engine.PushGuiPage(
return Engine.PushGuiPage(
"page_timedconfirmation.xml",
{
"width": width,
@ -29,9 +24,6 @@ async function timedConfirmation(width, height, message, timeParameter, timeout,
"title": title,
"buttonCaptions": buttonCaptions
});
if (btnCode !== undefined && btnCode[button])
btnCode[button](callbackArgs ? callbackArgs[button] : undefined);
}
function openURL(url)

View File

@ -23,9 +23,6 @@ var g_RequestCancelled;
var g_RequestStartTime;
/**
* Returns true if ModIoAdvanceRequest should be called.
*/
var g_ModIOState = {
/**
* Finished status indicators
@ -34,96 +31,103 @@ var g_ModIOState = {
// GameID acquired, ready to fetch mod list
if (!g_RequestCancelled)
updateModList();
return true;
},
"listed": progressData => {
// List of available mods acquired
// Only run this once (for each update).
if (Engine.GetGUIObjectByName("modsAvailableList").list.length)
return true;
return;
hideDialog();
Engine.GetGUIObjectByName("refreshButton").enabled = true;
g_ModsAvailableOnline = Engine.ModIoGetMods();
displayMods();
return true;
},
"success": progressData => {
// Successfully acquired a mod file
hideDialog();
Engine.GetGUIObjectByName("downloadButton").enabled = true;
return true;
},
/**
* In-progress status indicators.
*/
"gameid": progressData => {
// Acquiring GameID from mod.io
return true;
},
"listing": progressData => {
// Acquiring list of available mods from mod.io
return true;
},
"downloading": progressData => {
// Downloading a mod file
updateProgressBar(progressData.progress, g_ModsAvailableOnline[selectedModIndex()].filesize);
return true;
},
/**
* Error/Failure status indicators.
*/
"failed_gameid": progressData => {
"failed_gameid": async(progressData) => {
// Game ID couldn't be retrieved
showErrorMessageBox(
const promise = showErrorMessageBox(
sprintf(translateWithContext("mod.io error message", "Game ID could not be retrieved.\n\n%(technicalDetails)s"), {
"technicalDetails": progressData.error
}),
translateWithContext("mod.io error message", "Initialization Error"),
[translate("Abort"), translate("Retry")],
[closePage, init]);
return false;
[translate("Abort"), translate("Retry")]);
if (!promise)
return;
if (await promise === 0)
closePage();
else
init();
},
"failed_listing": progressData => {
"failed_listing": async(progressData) => {
// Mod list couldn't be retrieved
showErrorMessageBox(
const promise = showErrorMessageBox(
sprintf(translateWithContext("mod.io error message", "Mod List could not be retrieved.\n\n%(technicalDetails)s"), {
"technicalDetails": progressData.error
}),
translateWithContext("mod.io error message", "Fetch Error"),
[translate("Abort"), translate("Retry")],
[cancelModListUpdate, updateModList]);
return false;
[translate("Abort"), translate("Retry")]);
if (!promise)
return;
if (await promise === 0)
cancelModListUpdate();
else
updateModList();
},
"failed_downloading": progressData => {
"failed_downloading": async(progressData) => {
// File couldn't be retrieved
showErrorMessageBox(
const promise = showErrorMessageBox(
sprintf(translateWithContext("mod.io error message", "File download failed.\n\n%(technicalDetails)s"), {
"technicalDetails": progressData.error
}),
translateWithContext("mod.io error message", "Download Error"),
[translate("Abort"), translate("Retry")],
[cancelRequest, downloadMod]);
return false;
[translate("Abort"), translate("Retry")]);
if (!promise)
return;
if (await promise === 0)
cancelRequest();
else
downloadMod();
},
"failed_filecheck": progressData => {
"failed_filecheck": async(progressData) => {
// The file is corrupted
showErrorMessageBox(
const promise = showErrorMessageBox(
sprintf(translateWithContext("mod.io error message", "File verification error.\n\n%(technicalDetails)s"), {
"technicalDetails": progressData.error
}),
translateWithContext("mod.io error message", "Verification Error"),
[translate("Abort")],
[cancelRequest]);
return false;
[translate("Abort")]);
if (!promise)
return;
await promise;
cancelRequest();
},
/**
* Default
*/
"none": progressData => {
// Nothing has happened yet.
return true;
}
};
@ -151,7 +155,8 @@ function onTick()
return;
}
if (handler(progressData))
handler(progressData);
if (!progressData.status.startsWith("failed"))
Engine.ModIoAdvanceRequest();
}
@ -290,13 +295,13 @@ function closePage()
Engine.PopGuiPage();
}
function showErrorMessageBox(caption, title, buttonCaptions, buttonActions)
function showErrorMessageBox(caption, title, buttonCaptions)
{
if (g_Failure)
return;
messageBox(500, 250, caption, title, buttonCaptions, buttonActions);
return null;
g_Failure = true;
return messageBox(500, 250, caption, title, buttonCaptions);
}
function progressDialog(dialogCaption, dialogTitle, showProgressBar, buttonCaption, buttonAction)

View File

@ -92,24 +92,23 @@ class LoadModal extends AutoWatcher
});
}
deleteSelectedRun()
async deleteSelectedRun()
{
if (this.selectedRun === -1)
return;
let run = this.currentRuns[this.selectedRun];
messageBox(
const buttonIndex = await messageBox(
400, 200,
sprintf(translate("Are you sure you want to delete run %s? This cannot be undone."), run.getLabel()),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, () => {
run.destroy();
this.currentRuns.splice(this.selectedRun, 1);
this.selectedRun = -1;
}]
);
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
return;
run.destroy();
this.currentRuns.splice(this.selectedRun, 1);
this.selectedRun = -1;
}
startSelectedRun()

View File

@ -149,27 +149,25 @@ class HotkeysPage
this.setupHotkeyList();
}
resetUserHotkeys()
async resetUserHotkeys()
{
messageBox(
const buttonIndex = await messageBox(
400, 200,
translate("Reset all hotkeys to default values?\nWARNING: this cannot be reversed."),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[
() => {},
() => {
for (let cat in this.categories)
this.categories[cat].hotkeys.forEach(([name, _]) => {
Engine.ConfigDB_RemoveValue("user", "hotkey." + name);
});
Engine.ConfigDB_SaveChanges("user");
Engine.ReloadHotkeys();
this.saveButton.enabled = false;
this.setupHotkeyData();
this.setupHotkeyList();
}
]);
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
return;
for (const cat in this.categories)
this.categories[cat].hotkeys.forEach(([name, _]) => {
Engine.ConfigDB_RemoveValue("user", "hotkey." + name);
});
Engine.ConfigDB_SaveChanges("user");
Engine.ReloadHotkeys();
this.saveButton.enabled = false;
this.setupHotkeyData();
this.setupHotkeyList();
}
saveUserHotkeys()

View File

@ -42,22 +42,20 @@ class SavegameDeleter
return tooltip;
}
deleteGame(gameID, label)
async deleteGame(gameID, label)
{
if (Engine.HotkeyIsPressed("session.savedgames.noconfirmation"))
this.reallyDeleteGame(gameID);
else
messageBox(
if (!Engine.HotkeyIsPressed("session.savedgames.noconfirmation"))
{
const buttonIndex = await messageBox(
500, 200,
sprintf(translate("\"%(label)s\""), { "label": label }) + "\n" +
translate("Saved game will be permanently deleted, are you sure?"),
translate("DELETE"),
[translate("No"), translate("Yes")],
[null, () => { this.reallyDeleteGame(gameID); }]);
}
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
return;
}
reallyDeleteGame(gameID)
{
if (!Engine.DeleteSavedGame(gameID))
error("Could not delete saved game: " + gameID);

View File

@ -18,7 +18,7 @@ class SavegameLoader
};
}
loadGame(gameId, metadata)
async loadGame(gameId, metadata)
{
// Check compatibility before really loading it
let engineInfo = Engine.GetEngineInfo();
@ -54,11 +54,12 @@ class SavegameLoader
message += translate("Do you still want to proceed?");
messageBox(
const buttonIndex = await messageBox(
500, 250,
message,
translate("Warning"),
[translate("No"), translate("Yes")],
[undefined, Engine.PopGuiPage.bind(Engine, gameId)]);
[translate("No"), translate("Yes")]);
if (buttonIndex === 1)
Engine.PopGuiPage(gameId);
}
}

View File

@ -34,36 +34,29 @@ class SavegameWriter
};
}
saveGame(gameID, label)
async saveGame(gameID, label)
{
let desc = this.saveGameDesc.caption;
let name = gameID || "savegame";
if (!gameID)
if (gameID)
{
this.reallySaveGame(name, desc, true);
return;
const buttonIndex = await messageBox(
500, 200,
sprintf(translate("\"%(label)s\""), { "label": label }) + "\n" +
translate("Saved game will be permanently overwritten, are you sure?"),
translate("OVERWRITE SAVE"),
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
return;
}
messageBox(
500, 200,
sprintf(translate("\"%(label)s\""), { "label": label }) + "\n" +
translate("Saved game will be permanently overwritten, are you sure?"),
translate("OVERWRITE SAVE"),
[translate("No"), translate("Yes")],
[null, () => { this.reallySaveGame(name, desc, false); }]);
}
reallySaveGame(name, desc, nameIsPrefix)
{
let simulationState = Engine.GuiInterfaceCall("GetSimulationState");
this.savedGameData.timeElapsed = simulationState.timeElapsed;
this.savedGameData.states = simulationState.players.map(pState => pState.state);
if (nameIsPrefix)
Engine.SaveGamePrefix(name, desc, this.savedGameData);
else
Engine.SaveGame(name, desc, this.savedGameData);
Engine[gameID ? "SaveGame" : "SaveGamePrefix"](name, desc, this.savedGameData);
Engine.PopGuiPage();
}

View File

@ -27,7 +27,7 @@ class JoinButton
/**
* Immediately rejoin and join game setups. Otherwise confirm late-observer join attempt.
*/
onPress()
async onPress()
{
let game = this.gameList.selectedGame();
if (!game)
@ -37,39 +37,36 @@ class JoinButton
let playername = rating ? g_Nickname + " (" + rating + ")" : g_Nickname;
if (!game.isCompatible)
messageBox(
{
const buttonIndex = await messageBox(
400, 200,
translate("Your active mods do not match the mods of this game.") + "\n\n" +
comparedModsString(game.mods, Engine.GetEngineInfo().mods) + "\n\n" +
translate("Do you want to switch to the mod selection page?"),
translate("Incompatible mods"),
[translate("No"), translate("Yes")],
[null, this.openModSelectionPage.bind(this)]
);
else if (game.stanza.state == "init" || game.players.some(player => player.Name == playername))
this.joinSelectedGame();
else
messageBox(
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
return;
Engine.StopXmppClient();
Engine.SwitchGuiPage("page_modmod.xml", { "cancelbutton": true });
return;
}
const stanza = game.stanza;
if (stanza.state !== "init" && game.players.every(player => player.Name !== playername))
{
const buttonIndex = await messageBox(
400, 200,
translate("The game has already started. Do you want to join as observer?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, this.joinSelectedGame.bind(this)]);
}
[translate("No"), translate("Yes")]);
/**
* Attempt to join the selected game without asking for confirmation.
*/
joinSelectedGame()
{
if (this.joinButton.hidden)
return;
if (buttonIndex === 0)
return;
}
let game = this.gameList.selectedGame();
if (!game)
return;
let stanza = game.stanza;
Engine.PushGuiPage("page_gamesetup_mp.xml", {
"multiplayerGameType": "join",
"name": g_Nickname,
@ -79,14 +76,6 @@ class JoinButton
});
}
openModSelectionPage()
{
Engine.StopXmppClient();
Engine.SwitchGuiPage("page_modmod.xml", {
"cancelbutton": true
});
}
/**
* Rejoin games with the original playername, even if the rating changed meanwhile.
*/

View File

@ -45,7 +45,7 @@ class ConnectionHandler
banned ? translate("BANNED") : translate("KICKED"));
}
askReconnect()
async askReconnect()
{
if (this.kicked)
return;
@ -56,19 +56,14 @@ class ConnectionHandler
this.askingReconnect = true;
messageBox(
const buttonIndex = await messageBox(
400, 200,
translate("You have been disconnected from the lobby. Do you want to reconnect?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[
() => {
this.askingReconnect = false;
},
() => {
this.askingReconnect = false;
Engine.ConnectXmppClient();
}
]);
[translate("No"), translate("Yes")]);
this.askingReconnect = false;
if (buttonIndex === 1)
Engine.ConnectXmppClient();
}
}

View File

@ -173,18 +173,19 @@ var g_OptionType = {
control.tooltip = item && item.tooltip || option.tooltip;
};
},
"timeout": (option, oldValue, hasChanges, newValue) => {
"timeout": async(option, oldValue, hasChanges, newValue) => {
if (!option.timeout)
return;
timedConfirmation(
const buttonIndex = await timedConfirmation(
500, 200,
translate("Changes will be reverted in %(time)s seconds. Do you want to keep changes?"),
"time",
option.timeout,
translate("Warning"),
[translate("No"), translate("Yes")],
[() => {this.revertChange(option, +oldValue, hasChanges);}, null]
);
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
this.revertChange(option, +oldValue, hasChanges);
}
},
"slider":
@ -354,19 +355,17 @@ function enableButtons()
Engine.GetGUIObjectByName("saveChanges").enabled = hasChanges;
}
function setDefaults()
async function setDefaults()
{
messageBox(
const buttonIndex = messageBox(
500, 200,
translate("Resetting the options will erase your saved settings. Do you want to continue?"),
translate("Warning"),
[translate("No"), translate("Yes")],
[null, reallySetDefaults]
);
}
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
return;
function reallySetDefaults()
{
for (let category in g_Options)
for (let option of g_Options[category].options)
{
@ -405,37 +404,33 @@ function revertChanges()
displayOptions();
}
function saveChanges()
async function saveChanges()
{
for (let category in g_Options)
for (let i = 0; i < g_Options[category].options.length; ++i)
{
let option = g_Options[category].options[i];
const category = Object.keys(g_Options).find(key => {
return g_Options[key].options.some(option => {
let optionType = g_OptionType[option.type];
if (!optionType.sanitizeValue)
continue;
return false;
let value = optionType.configToValue(Engine.ConfigDB_GetValue("user", option.config));
if (value == optionType.sanitizeValue(value, undefined, option))
continue;
return value != optionType.sanitizeValue(value, undefined, option);
});
});
selectPanel(category);
if (category)
{
selectPanel(category);
messageBox(
500, 200,
translate("Some setting values are invalid! Are you sure you want to save them?"),
translate("Warning"),
[translate("No"), translate("Yes")],
[null, reallySaveChanges]
);
const buttonIndex = await messageBox(
500, 200,
translate("Some setting values are invalid! Are you sure you want to save them?"),
translate("Warning"),
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
return;
}
}
reallySaveChanges();
}
function reallySaveChanges()
{
Engine.ConfigDB_SaveChanges("user");
enableButtons();
}
@ -443,20 +438,18 @@ function reallySaveChanges()
/**
* Close GUI page and inform the parent GUI page which options changed.
**/
function closePage()
async function closePage()
{
if (Engine.ConfigDB_HasChanges("user"))
messageBox(
{
const buttonIndex = await messageBox(
500, 200,
translate("You have unsaved changes, do you want to close this window?"),
translate("Warning"),
[translate("No"), translate("Yes")],
[null, closePageWithoutConfirmation]);
else
closePageWithoutConfirmation();
}
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
return;
}
function closePageWithoutConfirmation()
{
Engine.PopGuiPage(g_ChangedKeys);
}

View File

@ -259,19 +259,24 @@ var g_MainMenuItems = [
{
"caption": translate("Scenario Editor"),
"tooltip": translate('Open the Atlas Scenario Editor in a new window. You can run this more reliably by starting the game with the command-line argument "-editor".'),
"onPress": () => {
if (Engine.AtlasIsAvailable())
messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D. and open the Scenario Editor?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, Engine.RestartInAtlas]);
else
"onPress": async() => {
if (!Engine.AtlasIsAvailable())
{
messageBox(
400, 200,
translate("The scenario editor is not available or failed to load. See the game logs for additional information."),
translate("Error"));
return;
}
const buttonIndex = await messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D. and open the Scenario Editor?"),
translate("Confirmation"),
[translate("No"), translate("Yes")]);
if (buttonIndex === 1)
Engine.RestartInAtlas();
}
},
{
@ -284,13 +289,15 @@ var g_MainMenuItems = [
{
"caption": translate("Exit"),
"tooltip": translate("Exit the game."),
"onPress": () => {
messageBox(
"onPress": async() => {
const buttonIndex = await messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D.?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, Engine.Exit]);
[translate("No"), translate("Yes")]);
if (buttonIndex === 1)
Engine.Exit();
}
}
];

View File

@ -44,22 +44,26 @@ function initRememberPassword()
Engine.ConfigDB_GetValue("user", "lobby.rememberpassword") == "true";
}
function toggleRememberPassword()
async function toggleRememberPassword()
{
let checkbox = Engine.GetGUIObjectByName("rememberPassword");
let enabled = Engine.ConfigDB_GetValue("user", "lobby.rememberpassword") == "true";
if (!checkbox.checked && enabled && Engine.ConfigDB_GetValue("user", "lobby.password"))
messageBox(
{
const buttonIndex = await messageBox(
360, 160,
translate("Are you sure you want to delete the password after connecting?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[
() => { checkbox.checked = true; },
() => { Engine.ConfigDB_CreateAndSaveValue("user", "lobby.rememberpassword", String(!enabled)); }
]);
else
Engine.ConfigDB_CreateAndSaveValue("user", "lobby.rememberpassword", String(!enabled));
[translate("No"), translate("Yes")]);
if (buttonIndex === 0)
{
checkbox.checked = true;
return;
}
}
Engine.ConfigDB_CreateAndSaveValue("user", "lobby.rememberpassword", String(!enabled));
}
function getEncryptedPassword()

View File

@ -148,55 +148,31 @@ function reloadCache()
/**
* Callback.
*/
function deleteReplayButtonPressed()
async function deleteReplayButtonPressed()
{
if (!Engine.GetGUIObjectByName("deleteReplayButton").enabled)
return;
if (Engine.HotkeyIsPressed("session.savedgames.noconfirmation"))
deleteReplayWithoutConfirmation();
else
deleteReplay();
}
/**
* Shows a confirmation dialog and deletes the selected replay from the disk in case.
*/
function deleteReplay()
{
// Get selected replay
var selected = Engine.GetGUIObjectByName("replaySelection").selected;
if (selected == -1)
return;
var replay = g_ReplaysFiltered[selected];
const replayDirectory = g_ReplaysFiltered[selected].directory;
messageBox(
500, 200,
translate("Are you sure you want to delete this replay permanently?") + "\n" +
escapeText(Engine.GetReplayDirectoryName(replay.directory)),
translate("Delete replay"),
[translate("No"), translate("Yes")],
[null, function() { reallyDeleteReplay(replay.directory); }]
);
}
if (!Engine.HotkeyIsPressed("session.savedgames.noconfirmation"))
{
const buttonIndex = await messageBox(
500, 200,
translate("Are you sure you want to delete this replay permanently?") + "\n" +
escapeText(Engine.GetReplayDirectoryName(replayDirectory)),
translate("Delete replay"),
[translate("No"), translate("Yes")]);
/**
* Attempts to delete the selected replay from the disk.
*/
function deleteReplayWithoutConfirmation()
{
var selected = Engine.GetGUIObjectByName("replaySelection").selected;
if (selected > -1)
reallyDeleteReplay(g_ReplaysFiltered[selected].directory);
}
if (buttonIndex === 0)
return;
}
/**
* Attempts to delete the given replay directory from the disk.
*
* @param replayDirectory {string}
*/
function reallyDeleteReplay(replayDirectory)
{
var replaySelection = Engine.GetGUIObjectByName("replaySelection");
var selectedIndex = replaySelection.selected;