From 4c73606fb38e096a1b69670c74959cf664fd2272 Mon Sep 17 00:00:00 2001 From: Charles Dang Date: Sun, 29 Oct 2017 16:49:27 +1100 Subject: [PATCH] MP Lobby: completely redesigned game entry layout I did one of these about a year ago, but in the time since I realize it was honestly really crowded and had some fundamental problems (for example, the listbox would get a horizontal scrollbar if a game with a lot of mods was started). This time around, I've focused on simplicity. The main changes: * The minimap has been made 10 px larger. This was to accommodate the 5 px borders, so now the actual map image is 72x72. * Game name and scenario name have swapped places, and the latter is now larger. * Game names are no longer colorized based on the number of vacant slots or whether you can observe the game or not. * The Turns/Slots label coloring has been toned down and been made larger. It is now either white (game has started), green (vacant slots available), or yellow (vacant slots available for reloaded game). * The Turns limit no longer displays "/-" for games with unlimited turns. Now you just get "Turn n". * Due to 1bfa170856ed, proper names of all missing content is displayed. I also got rid of those "Unknown Scenario"/"Unknown Campaign" labels in favor of a simple red-color game type token (S or C), respectively. * All game setting icons have been moved to a tooltip attached to an info icon, except those for Password Required and Observes Allowed. * The info icon will also change color (and its tooltip display a message) if the player need to download additional content to join that game. --- changelog | 1 + data/gui/window/lobby_main.cfg | 227 +++++++++---------------- images/icons/icon-info-error.png | Bin 0 -> 827 bytes images/icons/icon-info.png | Bin 0 -> 847 bytes src/game_initialization/lobby_data.cpp | 41 +++-- src/gui/dialogs/multiplayer/lobby.cpp | 197 +++++++++++---------- src/server/game.cpp | 10 +- 7 files changed, 216 insertions(+), 260 deletions(-) create mode 100644 images/icons/icon-info-error.png create mode 100644 images/icons/icon-info.png diff --git a/changelog b/changelog index 2d990b14bec01..092df9aea3f08 100644 --- a/changelog +++ b/changelog @@ -5,6 +5,7 @@ Version 1.13.10+dev: * Removed broken Unit Box and Widescreen themes. * Fixed a bug that partially prevented movement feedback announce messages to be displayed (UI regression bug #2130, affecting 1.13.8 and 1.13.10). + * New, greatly simplified display of games in the MP Lobby. * WML Engine: * File paths with backslashes are no longer allowed. This ensures that a UMC author can't accidentally use them and make an add-on that breaks on diff --git a/data/gui/window/lobby_main.cfg b/data/gui/window/lobby_main.cfg index e63e72ea7f426..e991cab619ee3 100644 --- a/data/gui/window/lobby_main.cfg +++ b/data/gui/window/lobby_main.cfg @@ -3,42 +3,6 @@ ### Definition of the lobby screen ### -#define GAMELISTBOX_BODY_LABEL_TINY ID LABEL - [column] - border = "right" - border_size = 5 - [label] - id = {ID} - definition = "default_tiny" - label = {LABEL} - [/label] - [/column] -#enddef - -#define GAMELISTBOX_BODY_IMAGE ID LABEL TOOLTIP GROW_FACTOR - [column] - grow_factor = {GROW_FACTOR} - horizontal_grow = true - border = "all" - border_size = 5 - [image] - id = {ID} - definition = "default" - label = {LABEL} - tooltip = {TOOLTIP} - [/image] - [/column] -#enddef - -#define GAMELISTBOX_LABEL LABEL - [column] - [label] - definition = "default" - label = {LABEL} - [/label] - [/column] -#enddef - #define GAMELISTBOX [listbox] id = "game_list" @@ -46,13 +10,17 @@ vertical_scrollbar_mode = "always" horizontal_scrollbar_mode = "auto" + [header] + [row] + [column] grow_factor = 1 horizontal_grow = true border = "bottom,right" border_size = 5 + [label] id = "map" definition = "default" @@ -61,18 +29,27 @@ [/column] [/row] [/header] + [list_definition] + [row] + [column] horizontal_grow = true + [toggle_panel] id = "panel" definition = "default" + [grid] + [row] + [column] grow_factor = 0 - {GUI_FORCE_WIDGET_MINIMUM_SIZE 72 72 ( + vertical_alignment = "center" + + {GUI_FORCE_WIDGET_MINIMUM_SIZE 82 82 ( border = "all" border_size = 5 [minimap] @@ -81,165 +58,99 @@ [/minimap] )} [/column] + [column] grow_factor = 1 horizontal_grow = true + [grid] + [row] + [column] grow_factor = 1 border = "all" border_size = 3 horizontal_alignment = "left" + [label] - id = "name" - definition = "default" + id = "scenario" + definition = "default_large" [/label] [/column] [/row] [row] + [column] grow_factor = 1 horizontal_grow = true border = "left,right" border_size = 3 + [label] - id = "scenario" + id = "name" definition = "default" [/label] [/column] [/row] - [row] - [column] - grow_factor = 1 - horizontal_grow = true + [/grid] - [grid] - - [row] - grow_factor = 1 - - [column] - grow_factor = 0 - horizontal_grow = true - border = "left,top,bottom" - border_size = 3 - [label] - id = "era_label" - definition = "default_small" - label = _ "Era:" - [/label] - [/column] - - [column] - grow_factor = 0 - horizontal_grow = true - border = "all" - border_size = 3 - [label] - id = "era" - definition = "default_small" - [/label] - [/column] - - [column] - grow_factor = 0 - horizontal_grow = true - border = "all" - border_size = 3 - [label] - id = "dash" - definition = "default" - label = " — " - [/label] - [/column] - - [column] - grow_factor = 0 - horizontal_grow = true - border = "left,top,bottom" - border_size = 3 - [label] - id = "mods_label" - definition = "default_small" - label = _ "Modifications:" - [/label] - [/column] - - [column] - grow_factor = 1 - horizontal_grow = true - border = "all" - border_size = 3 - [label] - id = "mods" - definition = "default_small" - [/label] - [/column] - [/row] - [/grid] + [/column] - [/column] - [/row] - [/grid] + [column] + grow_factor = 0 + border = "all" + border_size = 5 + horizontal_alignment = "right" + [label] + id = "status" + definition = "default_large" + [/label] [/column] + [column] grow_factor = 0 + border = "all" + border_size = 5 horizontal_alignment = "right" + vertical_grow = true + + [image] + id = "game_info" + definition = "centered" + label = "icons/icon-info.png" + [/image] + [/column] + + [column] + horizontal_alignment = "right" + [grid] + [row] [column] grow_factor = 0 - border = "left,right,bottom" + horizontal_grow = true + border = "all" border_size = 5 - horizontal_alignment = "right" - [label] - id = "status" + + [image] + id = "needs_password" definition = "default" - [/label] + label = "misc/key.png" + tooltip = _ "Requires a password to join" + [/image] [/column] - [/row] - - [row] - [column] - horizontal_grow = true - [grid] - [row] - {GAMELISTBOX_BODY_IMAGE "map_size_icon" "misc/map.png" "" 0} - {GAMELISTBOX_BODY_LABEL_TINY "map_size_text" "900x900"} - {GAMELISTBOX_BODY_IMAGE "observer_icon" "misc/eye.png" "" 0} - {GAMELISTBOX_BODY_IMAGE "shuffle_sides_icon" "misc/shuffle-sides.png" _"Assign sides to players at random" 0} - {GAMELISTBOX_BODY_IMAGE "needs_password" "misc/key.png" _"Requires a password to join" 0} - {GAMELISTBOX_BODY_IMAGE "registered_only" "misc/registered_users_only.png" _"Only registered users may join" 0} - {GAMELISTBOX_BODY_IMAGE "use_map_settings" "misc/ums.png" _"Use map settings" 0} - {GAMELISTBOX_BODY_IMAGE "reloaded" "misc/reloaded.png" _"Reloaded game" 0} - {GAMELISTBOX_BODY_IMAGE "no_era" "misc/qmark.png" _"Unknown era" 0} - {GAMELISTBOX_BODY_IMAGE "vision_icon" "" "" 0} - {GAMELISTBOX_BODY_IMAGE "gold_icon" "themes/gold.png" _"Gold per village" 0} - {GAMELISTBOX_BODY_LABEL_TINY "gold_text" "265"} - {GAMELISTBOX_BODY_IMAGE "xp_icon" "themes/units.png" _"Experience modifier" 0} - {GAMELISTBOX_BODY_LABEL_TINY "xp_text" "70%"} - {GAMELISTBOX_BODY_IMAGE "time_limit_icon" "themes/sand-clock.png" _"Time limit" 0} - {GAMELISTBOX_BODY_LABEL_TINY "time_limit_text" ""} - [/row] - [/grid] - [/column] - [/row] - [/grid] - [/column] - [column] - horizontal_alignment = "right" - [grid] - [row] [column] grow_factor = 1 horizontal_alignment = "right" border = "top,left,right" border_size = 5 + [button] id = "join" definition = "default" @@ -247,12 +158,28 @@ [/button] [/column] [/row] + [row] + + [column] + grow_factor = 0 + horizontal_grow = true + border = "all" + border_size = 5 + + [image] + id = "observer_icon" + definition = "default" + label = "misc/eye.png" + [/image] + [/column] + [column] grow_factor = 1 horizontal_alignment = "right" border = "all" border_size = 5 + [button] id = "observe" definition = "default" @@ -886,7 +813,7 @@ [/column] [/row] - {GUI_HORIZONTAL_SPACER_LINE} + #{GUI_HORIZONTAL_SPACER_LINE} [row] grow_factor = 1 diff --git a/images/icons/icon-info-error.png b/images/icons/icon-info-error.png new file mode 100644 index 0000000000000000000000000000000000000000..dcf83b3b3246a920f510efdf8a9869bfa2410e68 GIT binary patch literal 827 zcmV-B1H}A^P)P000>X1^@s6#OZ}&00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru;R+iD8U$~t=*R#703B&m zSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*00N&$L_t(Y$F0^)h*f16 z2k_rHGo?cyWHJM~m>?@$42AF}1);TUQ3xUiy3ti1i$qP8x;hh#=K0v4E+h=eZZM-O_i``dcUu;JrgF67?;2OMz9R+WXH_x{% zrwe!m<9Jt9ue%$hY4Tni>Icw-ceBF>IN#x9o11r)FQ*!>VH@_}3DWdoS2X$PQd}6L zRd5nJ3qBm9HGq>J<0I@=)l?Oyh4{xySt9>iZpJe?{qMFkS|YAHg5EW_9dZ1U!kd z?D=zjO!8{jL8X&4`?YCI;X66CyAJ0=Ou;;`azUr3Y{~j~}eSs&jM^2xN zvKgO~4c`vHdU_+}sM4pyWK&mvHAZb|?E002ovPDHLk FV1gR!Z{+|0 literal 0 HcmV?d00001 diff --git a/images/icons/icon-info.png b/images/icons/icon-info.png new file mode 100644 index 0000000000000000000000000000000000000000..9dcf0ab44eaaa71095747271c6595ae371fb04c4 GIT binary patch literal 847 zcmV-V1F-ywP)P000>X1^@s6#OZ}&00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru;R+iD4L8;L4M+e003B&m zSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*00Of~L_t(Y$F0^)h*o73 z2k_r>XG(`a$Ycg|F+t;miy;wiQV>?l7KI>Upc`HFvA9Irz@01cx^q#`#-^l&5e7Vr7+(K#@27 z{+9Z1ycyfE13R$=H!*`*%qjAl>jx>T7jOX2U^tx5;bClx_sh6|bBg?S{~%@c5KiGR zuHq~%;g@hc6rj6t2v6f8URUJT?hVpT&qd@ofzy~*E?*5t(NKLh${=L_1F~t!Y*Z%vXF{V{D~X)=>LD0JbT$6 zk@^V?W^C?-o|So*XNa0zRLVbJpew#V<;5)Pb;f%kGde`neG&=^|mq+YPu`HIv0v>@I9{K=hd!AX${OtG4-;jS>5m5 Z>pwKL+8TH8eiHxy002ovPDHLkV1j)mfs_CM literal 0 HcmV?d00001 diff --git a/src/game_initialization/lobby_data.cpp b/src/game_initialization/lobby_data.cpp index 3d500efd23d7e..2f7d69074211f 100644 --- a/src/game_initialization/lobby_data.cpp +++ b/src/game_initialization/lobby_data.cpp @@ -147,8 +147,12 @@ void user_info::update_relation() } } +namespace +{ +const std::string spaced_em_dash = " " + font::unicode_em_dash + " "; + // Returns an abbreviated form of the provided string - ie, 'Ageless Era' should become 'AE' -static std::string make_short_name(const std::string& long_name) +std::string make_short_name(const std::string& long_name) { if(long_name.empty()) { return ""; @@ -170,6 +174,17 @@ static std::string make_short_name(const std::string& long_name) return ss.str(); } +std::string make_game_type_marker(std::string text, bool color_for_missing) +{ + if(color_for_missing) { + return formatter() << "[" << text << "] "; + } else { + return formatter() << "[" << text << "] "; + } +} + +} // end anon namespace + game_info::game_info(const config& game, const config& game_config, const std::vector& installed_addons) : id(game["id"]) , map_data(game["map_data"]) @@ -248,7 +263,7 @@ game_info::game_info(const config& game, const config& game_config, const std::v addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far } else { have_era = !game["require_era"].to_bool(true); - era = vgettext("Unknown era: $era_name", {{"era_name", game["mp_era_name"].str()}}); + era = vgettext("$era_name (missing)", {{"era_name", game["mp_era_name"].str()}}); era_short = make_short_name(era); verified = false; @@ -279,7 +294,6 @@ game_info::game_info(const config& game, const config& game_config, const std::v } } - if(map_data.empty()) { map_data = filesystem::read_map(game["mp_scenario"]); } @@ -292,7 +306,7 @@ game_info::game_info(const config& game, const config& game_config, const std::v std::ostringstream msi; msi << map.w() << font::unicode_multiplication_sign << map.h(); map_size_info = msi.str(); - info_stream << " — " + map_size_info; + info_stream << spaced_em_dash << map_size_info; } catch(incorrect_map_format_error& e) { ERR_CF << "illegal map: " << e.message << std::endl; verified = false; @@ -301,6 +315,7 @@ game_info::game_info(const config& game, const config& game_config, const std::v verified = false; } } + info_stream << " "; // @@ -316,7 +331,7 @@ game_info::game_info(const config& game, const config& game_config, const std::v } if(*level_cfg) { - scenario = formatter() << "" << _("(S)") << "" << " " << (*level_cfg)["name"].str(); + scenario = formatter() << make_game_type_marker(_("S"), false) << (*level_cfg)["name"].str(); info_stream << scenario; // Reloaded games do not match the original scenario hash, so it makes no sense @@ -334,7 +349,7 @@ game_info::game_info(const config& game, const config& game_config, const std::v if(!hash_found) { remote_scenario = true; - info_stream << " — "; + info_stream << spaced_em_dash; info_stream << _("Remote scenario"); verified = false; } @@ -346,7 +361,7 @@ game_info::game_info(const config& game, const config& game_config, const std::v addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far } } else { - scenario = vgettext("Unknown scenario: $scenario_id", {{"scenario_id", game["mp_scenario_name"].str()}}); + scenario = formatter() << make_game_type_marker(_("S"), true) << game["mp_scenario_name"].str(); info_stream << scenario; verified = false; } @@ -354,15 +369,15 @@ game_info::game_info(const config& game, const config& game_config, const std::v if(const config& campaign_cfg = game_config.find_child("campaign", "id", game["mp_campaign"])) { std::stringstream campaign_text; campaign_text - << "" << _("(C)") << "" << " " - << campaign_cfg["name"] << " — " + << make_game_type_marker(_("C"), false) + << campaign_cfg["name"] << spaced_em_dash << game["mp_scenario_name"]; // Difficulty config difficulties = gui2::dialogs::generate_difficulty_config(campaign_cfg); for(const config& difficulty : difficulties.child_range("difficulty")) { if(difficulty["define"] == game["difficulty_define"]) { - campaign_text << " — " << difficulty["description"]; + campaign_text << spaced_em_dash << difficulty["description"]; break; } @@ -377,7 +392,7 @@ game_info::game_info(const config& game, const config& game_config, const std::v addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far //} } else { - scenario = vgettext("Unknown campaign: $campaign_id", {{"campaign_id", game["mp_campaign_name"].str()}}); + scenario = formatter() << make_game_type_marker(_("C"), true) << game["mp_campaign_name"].str(); info_stream << scenario; verified = false; } @@ -391,7 +406,7 @@ game_info::game_info(const config& game, const config& game_config, const std::v boost::replace_all(scenario, "\n", " " + font::unicode_em_dash + " "); if(reloaded) { - info_stream << " — "; + info_stream << spaced_em_dash; info_stream << _("Reloaded game"); verified = false; } @@ -430,6 +445,8 @@ game_info::game_info(const config& game, const config& game_config, const std::v << game["mp_countdown_init_time"].str() << "+" << game["mp_countdown_turn_bonus"].str() << "/" << game["mp_countdown_action_bonus"].str(); + } else { + time_limit = _("none"); } map_info = info_stream.str(); diff --git a/src/gui/dialogs/multiplayer/lobby.cpp b/src/gui/dialogs/multiplayer/lobby.cpp index b96306fcfa8c0..e895be9194dcd 100644 --- a/src/gui/dialogs/multiplayer/lobby.cpp +++ b/src/gui/dialogs/multiplayer/lobby.cpp @@ -193,24 +193,6 @@ void mp_lobby::post_build(window& win) namespace { - -void add_label_data(std::map& map, - const std::string& key, - const std::string& label) -{ - string_map item; - item["label"] = label; - item["use_markup"] = "true"; - map.emplace(key, item); -} - -void add_tooltip_data(std::map& map, - const std::string& key, - const std::string& label) -{ - map[key]["tooltip"] = label; -} - void modify_grid_with_data(grid* grid, const std::map& map) { for(const auto& v : map) { @@ -240,12 +222,17 @@ void modify_grid_with_data(grid* grid, const std::map& void set_visible_if_exists(grid* grid, const std::string& id, bool visible) { if(widget* w = grid->find(id, false)) { - w->set_visible(visible ? widget::visibility::visible : widget::visibility::invisible); + //w->set_visible(visible ? widget::visibility::visible : widget::visibility::invisible); + w->set_visible(visible ? widget::visibility::visible : widget::visibility::hidden); } } std::string colorize(const std::string& str, const std::string& color) { + if(color.empty()) { + return str; + } + return (formatter() << "" << str << "").str(); } @@ -401,106 +388,130 @@ void mp_lobby::update_gamelist_header() std::map mp_lobby::make_game_row_data(const mp::game_info& game) { std::map data; + string_map item; + + item["use_markup"] = "true"; std::string color_string; if(game.vacant_slots > 0) { color_string = (game.reloaded || game.started) ? "yellow" : "green"; - } else { - color_string = game.observers ? "#ddd" : "red"; } - if(!game.have_era && (game.vacant_slots > 0 || game.observers)) { - color_string = "#444"; - } + item["label"] = colorize("" + game.name + "", font::GRAY_COLOR.to_hex_string()); + data.emplace("name", item); - add_label_data(data, "status", colorize(game.status, color_string)); - add_label_data(data, "name", colorize(game.name, color_string)); - - add_label_data(data, "era", colorize(game.era, "#a69275")); - add_label_data(data, "era_short", game.era_short); - add_label_data(data, "mods", colorize(game.mod_info, "#a69275")); - add_label_data(data, "map_info", game.map_info); - add_label_data(data, "scenario", game.scenario); - add_label_data(data, "map_size_text", game.map_size_info); - add_label_data(data, "time_limit", game.time_limit); - add_label_data(data, "gold_text", game.gold); - add_label_data(data, "xp_text", game.xp); - add_label_data(data, "vision_text", game.vision); - add_label_data(data, "time_limit_text", game.time_limit); - add_label_data(data, "status", game.status); + item["label"] = game.scenario; + data.emplace("scenario", item); - if(game.observers) { - add_label_data(data, "observer_icon", "misc/eye.png"); - add_tooltip_data(data, "observer_icon", _("Observers allowed")); - } else { - add_label_data(data, "observer_icon", "misc/no_observer.png"); - add_tooltip_data(data, "observer_icon", _("Observers not allowed")); - } + item["label"] = colorize(game.status, color_string); + data.emplace("status", item); - std::string vision_icon; - if(game.fog) { - vision_icon = game.shroud ? "misc/vision-fog-shroud.png" : "misc/vision-fog.png"; - } else { - vision_icon = game.shroud ? "misc/vision-shroud.png" : "misc/vision-none.png"; - } - - add_label_data(data, "vision_icon", vision_icon); - add_tooltip_data(data, "vision_icon", game.vision); return data; } -void mp_lobby::adjust_game_row_contents(const mp::game_info& game, - int idx, - grid* grid, bool add_callbacks) +void mp_lobby::adjust_game_row_contents(const mp::game_info& game, int idx, grid* grid, bool add_callbacks) { find_widget(grid, "name", false).set_use_markup(true); find_widget(grid, "status", false).set_use_markup(true); toggle_panel& row_panel = find_widget(grid, "panel", false); - connect_signal_mouse_left_double_click(row_panel, std::bind(&mp_lobby::join_or_observe, this, idx)); - - set_visible_if_exists(grid, "time_limit_icon", !game.time_limit.empty()); - set_visible_if_exists(grid, "vision_fog", game.fog); - set_visible_if_exists(grid, "vision_shroud", game.shroud); - set_visible_if_exists(grid, "vision_none", !(game.fog || game.shroud)); - set_visible_if_exists(grid, "observers_yes", game.observers); - set_visible_if_exists(grid, "shuffle_sides_icon", game.shuffle_sides); - set_visible_if_exists(grid, "observers_no", !game.observers); - set_visible_if_exists(grid, "needs_password", game.password_required); - set_visible_if_exists(grid, "reloaded", game.reloaded); - set_visible_if_exists(grid, "started", game.started); - set_visible_if_exists(grid, "use_map_settings", game.use_map_settings); - set_visible_if_exists(grid, "registered_only", game.registered_users_only); - set_visible_if_exists(grid, "no_era", !game.have_era); - - if(minimap* map = dynamic_cast(grid->find("minimap", false))) { - map->set_config(&game_config_); - map->set_map_data(game.map_data); + // + // Game info + // + std::ostringstream ss; + + ss << "" << _("Era:") << "\n" << game.era << "\n"; + + ss << "\n" << _("Modifications:") << "\n"; + + std::vector mods = utils::split(game.mod_info); + + if(mods.empty()) { + ss << _("None") << "\n"; + } else { + for(const std::string& mod : mods) { + ss << mod << "\n"; + } } - if(!add_callbacks) { - return; + // TODO: move to some general are of the code. + const auto yes_or_no = [](bool val) { return val ? _("yes") : _("no"); }; + + ss << "\n" << _("Settings:") << "\n"; + ss << _("Experience modifier:") << " " << game.xp << "\n"; + ss << _("Gold:") << " " << game.gold << "\n"; + ss << _("Map size:") << " " << game.map_size_info << "\n"; + ss << _("Registered users only:") << " " << yes_or_no(game.registered_users_only) << "\n"; + ss << _("Reloaded:") << " " << yes_or_no(game.reloaded) << "\n"; + ss << _("Shared vision:") << " " << game.vision << "\n"; + ss << _("Shuffle sides:") << " " << yes_or_no(game.shuffle_sides) << "\n"; + ss << _("Time limit:") << " " << game.time_limit << "\n"; + ss << _("Use map settings:") << " " << yes_or_no(game.use_map_settings); + + image& info_icon = find_widget(grid, "game_info", false); + + if(!game.have_era || !game.have_all_mods || !game.required_addons.empty()) { + info_icon.set_label("icons/icon-info-error.png"); + + ss << "\n\n! "; + ss << _("One or more items need to be installed\nin order to join this game."); + } else { + info_icon.set_label("icons/icon-info.png"); } - if(button* join_button = dynamic_cast(grid->find("join", false))) { - connect_signal_mouse_left_click( - *join_button, - std::bind(&mp_lobby::join_global_button_callback, - this, - std::ref(*get_window()))); - join_button->set_active(game.can_join()); + info_icon.set_tooltip(ss.str()); + + // + // Password icon + // + image& password_icon = find_widget(grid, "needs_password", false); + + if(game.password_required) { + password_icon.set_visible(widget::visibility::visible); + } else { + password_icon.set_visible(widget::visibility::hidden); } - if(button* observe_button = dynamic_cast(grid->find("observe", false))) { - connect_signal_mouse_left_click( - *observe_button, - std::bind(&mp_lobby::observe_global_button_callback, - this, - std::ref(*get_window()))); - observe_button->set_active(game.can_observe()); + // + // Observer icon + // + image& observer_icon = find_widget(grid, "observer_icon", false); + + if(game.observers) { + observer_icon.set_label("misc/eye.png"); + observer_icon.set_tooltip( _("Observers allowed")); + } else { + observer_icon.set_label("misc/no_observer.png"); + observer_icon.set_tooltip( _("Observers not allowed")); + } + + // + // Minimap + // + minimap& map = find_widget(grid, "minimap", false); + + map.set_config(&game_config_); + map.set_map_data(game.map_data); + + connect_signal_mouse_left_double_click(row_panel, + std::bind(&mp_lobby::join_or_observe, this, idx)); + + button& join_button = find_widget