Index: binaries/data/mods/public/gui/session/hotkeys/camera.xml =================================================================== --- binaries/data/mods/public/gui/session/hotkeys/camera.xml +++ binaries/data/mods/public/gui/session/hotkeys/camera.xml @@ -1,92 +0,0 @@ - - - - - setCameraFollow(g_Selection.getFirstSelected()); - - - - performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "focus-rally"); - - - - - jumpCamera(1); - - - - jumpCamera(2); - - - - jumpCamera(3); - - - - jumpCamera(4); - - - - jumpCamera(5); - - - - jumpCamera(6); - - - - jumpCamera(7); - - - - jumpCamera(8); - - - - jumpCamera(9); - - - - jumpCamera(10); - - - - setJumpCamera(1); - - - - setJumpCamera(2); - - - - setJumpCamera(3); - - - - setJumpCamera(4); - - - - setJumpCamera(5); - - - - setJumpCamera(6); - - - - setJumpCamera(7); - - - - setJumpCamera(8); - - - - setJumpCamera(9); - - - - setJumpCamera(10); - - Index: binaries/data/mods/public/gui/session/hotkeys/misc.xml =================================================================== --- binaries/data/mods/public/gui/session/hotkeys/misc.xml +++ binaries/data/mods/public/gui/session/hotkeys/misc.xml @@ -1,126 +0,0 @@ - - - - closeOpenDialogs(); - - - - openChat(); - - - - openChat(g_IsObserver ? "/observers" : "/allies"); - - - - openChat(g_LastChatAddressee); - - - - g_ShowGUI = !g_ShowGUI; - - - - toggleMenu(); - - - - toggleTrade(); - - - - toggleTutorial(); - - - - openGameSummary(); - - - - toggleConfigBool("silhouettes"); - - - - - var newSetting = !Engine.Renderer_GetShowSkyEnabled(); - Engine.Renderer_SetShowSkyEnabled(newSetting); - - - - - togglePause(); - - - - Engine.QuickSave(); - - - - Engine.QuickLoad(); - - - - performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "delete"); - - - - unloadAll(); - - - - stopUnits(g_Selection.toList()); - - - - backToWork(); - - - - updateSelectionDetails(); - updateSelectionDetails(); - - - - - updateSelectionDetails(); - updateBarterButtons(); - - - - updateSelectionDetails(); - updateBarterButtons(); - - - - - - findIdleUnit(g_MilitaryTypes); - - - - findIdleUnit(["!Domestic"]); - - - - clearSelection(); - - - - toggleRangeOverlay("Attack"); - - - - toggleRangeOverlay("Auras"); - - - - toggleRangeOverlay("Heal"); - - - - - g_ShowAllStatusBars = !g_ShowAllStatusBars; - recalculateStatusBarDisplay(); - - - Index: binaries/data/mods/public/gui/session/hotkeys/training.xml =================================================================== --- binaries/data/mods/public/gui/session/hotkeys/training.xml +++ binaries/data/mods/public/gui/session/hotkeys/training.xml @@ -1,37 +0,0 @@ - - - - - addTrainingByPosition(0); - - - - - addTrainingByPosition(1); - - - - - addTrainingByPosition(2); - - - - - addTrainingByPosition(3); - - - - - addTrainingByPosition(4); - - - - - addTrainingByPosition(5); - - - - - addTrainingByPosition(6); - - Index: binaries/data/mods/public/gui/session/input.js =================================================================== --- binaries/data/mods/public/gui/session/input.js +++ binaries/data/mods/public/gui/session/input.js @@ -61,6 +61,174 @@ var doublePressTimer = 0; var prevHotkey = 0; +var g_Hotkeys = { + "session.backtowork": + { "hotkeydown": () => backToWork() }, + "session.gui.tutorial.toggle": + { "hotkeydown": () => toggleTutorial() }, + "silhouettes": + { "hotkeydown": () => toggleConfigBool("silhouettes") }, + "session.stop": + { "hotkeydown": () => stopUnits(g_Selection.toList()) }, + "quicksave": + { "hotkeydown": () => Engine.QuickSave() }, + "quickload": + { "hotkeydown": () => Engine.QuickLoad() }, + "session.kill": + { "hotkeydown": () => performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "delete") }, + "session.unload": + { "hotkeydown": () => unloadAll() }, + "session.batchtrain": + { "hotkeydown": () => updateSelectionDetails(), "hotkeyup": () => updateSelectionDetails() }, + "session.massbarter": + { "hotkeydown": () => { + updateSelectionDetails(); + updateBarterButtons(); + }, + "hotkeyup": () => { + updateSelectionDetails(); + updateBarterButtons(); + } }, + "cancel": + { "hotkeydown": () => closeOpenDialogs() }, + "teamchat": + { "hotkeydown": () => openChat(g_IsObserver ? "/observers" : "/allies") }, + "privatechat": + { "hotkeydown": () => openChat(g_LastChatAddressee) }, + "session.gui.toggle": + { "hotkeydown": () => g_ShowGUI = !g_ShowGUI }, + "showsky": + { "hotkeydown": () => { + var newSetting = !Engine.Renderer_GetShowSkyEnabled(); + Engine.Renderer_SetShowSkyEnabled(newSetting); + } }, + // Find idle warrior - TODO: Potentially move this to own UI button? + "selection.idlewarrior": + { "hotkeydown": () => findIdleUnit(g_MilitaryTypes) }, + "selection.idleunit": + { "hotkeydown": () => findIdleUnit(["!Domestic"]) }, + "selection.cancel": + { "hotkeydown": () => clearSelection() }, + "session.toggleattackrange": + { "hotkeydown": () => toggleRangeOverlay("Attack") }, + "session.toggleaurasrange": + { "hotkeydown": () => toggleRangeOverlay("Auras") }, + "session.togglehealrange": + { "hotkeydown": () => toggleRangeOverlay("Heal") }, + "session.showstatusbars": + { "hotkeydown": () => { + g_ShowAllStatusBars = !g_ShowAllStatusBars; + recalculateStatusBarDisplay(); + } }, + + // Queue first unit in the training queue. + "session.queueunit.1": + { "hotkeydown": () => addTrainingByPosition(0) }, + // Queue 2nd unit in the training queue. + "session.queueunit.2": + { "hotkeydown": () => addTrainingByPosition(1) }, + "session.queueunit.3": + { "hotkeydown": () => addTrainingByPosition(2) }, + "session.queueunit.4": + { "hotkeydown": () => addTrainingByPosition(3) }, + "session.queueunit.5": + { "hotkeydown": () => addTrainingByPosition(4) }, + "session.queueunit.6": + { "hotkeydown": () => addTrainingByPosition(5) }, + "session.queueunit.7": + { "hotkeydown": () => addTrainingByPosition(6) }, + + // camera.follow mode - follow the first unit in the selection. + "camera.follow": + { "hotkeydown": () => setCameraFollow(g_Selection.getFirstSelected()) }, + "camera.rallypointfocus": + { "hotkeydown": () => performCommand(g_Selection.toList().map(ent => GetEntityState(ent)), "focus-rally") }, + // Camera jumping - press a hotkey to mark a position and another hotkey to jump back there. + "camera.jump.1": + { "hotkeydown": () => jumpCamera(1) }, + "camera.jump.2": + { "hotkeydown": () => jumpCamera(2) }, + "camera.jump.3": + { "hotkeydown": () => jumpCamera(3) }, + "camera.jump.4": + { "hotkeydown": () => jumpCamera(4) }, + "camera.jump.5": + { "hotkeydown": () => jumpCamera(5) }, + "camera.jump.6": + { "hotkeydown": () => jumpCamera(6) }, + "camera.jump.7": + { "hotkeydown": () => jumpCamera(7) }, + "camera.jump.8": + { "hotkeydown": () => jumpCamera(8) }, + "camera.jump.9": + { "hotkeydown": () => jumpCamera(9) }, + "camera.jump.10": + { "hotkeydown": () => jumpCamera(10) }, + "camera.jump.set.1": + { "hotkeydown": () => setJumpCamera(1) }, + "camera.jump.set.2": + { "hotkeydown": () => setJumpCamera(2) }, + "camera.jump.set.3": + { "hotkeydown": () => setJumpCamera(3) }, + "camera.jump.set.4": + { "hotkeydown": () => setJumpCamera(4) }, + "camera.jump.set.5": + { "hotkeydown": () => setJumpCamera(5) }, + "camera.jump.set.6": + { "hotkeydown": () => setJumpCamera(6) }, + "camera.jump.set.7": + { "hotkeydown": () => setJumpCamera(7) }, + "camera.jump.set.8": + { "hotkeydown": () => setJumpCamera(8) }, + "camera.jump.set.9": + { "hotkeydown": () => setJumpCamera(9) }, + "camera.jump.set.10": + { "hotkeydown": () => setJumpCamera(10) } +}; + +/** + * Group hotkeys that are mapped on the same key to one array, that is later processed if hotkey is hitted. + * If explicity want only one function happening on condition + * When hitted, the processing can be stopped by returning a true in a function. So only furthe + */ +function groupHotkeysMappedSameKey() +{ + let getUnifiedHotkeyName = name => Engine.ConfigDB_GetValue("user", name).toLowerCase().replace("escape", "esc"); + + for (let hotkeyA in g_Hotkeys) + { + let keyA = getUnifiedHotkeyName("hotkey." + hotkeyA); + + for (let typeA in g_Hotkeys[hotkeyA]) + { + // Has hotkey already been checked. + if (!g_Hotkeys[hotkeyA][typeA] || Array.isArray(g_Hotkeys[hotkeyA][typeA])) + continue; + + for (let hotkeyB in g_Hotkeys) + { + // Same hotkey. + if (hotkeyB == hotkeyA) + continue; + + let keyB = getUnifiedHotkeyName("hotkey." + hotkeyB); + for (let typeB in g_Hotkeys[hotkeyB]) + { + // Same key and type. + if (keyB == keyA && typeA == typeB) + { + if (!Array.isArray(g_Hotkeys[hotkeyA][typeA])) + g_Hotkeys[hotkeyA][typeA] = [g_Hotkeys[hotkeyA][typeA]]; + + g_Hotkeys[hotkeyA][typeA].push(g_Hotkeys[hotkeyB][typeB]) + g_Hotkeys[hotkeyB][typeB] = ""; + } + } + } + } + } +} + function updateCursorAndTooltip() { var cursorSet = false; @@ -797,6 +965,13 @@ g_ShowGuarded = (ev.type == "hotkeydown"); updateAdditionalHighlight(); } + else if (!!g_Hotkeys[ev.hotkey] && !!g_Hotkeys[ev.hotkey][ev.type]) + { + if (Array.isArray(g_Hotkeys[ev.hotkey][ev.type])) + g_Hotkeys[ev.hotkey][ev.type].some(exec => exec()); + else + g_Hotkeys[ev.hotkey][ev.type](); + } if (inputState != INPUT_NORMAL && inputState != INPUT_SELECTING) clickedEntity = INVALID_ENTITY; Index: binaries/data/mods/public/gui/session/menu.js =================================================================== --- binaries/data/mods/public/gui/session/menu.js +++ binaries/data/mods/public/gui/session/menu.js @@ -130,12 +130,6 @@ Engine.PushGuiPage("page_lobby.xml", { "dialog": true }); } -function chatMenuButton() -{ - closeOpenDialogs(); - openChat(); -} - function diplomacyMenuButton() { closeOpenDialogs(); @@ -1238,11 +1232,20 @@ function closeOpenDialogs() { + if (Engine.GetGUIObjectByName("chatDialogPanel").hidden && + !g_IsMenuOpen && + !g_IsDiplomacyOpen && + !g_IsTradeOpen && + !g_IsObjectivesOpen) + return false; + closeMenu(); closeChat(); closeDiplomacy(); closeTrade(); closeObjectives(); + + return true; } function formatTributeTooltip(playerID, resourceCode, amount) Index: binaries/data/mods/public/gui/session/menu.xml =================================================================== --- binaries/data/mods/public/gui/session/menu.xml +++ binaries/data/mods/public/gui/session/menu.xml @@ -24,9 +24,10 @@ style="StoneButtonFancy" size="0 32 100% 60" tooltip_style="sessionToolTip" + hotkey="chat" > Chat - chatMenuButton(); + openChat(); @@ -48,6 +49,7 @@ style="StoneButtonFancy" size="0 96 100% 124" tooltip_style="sessionToolTip" + hotkey="summary" > Summary openGameSummary(); @@ -62,7 +64,6 @@ hotkey="lobby" > Lobby - Show the multiplayer lobby in a dialog window. lobbyDialogButton(); @@ -83,6 +84,7 @@ style="StoneButtonFancy" size="0 192 100% 220" tooltip_style="sessionToolTip" + hotkey="pause" > Pause togglePause(); Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -200,6 +200,53 @@ */ var g_MilitaryTypes = ["Melee", "Ranged"]; +/** + * Setting tooltips, hotkey descriptions in tooltips and enable/disable states of the GUI buttons. + */ +var g_GuiButtons = () => { return { + "menuButton": { "description": "Toggle the in-game menu." }, + "manualButton": { "description": "Show the game manual." }, + "saveGameButton": { "description": "Save the current game." }, + "summaryButton": { "description": "Show the summary dialog." }, + "lobbyButton": { "description": "Show the multiplayer lobby in a dialog.", "enabled": Engine.HasXmppClient() }, + "chatButton": { "hotkey": [ + { "hotkey": "chat", "description": translate("Chat"), "showOnlyWhenUsed": true }, + { "hotkey": "teamchat", "description": translate("Team chat"), "showOnlyWhenUsed": true }, + { "hotkey": "privatechat", "description": translate("Private chat"), "showOnlyWhenUsed": true } + ] }, + "optionsButton": { "description": "Show the options dialog." }, + "pauseButton": { "description": "Toggle pause the game.", "enabled": !g_IsObserver || !g_IsNetworked || g_IsController }, + "menuResignButton": { "description": "Resign from the current game.", "enabled": !g_IsObserver }, + "menuExitButton": { "description": "Exit from the current game." }, + "tradeButton1": { "description": "Barter & Trade" }, + "idleWorkerButton": { "description": "Find idle worker." }, + "diplomacyColorsButton": { "description": "Toggle Diplomacy Colors." }, + "tradeHelp": { + "description": colorizeHotkey( + translate("Select one type of goods you want to modify by clicking on it, and then use the arrows of the other types to modify their shares. You can also press %(hotkey)s while selecting one type of goods to bring its share to 100%%."), + "session.fulltradeswap") + }, + "barterHelp": { + "description": sprintf( + translate("Start by selecting the resource you wish to sell from the upper row. For each time the lower buttons are pressed, %(quantity)s of the upper resource will be sold for the displayed quantity of the lower. Press and hold %(hotkey)s to temporarily multiply the traded amount by %(multiplier)s."), { + "quantity": g_BarterResourceSellQuantity, + "hotkey": colorizeHotkey("%(hotkey)s", "session.massbarter"), + "multiplier": g_BarterMultiplier + }) + }, + "chatInput": { + "description": translateWithContext("chat input", "Type the message to send.") + "\n" + + colorizeAutocompleteHotkey() + + colorizeHotkey("\n" + translate("Press %(hotkey)s to open the public chat."), "chat") + + colorizeHotkey( + "\n" + (g_IsObserver ? + translate("Press %(hotkey)s to open the observer chat.") : + translate("Press %(hotkey)s to open the ally chat.")), + "teamchat") + + colorizeHotkey("\n" + translate("Press %(hotkey)s to open the previously selected private chat."), "privatechat") + } +} }; + function GetSimState() { if (!g_SimState) @@ -290,6 +337,7 @@ initializeMusic(); // before changing the perspective initSessionMenuButtons(); + groupHotkeysMappedSameKey(); for (let slot in Engine.GetGUIObjectByName("panelEntityPanel").children) initPanelEntities(slot); @@ -430,39 +478,40 @@ } /** - * Depends on the current player (g_IsObserver). + * Setting enable/disable state and tooltip with hotkey information to GUI Buttons. */ -function updateHotkeyTooltips() +function updateGuiButtons() { - Engine.GetGUIObjectByName("chatInput").tooltip = - translateWithContext("chat input", "Type the message to send.") + "\n" + - colorizeAutocompleteHotkey() + - colorizeHotkey("\n" + translate("Press %(hotkey)s to open the public chat."), "chat") + - colorizeHotkey( - "\n" + (g_IsObserver ? - translate("Press %(hotkey)s to open the observer chat.") : - translate("Press %(hotkey)s to open the ally chat.")), - "teamchat") + - colorizeHotkey("\n" + translate("Press %(hotkey)s to open the previously selected private chat."), "privatechat"); - - Engine.GetGUIObjectByName("idleWorkerButton").tooltip = - colorizeHotkey("%(hotkey)s" + " ", "selection.idleworker") + - translate("Find idle worker"); - - Engine.GetGUIObjectByName("diplomacyColorsButton").tooltip = - colorizeHotkey("%(hotkey)s" + " ", "diplomacycolors") + - translate("Toggle Diplomacy Colors"); - - Engine.GetGUIObjectByName("tradeHelp").tooltip = colorizeHotkey( - translate("Select one type of goods you want to modify by clicking on it, and then use the arrows of the other types to modify their shares. You can also press %(hotkey)s while selecting one type of goods to bring its share to 100%%."), - "session.fulltradeswap"); - - Engine.GetGUIObjectByName("barterHelp").tooltip = sprintf( - translate("Start by selecting the resource you wish to sell from the upper row. For each time the lower buttons are pressed, %(quantity)s of the upper resource will be sold for the displayed quantity of the lower. Press and hold %(hotkey)s to temporarily multiply the traded amount by %(multiplier)s."), { - "quantity": g_BarterResourceSellQuantity, - "hotkey": colorizeHotkey("%(hotkey)s", "session.massbarter"), - "multiplier": g_BarterMultiplier - }); + let colorizedHotkey = name => { + let hotkey = Engine.ConfigDB_GetValue("user", "hotkey." + name); + return !hotkey || hotkey == "unused" ? "" : setStringTags("\\[" + hotkey + "]", g_HotkeyTags); + } + + let getTooltip = (hotkey, description) => + colorizedHotkey(hotkey) == "" ? description : translate(description) != "" ? sprintf(translate("%(hotkey)s: " + description), { + "hotkey": colorizedHotkey(hotkey) + }) : colorizedHotkey(hotkey); + + let buttons = g_GuiButtons(); + for (let name in buttons) + { + let button = buttons[name]; + let enabled = button.enabled == undefined || button.enabled; + let guiObj = Engine.GetGUIObjectByName(name); + guiObj.enabled = enabled; + let hotkey = button.hotkey || guiObj.hotkey || ""; + + if ((!hotkey && !button.description) || !enabled) + continue; + + if (hotkey && Array.isArray(hotkey)) + guiObj.tooltip = hotkey.map(tooltip => + !!tooltip.showOnlyWhenUsed && colorizedHotkey(tooltip.hotkey) == "" ? "" : + getTooltip(tooltip.hotkey, tooltip.description)).join("\n"); + else + if (!hotkey || typeof(hotkey) == 'string') + guiObj.tooltip = getTooltip(hotkey, button.description || ""); + } } function initPanelEntities(slot) @@ -555,7 +604,8 @@ updateDisplayedPlayerColors(); updateTopPanel(); updateChatAddressees(); - updateHotkeyTooltips(); + updateGuiButtons(); + // updateHotkeyTooltips(); // Update GUI and clear player-dependent cache g_TemplateData = {}; @@ -690,10 +740,6 @@ let alphaLabel = Engine.GetGUIObjectByName("alphaLabel"); alphaLabel.hidden = isPlayer && !viewPlayer.hidden; alphaLabel.size = isPlayer ? "50%+44 0 100%-283 100%" : "155 0 85%-279 100%"; - - Engine.GetGUIObjectByName("pauseButton").enabled = !g_IsObserver || !g_IsNetworked || g_IsController; - Engine.GetGUIObjectByName("menuResignButton").enabled = !g_IsObserver; - Engine.GetGUIObjectByName("lobbyButton").enabled = Engine.HasXmppClient(); } function reportPerformance(time) Index: binaries/data/mods/public/gui/session/top_panel/button_menu.xml =================================================================== --- binaries/data/mods/public/gui/session/top_panel/button_menu.xml +++ binaries/data/mods/public/gui/session/top_panel/button_menu.xml @@ -5,6 +5,7 @@ style="StoneButtonFancy" tooltip_style="sessionToolTip" z="70" + hotkey="session.gui.menu.toggle" >