Index: binaries/data/mods/mod/gui/common/modern/sprites.xml =================================================================== --- binaries/data/mods/mod/gui/common/modern/sprites.xml +++ binaries/data/mods/mod/gui/common/modern/sprites.xml @@ -483,6 +483,26 @@ - Misc. - ========================================== --> + + + + + + + + + + + 0 ? + 0 : + g_TabCategoryCount - 1 : + (g_TabCategorySelected + direction + g_TabCategoryCount) % g_TabCategoryCount); } function selectPanel(category) Index: binaries/data/mods/public/gui/gamesetup/gamesetup.js =================================================================== --- binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -300,6 +300,11 @@ var g_ChatMessages = []; /** + * Minimum amount of pixels required for the chat panel to be visible. + */ +var g_MinChatWidth = 74; + +/** * Filename and translated title of all maps, given the currently selected * maptype and filter. Sorted by title, shown in the dropdown. */ @@ -343,42 +348,91 @@ var g_PopulationCapacityRecommendation = 1200; /** + * Vertical size of a tab button. + */ +var g_TabButtonHeight = 30; + +/** + * Vertical space between two tab buttons. + */ +var g_TabButtonDist = 4; + +/** + * Vertical size of a setting object. + */ +var g_SettingHeight = 32; + +/** + * Vertical space between two setting objects. + */ +var g_SettingDist = 2; + +/** + * Width of a column in the settings panel. + */ +var g_ColumnWidth = 320; + +/** + * Pixels per millisecond the settings panel slides when opening/closing. + */ +var g_SlideSpeed = 1.2; + +/** + * Store last tick time. + */ +var g_LastTickTime = Date.now(); + +/** * Order in which the GUI elements will be shown. - * All valid options are required to appear here. - * The ones under "map" are shown in the map selection panel, - * the others in the "more options" dialog. - */ -var g_OptionOrderGUI = { - "map": [ - "mapType", - "mapFilter", - "mapSelection", - "numPlayers", - "mapSize" - ], - "more": [ - "triggerDifficulty", - "biome", - "gameSpeed", - "victoryCondition", - "relicCount", - "relicDuration", - "wonderDuration", - "populationCap", - "startingResources", - "ceasefire", - "nomad", - "regicideGarrison", - "exploreMap", - "revealMap", - "disableTreasures", - "disableSpies", - "lockTeams", - "lastManStanding", - "enableCheats", - "enableRating" - ] -}; + * All valid settings are required to appear here. + */ +var g_SettingsTabsGUI = [ + { + "label": translateWithContext("Match settings tab name", "Map"), + "settings": [ + "mapType", + "mapFilter", + "mapSelection", + "mapSize", + "biome", + "triggerDifficulty", + "disableTreasures", + "exploreMap", + "revealMap" + ] + }, + { + "label": translateWithContext("Match settings tab name", "Player"), + "settings": [ + "numPlayers", + "populationCap", + "startingResources", + "nomad", + "disableSpies" + ] + }, + { + "label": translateWithContext("Match settings tab name", "Victory Condition"), + "settings": [ + "victoryCondition", + "relicCount", + "relicDuration", + "wonderDuration", + "regicideGarrison" + ] + }, + { + "label": translateWithContext("Match settings tab name", "Miscellaneous"), + "settings": [ + "gameSpeed", + "ceasefire", + "lockTeams", + "lastManStanding", + "enableCheats", + "enableRating" + ] + } +]; /** * Contains the logic of all multiple-choice gamesettings. @@ -400,7 +454,7 @@ * autocomplete - Marks whether to autocomplete translated values of the string. (default: undefined) * If not undefined, must be a number that denotes the priority (higher numbers come first). * If undefined, still autocompletes the translated title of the setting. - * initOrder - Options with lower values will be initialized first. + * initOrder - Settings with lower values will be initialized first. */ var g_Dropdowns = { "mapType": { @@ -783,10 +837,10 @@ }, "revealMap": { "title": () => - // Translation: Make sure to differentiate between the revealed map and explored map options! + // Translation: Make sure to differentiate between the revealed map and explored map settings! translate("Revealed Map"), "tooltip": - // Translation: Make sure to differentiate between the revealed map and explored map options! + // Translation: Make sure to differentiate between the revealed map and explored map settings! () => translate("Toggle revealed map (see everything)."), "default": () => false, "defined": () => g_GameAttributes.settings.RevealMap !== undefined, @@ -802,10 +856,10 @@ }, "exploreMap": { "title": - // Translation: Make sure to differentiate between the revealed map and explored map options! + // Translation: Make sure to differentiate between the revealed map and explored map settings! () => translate("Explored Map"), "tooltip": - // Translation: Make sure to differentiate between the revealed map and explored map options! + // Translation: Make sure to differentiate between the revealed map and explored map settings! () => translate("Toggle explored map (see initial map)."), "default": () => false, "defined": () => g_GameAttributes.settings.ExploreMap !== undefined, @@ -908,7 +962,13 @@ */ var g_MiscControls = { "chatPanel": { - "hidden": () => !g_IsNetworked, + "hidden": () => { + if (!g_IsNetworked) + return true; + + let size = Engine.GetGUIObjectByName("chatPanel").getComputedSize(); + return size.right - size.left < g_MinChatWidth; + }, }, "chatInput": { "tooltip": () => colorizeAutocompleteHotkey(translate("Press %(hotkey)s to autocomplete playernames or settings.")), @@ -952,14 +1012,7 @@ Engine.PushGuiPage("page_lobby.xml", { "dialog": true }); }, "hidden": () => !Engine.HasXmppClient() - }, - // Display these after having hidden every GUI object in the "More Options" dialog - "moreOptionsLabel": { - "hidden": () => false, - }, - "hideMoreOptions": { - "hidden": () => false, - }, + } }; /** @@ -1090,6 +1143,12 @@ */ function initGUIObjects() { + for (let tab in g_SettingsTabsGUI) + g_SettingsTabsGUI[tab].tooltip = + sprintf(translate("Toggle the %(name)s settings tab."), { "name": g_SettingsTabsGUI[tab].label }) + + colorizeHotkey("\n" + translate("Use %(hotkey)s to move a settings tab down."), "tab.next") + + colorizeHotkey("\n" + translate("Use %(hotkey)s to move a settings tab up."), "tab.prev"); + // Copy all initOrder values into one object let initOrder = {}; for (let dropdown in g_Dropdowns) @@ -1097,19 +1156,37 @@ for (let checkbox in g_Checkboxes) initOrder[checkbox] = g_Checkboxes[checkbox].initOrder; - // Sort the object on initOrder so we can init the options in an arbitrary order - for (let option of Object.keys(initOrder).sort((a, b) => initOrder[a] - initOrder[b])) - if (g_Dropdowns[option]) - initDropdown(option); - else if (g_Checkboxes[option]) - initCheckbox(option); + // Sort the object on initOrder so we can init the settings in an arbitrary order + for (let setting of Object.keys(initOrder).sort((a, b) => initOrder[a] - initOrder[b])) + if (g_Dropdowns[setting]) + initDropdown(setting); + else if (g_Checkboxes[setting]) + initCheckbox(setting); else - warn('The option "' + option + '" is not defined.'); + warn('The setting "' + setting + '" is not defined.'); for (let dropdown in g_PlayerDropdowns) initPlayerDropdowns(dropdown); - resizeMoreOptionsWindow(); + let settingTabButtons = Engine.GetGUIObjectByName("settingTabButtons"); + let settingTabButtonsSize = settingTabButtons.size; + let settingTabButtonsBottom = settingTabButtonsSize.top + g_SettingsTabsGUI.length * (g_TabButtonHeight + g_TabButtonDist); + settingTabButtonsSize.bottom = settingTabButtonsBottom; + settingTabButtons.size = settingTabButtonsSize; + + let gameDescription = Engine.GetGUIObjectByName("mapInfoDescriptionFrame"); + let gameDescriptionSize = gameDescription.size; + gameDescriptionSize.top = settingTabButtonsBottom + 3; + gameDescription.size = gameDescriptionSize; + + placeTabButtons( + g_SettingsTabsGUI, + g_TabButtonHeight, + g_TabButtonDist, + category => { + selectPanel(category == g_TabCategorySelected ? undefined : category); + }, + displaySettings); initSPTips(); @@ -1128,6 +1205,60 @@ hideLoadingWindow(); } +function displaySettings() +{ + updateGUIObjects(); + Engine.GetGUIObjectByName("settingsPanel").hidden = false; +} + +/** + * Slide settings panel. + * @param dt - time in milliseconds since last call. + */ +function updateSettingsPanelPosition(dt) +{ + let settingsPanel = Engine.GetGUIObjectByName("settingsPanel"); + + let rightBorder = Engine.GetGUIObjectByName("settingTabButtons").size.left; + let offset = 0; + if (g_TabCategorySelected === undefined) + { + let maxOffset = rightBorder - settingsPanel.size.left; + if (maxOffset > 0) + offset = Math.min(g_SlideSpeed * dt, maxOffset); + } + else if (rightBorder > settingsPanel.size.right) + offset = Math.min(g_SlideSpeed * dt, rightBorder - settingsPanel.size.right); + else + { + let maxOffset = settingsPanel.size.right - rightBorder; + if (maxOffset > 0) + offset = -Math.min(g_SlideSpeed * dt, maxOffset); + } + + let size = settingsPanel.size; + size.left += offset; + size.right += offset; + settingsPanel.size = size; + + let settingsBackground = Engine.GetGUIObjectByName("settingsBackground"); + let backgroundSize = settingsBackground.size; + backgroundSize.left = size.left; + settingsBackground.size = backgroundSize; + + let chatPanel = Engine.GetGUIObjectByName("chatPanel"); + let chatSize = chatPanel.size; + + chatSize.right += offset; + chatPanel.size = chatSize; + chatPanel.hidden = g_MiscControls.chatPanel.hidden(); + + let spTips = Engine.GetGUIObjectByName("spTips"); + spTips.hidden = g_IsNetworked || + Engine.ConfigDB_GetValue("user", "gui.gamesetup.enabletips") !== "true" || + spTips.size.right > settingsPanel.getComputedSize().left; +} + function hideLoadingWindow() { let loadingWindow = Engine.GetGUIObjectByName("loadingWindow"); @@ -1140,24 +1271,26 @@ } /** - * Options in the "More Options" or "Map" panel use a generic name. + * Settings under the settings tabs use a generic name. * Player settings use custom names. */ -function getGUIObjectNameFromSetting(name) +function getGUIObjectNameFromSetting(setting) { - for (let panel in g_OptionOrderGUI) + let idxOffset = 0; + for (let category of g_SettingsTabsGUI) { - let idx = g_OptionOrderGUI[panel].indexOf(name); + let idx = category.settings.indexOf(setting); if (idx != -1) return [ - panel + "Option", - g_Dropdowns[name] ? "Dropdown" : "Checkbox", - "[" + idx + "]" + "setting", + g_Dropdowns[setting] ? "Dropdown" : "Checkbox", + "[" + (idx + idxOffset) + "]" ]; + idxOffset += category.settings.length; } // Assume there is a GUI object with exactly that setting name - return [name, "", ""]; + return [setting, "", ""]; } function initDropdown(name, playerIdx) @@ -1239,45 +1372,52 @@ Engine.ConfigDB_WriteValueToFile("user", "gui.gamesetup.enabletips", enabled, "config/user.cfg"); } -function verticallyDistributeGUIObjects(parent, objectHeight, ignore) +/** + * Distribute the currently visible settings over the settings panel. + * First calculate the number of columns required, then place the objects. + */ +function distributeSettings() { - let yPos; + let settingsPanel = Engine.GetGUIObjectByName("settingsPanel"); + let actualSettingsPanelSize = settingsPanel.getComputedSize(); - let parentObject = Engine.GetGUIObjectByName(parent); - for (let child of parentObject.children) - { - if (ignore.indexOf(child.name) != -1) - continue; + let maxPerColumn = Math.floor((actualSettingsPanelSize.bottom - actualSettingsPanelSize.top) / g_SettingHeight); + let childs = 0; + for (let child of settingsPanel.children) + if (child.hidden) + ++childs; - let childSize = child.size; - yPos = yPos || childSize.top; + let perColumn = childs / Math.ceil(childs / maxPerColumn); + let yPos = g_SettingDist; + let column = 0; + let thisColumn = 0; + let settingsPanelSize = settingsPanel.size; + for (let child of settingsPanel.children) + { if (child.hidden) continue; - childSize.top = yPos; - childSize.bottom = yPos + objectHeight - 2; - child.size = childSize; - - yPos += objectHeight; - } - return yPos; -} + if (thisColumn >= perColumn) + { + yPos = g_SettingDist; + ++column; + thisColumn = 0; + } -/** - * Remove empty space in case of hidden options (like cheats, rating or victory duration) - */ -function resizeMoreOptionsWindow() -{ - verticallyDistributeGUIObjects("mapOptions", 32, []); + let childSize = child.size; + child.size = new GUISize( + column * g_ColumnWidth, + yPos, + column * g_ColumnWidth + g_ColumnWidth - 10, + yPos + g_SettingHeight - g_SettingDist); - let yPos = verticallyDistributeGUIObjects("moreOptions", 32, ["moreOptionsLabel"]); + yPos += g_SettingHeight; + ++thisColumn; + } - // Resize the vertically centered window containing the options - let moreOptions = Engine.GetGUIObjectByName("moreOptions"); - let mSize = moreOptions.size; - mSize.bottom = mSize.top + yPos + 20; - moreOptions.size = mSize; + settingsPanelSize.right = settingsPanelSize.left + (column + 1) * g_ColumnWidth; + settingsPanel.size = settingsPanelSize; } /** @@ -1572,6 +1712,7 @@ })))); initDropdown("biome"); + updateGUIDropdown("biome"); } function reloadTriggerDifficulties() @@ -1601,6 +1742,7 @@ }))); initDropdown("triggerDifficulty"); + updateGUIDropdown("triggerDifficulty"); } function reloadGameSpeedChoices() @@ -1763,6 +1905,12 @@ handleNetMessages(); updateTimers(); + + let now = Date.now(); + let tickLength = now - g_LastTickTime; + g_LastTickTime = now; + + updateSettingsPanelPosition(tickLength); } /** @@ -1872,10 +2020,11 @@ let indexHidden = isControlArrayElementHidden(playerIdx); let obj = (playerIdx === undefined ? g_Dropdowns : g_PlayerDropdowns)[name]; - let selected = indexHidden ? -1 : dropdown.list_data.indexOf(String(obj.get(playerIdx))); - let enabled = !indexHidden && (!obj.enabled || obj.enabled(playerIdx)); let hidden = indexHidden || obj.hidden && obj.hidden(playerIdx); + let selected = hidden ? -1 : dropdown.list_data.indexOf(String(obj.get(playerIdx))); + let enabled = !indexHidden && (!obj.enabled || obj.enabled(playerIdx)); + dropdown.enabled = g_IsController && enabled; dropdown.hidden = !g_IsController || !enabled || hidden; dropdown.selected = selected; dropdown.tooltip = !indexHidden && obj.tooltip ? obj.tooltip(-1, playerIdx) : ""; @@ -1884,12 +2033,12 @@ frame.hidden = hidden; if (title && obj.title && !indexHidden) - title.caption = sprintf(translate("%(option)s:"), { "option": obj.title(playerIdx) }); + title.caption = sprintf(translateWithContext("Title for specific setting", "%(setting)s:"), { "setting": obj.title(playerIdx) }); if (label && !indexHidden) { label.hidden = g_IsController && enabled || hidden; - label.caption = selected == -1 ? translateWithContext("option value", "Unknown") : dropdown.list[selected]; + label.caption = selected == -1 ? translateWithContext("settings value", "Unknown") : dropdown.list[selected]; } } @@ -1915,7 +2064,7 @@ Engine.GetGUIObjectByName(guiName + "Dropdown" + guiIdx).hidden = true; checkbox.checked = checked; - checkbox.enabled = enabled; + checkbox.enabled = g_IsController && enabled; checkbox.hidden = hidden || !g_IsController; checkbox.tooltip = obj.tooltip ? obj.tooltip() : ""; @@ -1926,7 +2075,7 @@ frame.hidden = hidden; if (title && obj.title) - title.caption = sprintf(translate("%(option)s:"), { "option": obj.title() }); + title.caption = sprintf(translate("%(setting)s:"), { "setting": obj.title() }); } function updateGUIMiscControl(name, playerIdx) @@ -2081,16 +2230,20 @@ reloadPlayerAssignmentChoices(); // Hide exceeding dropdowns and checkboxes - for (let panel in g_OptionOrderGUI) - for (let child of Engine.GetGUIObjectByName(panel + "Options").children) - child.hidden = true; + for (let setting of Engine.GetGUIObjectByName("settingsPanel").children) + setting.hidden = true; // Show the relevant ones - for (let name in g_Dropdowns) - updateGUIDropdown(name); - - for (let name in g_Checkboxes) - updateGUICheckbox(name); + if (g_TabCategorySelected !== undefined) + { + for (let name in g_Dropdowns) + if (g_SettingsTabsGUI[g_TabCategorySelected].settings.indexOf(name) != -1) + updateGUIDropdown(name); + + for (let name in g_Checkboxes) + if (g_SettingsTabsGUI[g_TabCategorySelected].settings.indexOf(name) != -1) + updateGUICheckbox(name); + } for (let i = 0; i < g_MaxPlayers; ++i) { @@ -2105,7 +2258,7 @@ updateGUIMiscControl(name); updateGameDescription(); - resizeMoreOptionsWindow(); + distributeSettings(); rightAlignCancelButton(); updateAutocompleteEntries(); @@ -2358,12 +2511,6 @@ Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n"); } -function showMoreOptions(show) -{ - Engine.GetGUIObjectByName("moreOptionsFade").hidden = !show; - Engine.GetGUIObjectByName("moreOptions").hidden = !show; -} - function resetCivilizations() { for (let i in g_GameAttributes.settings.PlayerData) Index: binaries/data/mods/public/gui/gamesetup/gamesetup.xml =================================================================== --- binaries/data/mods/public/gui/gamesetup/gamesetup.xml +++ binaries/data/mods/public/gui/gamesetup/gamesetup.xml @@ -29,6 +29,10 @@ onTick(); + + + distributeSettings(); + @@ -125,7 +129,7 @@ -