Index: ps/trunk/binaries/data/mods/public/gui/common/settings.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/settings.js +++ ps/trunk/binaries/data/mods/public/gui/common/settings.js @@ -186,15 +186,18 @@ { "Name": "skirmish", "Title": translateWithContext("map", "Skirmish"), + "Description": translate("A map with a predefined landscape and number of players. Freely select the other gamesettings."), "Default": true }, { "Name": "random", - "Title": translateWithContext("map", "Random") + "Title": translateWithContext("map", "Random"), + "Description": translate("Create a unique map with a different resource distribution each time. Freely chose the number of players and teams.") }, { "Name": "scenario", - "Title": translateWithContext("map", "Scenario") + "Title": translateWithContext("map", "Scenario"), + "Description": translate("A map with a predefined landscape and matchsettings.") } ]; } Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -358,7 +358,7 @@ * * GUI * title - The caption shown in the label. - * tooltip - A description shown when hovering the option. + * tooltip - A description shown when hovering the dropdown or a specific item. * labels - Array of translated strings selectable for this dropdown. * colors - Optional array of colors to tint the according dropdown items with. * hidden - If hidden, both the label and dropdown won't be visible. (default: false) @@ -369,7 +369,7 @@ var g_Dropdowns = { "mapType": { "title": () => translate("Map Type"), - "tooltip": () => translate("Select a map type."), + "tooltip": (hoverIdx) => g_MapTypes.Description[hoverIdx] || translate("Select a map type."), "labels": () => g_MapTypes.Title, "ids": () => g_MapTypes.Name, "default": () => g_MapTypes.Default, @@ -392,7 +392,7 @@ }, "mapFilter": { "title": () => translate("Map Filter"), - "tooltip": () => translate("Select a map filter."), + "tooltip": (hoverIdx) => translate("Select a map filter."), "labels": () => g_MapFilterList.name, "ids": () => g_MapFilterList.id, "default": () => g_MapFilterList.Default, @@ -407,7 +407,7 @@ }, "mapSelection": { "title": () => translate("Select Map"), - "tooltip": () => translate("Select a map to play on."), + "tooltip": (hoverIdx) => translate("Select a map to play on."), "labels": () => g_MapSelectionList.name, "colors": () => g_MapSelectionList.color, "ids": () => g_MapSelectionList.file, @@ -421,7 +421,7 @@ }, "mapSize": { "title": () => translate("Map Size"), - "tooltip": () => translate("Select map size. (Larger sizes may reduce performance.)"), + "tooltip": (hoverIdx) => translate("Select map size. (Larger sizes may reduce performance.)"), "labels": () => g_MapSizes.Name, "ids": () => g_MapSizes.Tiles, "default": () => g_MapSizes.Default, @@ -435,7 +435,7 @@ }, "numPlayers": { "title": () => translate("Number of Players"), - "tooltip": () => translate("Select number of players."), + "tooltip": (hoverIdx) => translate("Select number of players."), "labels": () => g_NumPlayersList, "ids": () => g_NumPlayersList, "default": () => g_MaxPlayers - 1, @@ -455,7 +455,7 @@ }, "populationCap": { "title": () => translate("Population Cap"), - "tooltip": () => translate("Select population cap."), + "tooltip": (hoverIdx) => translate("Select population cap."), "labels": () => g_PopulationCapacities.Title, "ids": () => g_PopulationCapacities.Population, "default": () => g_PopulationCapacities.Default, @@ -468,7 +468,13 @@ }, "startingResources": { "title": () => translate("Starting Resources"), - "tooltip": () => translate("Select the game's starting resources."), + "tooltip": (hoverIdx) => { + return hoverIdx >= 0 ? + sprintf(translate("Initial amount of each resource: %(resources)s."), { + "resources": g_StartingResources.Resources[hoverIdx] + }) : + translate("Select the game's starting resources."); + }, "labels": () => g_StartingResources.Title, "ids": () => g_StartingResources.Resources, "default": () => g_StartingResources.Default, @@ -482,7 +488,7 @@ }, "ceasefire": { "title": () => translate("Ceasefire"), - "tooltip": () => translate("Set time where no attacks are possible."), + "tooltip": (hoverIdx) => translate("Set time where no attacks are possible."), "labels": () => g_Ceasefire.Title, "ids": () => g_Ceasefire.Duration, "default": () => g_Ceasefire.Default, @@ -495,7 +501,7 @@ }, "victoryCondition": { "title": () => translate("Victory Condition"), - "tooltip": () => translate("Select victory condition."), + "tooltip": (hoverIdx) => g_VictoryConditions.Description[hoverIdx] || translate("Select victory condition."), "labels": () => g_VictoryConditions.Title, "ids": () => g_VictoryConditions.Name, "default": () => g_VictoryConditions.Default, @@ -510,7 +516,7 @@ }, "relicCount": { "title": () => translate("Relic Count"), - "tooltip": () => translate("Total number of relics spawned on the map."), + "tooltip": (hoverIdx) => translate("Total number of relics spawned on the map."), "labels": () => g_RelicCountList, "ids": () => g_RelicCountList, "default": () => g_RelicCountList.indexOf(5), @@ -524,7 +530,7 @@ }, "victoryDuration": { "title": () => translate("Victory Duration"), - "tooltip": () => translate("Number of minutes until the player has won."), + "tooltip": (hoverIdx) => translate("Number of minutes until the player has won."), "labels": () => g_VictoryDurations.Title, "ids": () => g_VictoryDurations.Duration, "default": () => g_VictoryDurations.Default, @@ -540,7 +546,7 @@ }, "gameSpeed": { "title": () => translate("Game Speed"), - "tooltip": () => translate("Select game speed."), + "tooltip": (hoverIdx) => translate("Select game speed."), "labels": () => g_GameSpeeds.Title, "ids": () => g_GameSpeeds.Speed, "default": () => g_GameSpeeds.Default, @@ -777,7 +783,7 @@ "startGame": { "caption": () => g_IsController ? translate("Start game!") : g_ReadyData[g_IsReady].caption, - "tooltip": () => + "tooltip": (hoverIdx) => !g_IsController ? g_ReadyData[g_IsReady].tooltip : !g_IsNetworked || Object.keys(g_PlayerAssignments).every(guid => @@ -1005,6 +1011,11 @@ supplementDefaults(); updateGameAttributes(); }; + + if (data.tooltip) + dropdown.onHoverChange = function() { + this.tooltip = data.tooltip(this.hovered); + }; } function initPlayerDropdowns(name) Index: ps/trunk/source/gui/CList.h =================================================================== --- ps/trunk/source/gui/CList.h +++ ps/trunk/source/gui/CList.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -105,6 +105,8 @@ */ std::vector m_ItemsYPositions; + virtual int GetHoveredItem(); + private: // Whether the list's items have been modified since last handling a message. bool m_Modified; Index: ps/trunk/source/gui/CList.cpp =================================================================== --- ps/trunk/source/gui/CList.cpp +++ ps/trunk/source/gui/CList.cpp @@ -45,16 +45,17 @@ AddSetting(GUIST_CColor, "textcolor"); AddSetting(GUIST_CColor, "textcolor_selected"); AddSetting(GUIST_int, "selected"); // Index selected. -1 is none. + AddSetting(GUIST_int, "hovered"); AddSetting(GUIST_CStrW, "tooltip"); AddSetting(GUIST_CStr, "tooltip_style"); + // Each list item has both a name (in 'list') and an associated data string (in 'list_data') AddSetting(GUIST_CGUIList, "list"); AddSetting(GUIST_CGUIList, "list_data"); // TODO: this should be a list of raw strings, not of CGUIStrings GUI::SetSetting(this, "scrollbar", false); - - // Nothing is selected as default. GUI::SetSetting(this, "selected", -1); + GUI::SetSetting(this, "hovered", -1); // Add scroll-bar CGUIScrollBarVertical* bar = new CGUIScrollBarVertical(); @@ -194,45 +195,46 @@ break; } - bool scrollbar; - CGUIList* pList; - GUI::GetSetting(this, "scrollbar", scrollbar); - GUI::GetSettingPointer(this, "list", pList); - float scroll = 0.f; - if (scrollbar) - scroll = GetScrollBar(0).GetPos(); + int hovered = GetHoveredItem(); + if (hovered == -1) + break; + GUI::SetSetting(this, "selected", hovered); + UpdateAutoScroll(); - CRect rect = GetListRect(); - CPos mouse = GetMousePos(); - mouse.y += scroll; - int set = -1; - for (int i = 0; i < (int)pList->m_Items.size(); ++i) - { - if (mouse.y >= rect.top + m_ItemsYPositions[i] && - mouse.y < rect.top + m_ItemsYPositions[i+1] && - // mouse is not over scroll-bar - (!scrollbar || !GetScrollBar(0).IsVisible() || - mouse.x < GetScrollBar(0).GetOuterRect().left || - mouse.x > GetScrollBar(0).GetOuterRect().right)) - { - set = i; - } - } + CStrW soundPath; + if (g_SoundManager && GUI::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty()) + g_SoundManager->PlayAsUI(soundPath.c_str(), false); - if (set != -1) - { - GUI::SetSetting(this, "selected", set); - UpdateAutoScroll(); + if (timer_Time() - m_LastItemClickTime < SELECT_DBLCLICK_RATE && hovered == m_PrevSelectedItem) + this->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT_ITEM, "mouseleftdoubleclickitem"); + m_LastItemClickTime = timer_Time(); + m_PrevSelectedItem = hovered; + break; + } - CStrW soundPath; - if (g_SoundManager && GUI::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty()) - g_SoundManager->PlayAsUI(soundPath.c_str(), false); + case GUIM_MOUSE_LEAVE: + { + int hoveredSetting = -1; + GUI::GetSetting(this, "hovered", hoveredSetting); + if (hoveredSetting == -1) + break; - if (timer_Time() - m_LastItemClickTime < SELECT_DBLCLICK_RATE && set == m_PrevSelectedItem) - this->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT_ITEM, "mouseleftdoubleclickitem"); - m_LastItemClickTime = timer_Time(); - m_PrevSelectedItem = set; - } + GUI::SetSetting(this, "hovered", -1); + ScriptEvent("hoverchange"); + break; + } + + case GUIM_MOUSE_OVER: + { + int hoveredSetting = -1; + GUI::GetSetting(this, "hovered", hoveredSetting); + + int hovered = GetHoveredItem(); + if (hovered == hoveredSetting) + break; + + GUI::SetSetting(this, "hovered", hovered); + ScriptEvent("hoverchange"); break; } @@ -515,3 +517,31 @@ if (m_ItemsYPositions[selected+1]-rect.GetHeight() > scroll) GetScrollBar(0).SetPos(m_ItemsYPositions[selected+1]-rect.GetHeight()); } + +int CList::GetHoveredItem() +{ + bool scrollbar; + CGUIList* pList; + GUI::GetSetting(this, "scrollbar", scrollbar); + GUI::GetSettingPointer(this, "list", pList); + float scroll = 0.f; + if (scrollbar) + scroll = GetScrollBar(0).GetPos(); + + CRect rect = GetListRect(); + CPos mouse = GetMousePos(); + mouse.y += scroll; + + // Mouse is over scrollbar + if (scrollbar && GetScrollBar(0).IsVisible() && + mouse.x >= GetScrollBar(0).GetOuterRect().left && + mouse.x <= GetScrollBar(0).GetOuterRect().right) + return -1; + + for (size_t i = 0; i < pList->m_Items.size(); ++i) + if (mouse.y >= rect.top + m_ItemsYPositions[i] && + mouse.y < rect.top + m_ItemsYPositions[i + 1]) + return i; + + return -1; +}