diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg index 4f8b3aa645..590c5b549c 100644 --- a/binaries/data/config/default.cfg +++ b/binaries/data/config/default.cfg @@ -232,6 +232,12 @@ summary = "Ctrl+Tab" ; Toggle in-game summary lobby = "Alt+L" ; Show the multiplayer lobby in a dialog window. structree = "Alt+Shift+T" ; Show structure tree civinfo = "Alt+Shift+H" ; Show civilization info +catafalque = "Alt+Shift+F" ; Show catafalque overview +mapbrowser = "Alt+Shift+M" ; Show map browser +manual = "Alt+Shift+U" ; Show manual +tips = "Alt+Shift+I" ; Show tips and tricks +options = "Alt+Shift+O" ; Show options +hotkeys = "Alt+Shift+Y" ; Show hotkeys ; > CLIPBOARD CONTROLS copy = "Ctrl+C" ; Copy to clipboard @@ -388,9 +394,6 @@ defense_tower = "Space+W" market = "Space+M" dock = "Space+D" -[hotkey.gamesetup] -mapbrowser.open = "M" - [hotkey.session] kill = Delete, Backspace ; Destroy selected units stop = "H" ; Stop the current action diff --git a/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js b/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js index 7a2495dcc8..43e7318a24 100644 --- a/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js +++ b/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js @@ -247,6 +247,7 @@ var g_CampaignMenu; async function init(initData) { + registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]); let run = initData?.filename || CampaignRun.getCurrentRunFilename(); try { diff --git a/binaries/data/mods/public/gui/campaigns/load_modal/LoadModal.js b/binaries/data/mods/public/gui/campaigns/load_modal/LoadModal.js index c0bd9395a7..b17b8a7f69 100644 --- a/binaries/data/mods/public/gui/campaigns/load_modal/LoadModal.js +++ b/binaries/data/mods/public/gui/campaigns/load_modal/LoadModal.js @@ -143,5 +143,6 @@ var g_LoadModal; function init() { + registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]); return new Promise(closePageCallback => { g_LoadModal = new LoadModal(closePageCallback); }); } diff --git a/binaries/data/mods/public/gui/campaigns/new_modal/NewCampaignModal.js b/binaries/data/mods/public/gui/campaigns/new_modal/NewCampaignModal.js index afe8f1d0f0..9475792de4 100644 --- a/binaries/data/mods/public/gui/campaigns/new_modal/NewCampaignModal.js +++ b/binaries/data/mods/public/gui/campaigns/new_modal/NewCampaignModal.js @@ -45,6 +45,7 @@ function init(campaign_template_data) { return new Promise(closePageCallback => { + registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]); g_NewCampaignModal = new NewCampaignModal(campaign_template_data, closePageCallback); }); } diff --git a/binaries/data/mods/public/gui/campaigns/setup/CampaignSetupPage.js b/binaries/data/mods/public/gui/campaigns/setup/CampaignSetupPage.js index f23207038d..a47f031556 100644 --- a/binaries/data/mods/public/gui/campaigns/setup/CampaignSetupPage.js +++ b/binaries/data/mods/public/gui/campaigns/setup/CampaignSetupPage.js @@ -75,6 +75,7 @@ var g_CampaignSetupPage; function init() { + registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "manual", "tips", "mapbrowser"]); return new Promise(closePageCallback => { g_CampaignSetupPage = new CampaignSetupPage(closePageCallback); diff --git a/binaries/data/mods/public/gui/common/functions_utility.js b/binaries/data/mods/public/gui/common/functions_utility.js index fe3d98f822..ed20d03bc2 100644 --- a/binaries/data/mods/public/gui/common/functions_utility.js +++ b/binaries/data/mods/public/gui/common/functions_utility.js @@ -325,4 +325,23 @@ function toPascalCase(str) .split('_') .map(s => s.charAt(0).toUpperCase() + s.slice(1)) .join(''); +} +/** + * Registers global hotkeys for opening GUI pages. + * + * Each hotkey opens a child page following the naming convention: + * page_${hotkey}.xml + */ +function registerGlobalGuiPageHotkeys(hotkeys) +{ + for (const key of hotkeys) + { + const guiPage = `gui/page_${key}.xml`; + if (!Engine.FileExists(guiPage)) + { + warn(`Skipping global hotkey '${key}': Missing GUI page '${guiPage}'.`); + continue; + } + Engine.SetGlobalHotkey(key, "Press", () => Engine.OpenChildPage(`page_${key}.xml`)); + } } \ No newline at end of file diff --git a/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Buttons/MapBrowser.js b/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Buttons/MapBrowser.js index 1c5dd9efe9..71890b2bed 100644 --- a/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Buttons/MapBrowser.js +++ b/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Buttons/MapBrowser.js @@ -11,7 +11,7 @@ GameSettingControls.MapBrowser = class MapBrowser extends GameSettingControlButt } this.button.tooltip = colorizeHotkey(this.HotkeyTooltip, this.HotkeyConfig); - Engine.SetGlobalHotkey(this.HotkeyConfig, "Press", this.onPress.bind(this)); + this.button.hotkey = this.HotkeyConfig; } onSettingsLoaded() @@ -31,15 +31,20 @@ GameSettingControls.MapBrowser = class MapBrowser extends GameSettingControlButt onPress() { - this.setupWindow.pages.MapBrowserPage.openPage(this.enabled); + const page = this.setupWindow.pages.MapBrowserPage; + + if (!page.mapBrowserPage.hidden) + page.submitMapSelection(); + else + page.openPage(this.enabled); } }; GameSettingControls.MapBrowser.prototype.HotkeyConfig = - "gamesetup.mapbrowser.open"; + "mapbrowser"; GameSettingControls.MapBrowser.prototype.Caption = translate("Browse Maps"); GameSettingControls.MapBrowser.prototype.HotkeyTooltip = - translate("Press %(hotkey)s to view the list of available maps."); + translate("%(hotkey)s: View the list of available maps."); diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js index af6feb3634..af1f86e869 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -43,6 +43,7 @@ var g_SetupWindow; function init(initData, hotloadData) { + registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "manual", "tips"]); return Promise.race([new Promise(closePageCallback => { g_SetupWindow = new SetupWindow(initData, hotloadData, closePageCallback); diff --git a/binaries/data/mods/public/gui/hotkeys/HotkeysPage.js b/binaries/data/mods/public/gui/hotkeys/HotkeysPage.js index 25e3cc1caa..b089001048 100644 --- a/binaries/data/mods/public/gui/hotkeys/HotkeysPage.js +++ b/binaries/data/mods/public/gui/hotkeys/HotkeysPage.js @@ -193,6 +193,7 @@ function init() // suppress the warning. /* eslint-disable-next-line no-new */ new HotkeysPage(new HotkeyMetadata(), closePageCallback); + Engine.SetGlobalHotkey("hotkeys", "Press", closePageCallback); }); } diff --git a/binaries/data/mods/public/gui/hotkeys/spec/engine.json b/binaries/data/mods/public/gui/hotkeys/spec/engine.json index 08531e4059..4906530b04 100644 --- a/binaries/data/mods/public/gui/hotkeys/spec/engine.json +++ b/binaries/data/mods/public/gui/hotkeys/spec/engine.json @@ -106,6 +106,38 @@ "mousegrabtoggle": { "name": "Toggle mouse grab", "desc": "Constrain to window boundaries or release the mouse." + }, + "structree": { + "name": "Open Structure Tree window", + "desc": "Open Structure Tree window." + }, + "civinfo": { + "name": "Open Civilization Overview window", + "desc": "Open Civilization Overview window." + }, + "catafalque": { + "name": "Open Catafalque Overview", + "desc": "Open Catafalque Overview window." + }, + "mapbrowser": { + "name": "Open Map Browser window", + "desc": "Open Map Browser window." + }, + "manual": { + "name": "Open the 0 A.D. Game Manual", + "desc": "Open the 0 A.D. Game Manual." + }, + "tips": { + "name": "Open Tips and Tricks window", + "desc": "Open Tips and Tricks window." + }, + "hotkeys": { + "name": "Open Hotkeys window", + "desc": "Open Hotkeys window." + }, + "options": { + "name": "Open Options window", + "desc": "Open Options window." } } } diff --git a/binaries/data/mods/public/gui/hotkeys/spec/ingamegui.json b/binaries/data/mods/public/gui/hotkeys/spec/ingamegui.json index fc9fc9988c..f7bfe55863 100644 --- a/binaries/data/mods/public/gui/hotkeys/spec/ingamegui.json +++ b/binaries/data/mods/public/gui/hotkeys/spec/ingamegui.json @@ -39,14 +39,6 @@ "name": "Toggle MP lobby", "desc": "Show the multiplayer lobby in a dialog window." }, - "structree": { - "name": "Toggle structure tree", - "desc": "Toggle the structure tree." - }, - "civinfo": { - "name": "Toggle civ info", - "desc": "Toggle the civilization info." - }, "session.gui.menu.toggle": { "name": "Toggle in-game menu", "desc": "Toggle in-game menu." diff --git a/binaries/data/mods/public/gui/lobby/lobby.js b/binaries/data/mods/public/gui/lobby/lobby.js index ec565ef3b8..208c81e9cf 100644 --- a/binaries/data/mods/public/gui/lobby/lobby.js +++ b/binaries/data/mods/public/gui/lobby/lobby.js @@ -36,6 +36,7 @@ async function init(attribs) if (g_Settings) return new Promise(closePageCallback => { + registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]); g_LobbyHandler = new LobbyHandler(closePageCallback, attribs && attribs.dialog); }); diff --git a/binaries/data/mods/public/gui/manual/manual.js b/binaries/data/mods/public/gui/manual/manual.js index 4592f0c44a..3d7ab74d7e 100644 --- a/binaries/data/mods/public/gui/manual/manual.js +++ b/binaries/data/mods/public/gui/manual/manual.js @@ -11,5 +11,6 @@ function init() return new Promise(closePageCallback => { Engine.GetGUIObjectByName("closeButton").onPress = closePageCallback; + Engine.SetGlobalHotkey("manual", "Press", closePageCallback); }); -} +} \ No newline at end of file diff --git a/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowserPage.js b/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowserPage.js index e7b2ee2591..b1bdbb8212 100644 --- a/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowserPage.js +++ b/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowserPage.js @@ -10,5 +10,9 @@ function init() const browser = new MapBrowser(cache, filters); browser.openPage(false); browser.controls.MapFiltering.select("default", "skirmish"); - return new Promise(closePageCallback => { browser.registerClosePageHandler(closePageCallback); }); -} + Engine.SetGlobalHotkey("mapbrowser", "Press", () => browser.closePage()); + return new Promise(closePageCallback => + { + browser.registerClosePageHandler(closePageCallback); + }); +} \ No newline at end of file diff --git a/binaries/data/mods/public/gui/options/options.js b/binaries/data/mods/public/gui/options/options.js index 23bc5e714c..f50d423f11 100644 --- a/binaries/data/mods/public/gui/options/options.js +++ b/binaries/data/mods/public/gui/options/options.js @@ -234,7 +234,9 @@ async function init(data, hotloadData) g_Options = Engine.ReadJSONFile("gui/options/options.json"); translateObjectKeys(g_Options, ["label", "tooltip"]); deepfreeze(g_Options); - + Engine.SetGlobalHotkey("options", "Press", () => + Engine.GetGUIObjectByName("closeButton").onPress() + ); placeTabButtons( g_Options, false, diff --git a/binaries/data/mods/public/gui/hotkeys/page_hotkeys.xml b/binaries/data/mods/public/gui/page_hotkeys.xml similarity index 100% rename from binaries/data/mods/public/gui/hotkeys/page_hotkeys.xml rename to binaries/data/mods/public/gui/page_hotkeys.xml diff --git a/binaries/data/mods/public/gui/pregame/MainMenuItems.js b/binaries/data/mods/public/gui/pregame/MainMenuItems.js index daa1efc0c1..1066c11c15 100644 --- a/binaries/data/mods/public/gui/pregame/MainMenuItems.js +++ b/binaries/data/mods/public/gui/pregame/MainMenuItems.js @@ -5,7 +5,8 @@ export const mainMenuItems = [ "submenu": [ { "caption": translate("Manual"), - "tooltip": translate("Open the 0 A.D. Game Manual."), + "tooltip": colorizeHotkey(translate("%(hotkey)s: Open the 0 A.D. Game Manual."), "manual"), + "hotkey": "manual", "onPress": () => { Engine.OpenChildPage("page_manual.xml"); @@ -41,7 +42,8 @@ export const mainMenuItems = [ }, { "caption": translate("Tips and Tricks"), - "tooltip": translate("Discover simple tips, tricks, and game mechanics."), + "tooltip": colorizeHotkey(translate("%(hotkey)s: Discover simple tips, tricks, and game mechanics."), "tips"), + "hotkey": "tips", "onPress": Engine.OpenChildPage.bind(null, "page_tips.xml", { "tipScrolling": true }) @@ -66,7 +68,8 @@ export const mainMenuItems = [ }, { "caption": translate("Catafalque Overview"), - "tooltip": translate("Compare the bonuses of catafalques featured in 0 A.D."), + "tooltip": colorizeHotkey(translate("%(hotkey)s: Compare the bonuses of catafalques featured in 0 A.D."), "catafalque"), + "hotkey": "catafalque", "onPress": () => { Engine.OpenChildPage("page_catafalque.xml"); @@ -74,7 +77,8 @@ export const mainMenuItems = [ }, { "caption": translate("Map Overview"), - "tooltip": translate("View the different maps featured in 0 A.D."), + "tooltip": colorizeHotkey(translate("%(hotkey)s: View the different maps featured in 0 A.D."), "mapbrowser"), + "hotkey": "mapbrowser", "onPress": () => { Engine.OpenChildPage("page_mapbrowser.xml"); @@ -295,7 +299,8 @@ export const mainMenuItems = [ "submenu": [ { "caption": translate("Options"), - "tooltip": translate("Adjust game settings."), + "tooltip": colorizeHotkey(translate("%(hotkey)s: Adjust game settings."), "options"), + "hotkey": "options", "onPress": async() => { fireConfigChangeHandlers(await Engine.OpenChildPage("page_options.xml")); @@ -303,10 +308,11 @@ export const mainMenuItems = [ }, { "caption": translate("Hotkeys"), - "tooltip": translate("Adjust hotkeys."), + "tooltip": colorizeHotkey(translate("%(hotkey)s: Adjust hotkeys."), "hotkeys"), + "hotkey": "hotkeys", "onPress": () => { - Engine.OpenChildPage("hotkeys/page_hotkeys.xml"); + Engine.OpenChildPage("page_hotkeys.xml"); } }, { diff --git a/binaries/data/mods/public/gui/reference/catafalque/catafalque.js b/binaries/data/mods/public/gui/reference/catafalque/catafalque.js index d8f823836e..eb61e3b13a 100644 --- a/binaries/data/mods/public/gui/reference/catafalque/catafalque.js +++ b/binaries/data/mods/public/gui/reference/catafalque/catafalque.js @@ -1,4 +1,8 @@ function init(data = {}) { - return new Promise(closePageCallback => { g_Page = new CatafalquePage(closePageCallback); }); + return new Promise(closePageCallback => + { + g_Page = new CatafalquePage(closePageCallback); + Engine.SetGlobalHotkey("catafalque", "Press", closePageCallback); + }); } diff --git a/binaries/data/mods/public/gui/reference/tips/TipsPage.js b/binaries/data/mods/public/gui/reference/tips/TipsPage.js index ad2185a30e..11d2b6be0e 100644 --- a/binaries/data/mods/public/gui/reference/tips/TipsPage.js +++ b/binaries/data/mods/public/gui/reference/tips/TipsPage.js @@ -2,6 +2,8 @@ class TipsPage { constructor(initData, hotloadData, closePageCallback) { + initData = { "tipScrolling": true, ...initData }; + this.closePageCallback = closePageCallback; this.tipDisplay = new TipDisplay(initData, hotloadData); this.closeButton = new CloseButton(this); diff --git a/binaries/data/mods/public/gui/reference/tips/tips.js b/binaries/data/mods/public/gui/reference/tips/tips.js index 299c328cf6..e0adaf9e13 100644 --- a/binaries/data/mods/public/gui/reference/tips/tips.js +++ b/binaries/data/mods/public/gui/reference/tips/tips.js @@ -5,6 +5,7 @@ function init(initData, hotloadData) return new Promise(closePageCallback => { g_TipsPage = new TipsPage(initData, hotloadData, closePageCallback); + Engine.SetGlobalHotkey("tips", "Press", closePageCallback); }); } diff --git a/binaries/data/mods/public/gui/replaymenu/replay_menu.js b/binaries/data/mods/public/gui/replaymenu/replay_menu.js index 189f6f0b7d..dd38c6a4d3 100644 --- a/binaries/data/mods/public/gui/replaymenu/replay_menu.js +++ b/binaries/data/mods/public/gui/replaymenu/replay_menu.js @@ -77,7 +77,7 @@ async function init(data) "page": "page_pregame.xml" } }; } - + registerGlobalGuiPageHotkeys(["options", "hotkeys", "civinfo", "structree", "catafalque", "mapbrowser", "manual", "tips"]); initHotkeyTooltips(); displayReplayList(); diff --git a/binaries/data/mods/public/gui/session/MenuButtons.js b/binaries/data/mods/public/gui/session/MenuButtons.js index c792a405c3..b1e9892c6e 100644 --- a/binaries/data/mods/public/gui/session/MenuButtons.js +++ b/binaries/data/mods/public/gui/session/MenuButtons.js @@ -13,7 +13,16 @@ MenuButtons.prototype.Manual = class { this.button = button; this.button.caption = translate(translate("Manual")); + this.button.hotkey = "manual"; this.pauseControl = pauseControl; + registerHotkeyChangeHandler(this.rebuild.bind(this)); + } + + rebuild() + { + this.button.tooltip = sprintf(translate("%(hotkey)s: Open the 0 A.D. game manual."), { + "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), + }); } async onPress() @@ -88,7 +97,7 @@ MenuButtons.prototype.Summary = class rebuild() { - this.button.tooltip = sprintf(translate("Press %(hotkey)s to open the summary screen."), { + this.button.tooltip = sprintf(translate("%(hotkey)s: Open the summary screen."), { "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), }); } @@ -139,7 +148,7 @@ MenuButtons.prototype.Lobby = class rebuild() { - this.button.tooltip = sprintf(translate("Press %(hotkey)s to open the multiplayer lobby page without leaving the game."), { + this.button.tooltip = sprintf(translate("%(hotkey)s: Open the multiplayer lobby page without leaving the game."), { "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), }); } @@ -159,7 +168,16 @@ MenuButtons.prototype.Options = class { this.button = button; this.button.caption = translate("Options"); + this.button.hotkey = "options"; this.pauseControl = pauseControl; + registerHotkeyChangeHandler(this.rebuild.bind(this)); + } + + rebuild() + { + this.button.tooltip = sprintf(translate("%(hotkey)s: Adjust game settings."), { + "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), + }); } async onPress() @@ -178,7 +196,16 @@ MenuButtons.prototype.Hotkeys = class { this.button = button; this.button.caption = translate("Hotkeys"); + this.button.hotkey = "hotkeys"; this.pauseControl = pauseControl; + registerHotkeyChangeHandler(this.rebuild.bind(this)); + } + + rebuild() + { + this.button.tooltip = sprintf(translate("%(hotkey)s: Adjust hotkeys."), { + "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), + }); } async onPress() @@ -186,7 +213,7 @@ MenuButtons.prototype.Hotkeys = class closeOpenDialogs(); this.pauseControl.implicitPause(); - await Engine.OpenChildPage("hotkeys/page_hotkeys.xml"); + await Engine.OpenChildPage("page_hotkeys.xml"); resumeGame(); } }; @@ -211,7 +238,7 @@ MenuButtons.prototype.Pause = class { this.button.enabled = this.pauseControl.canPause(true); this.button.caption = this.pauseControl.explicitPause ? translate("Resume") : translate("Pause"); - this.button.tooltip = sprintf(translate("Press %(hotkey)s to pause or resume the game."), { + this.button.tooltip = sprintf(translate("%(hotkey)s: Pause or resume the game."), { "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), }); } diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index bb55f305f7..81c9c53f85 100644 --- a/binaries/data/mods/public/gui/session/session.js +++ b/binaries/data/mods/public/gui/session/session.js @@ -316,6 +316,20 @@ async function init(initData, hotloadData) updatePlayerData(); initializeMusic(); // before changing the perspective Engine.SetBoundingBoxDebugOverlay(false); + Engine.SetGlobalHotkey("catafalque", "Press", async() => + { + closeOpenDialogs(); + g_PauseControl.implicitPause(); + await Engine.OpenChildPage("page_catafalque.xml"); + resumeGame(); + }); + Engine.SetGlobalHotkey("tips", "Press", async() => + { + closeOpenDialogs(); + g_PauseControl.implicitPause(); + await Engine.OpenChildPage("page_tips.xml"); + resumeGame(); + }); const promise = Promise.race([g_IsNetworked ? handleNetMessages() : new Promise(() => {}), new Promise(closePageCallback =>