The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
lobby_player_list_helper.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2009 - 2025
3 by Tomasz Sniatowski <kailoran@gmail.com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
17
20#include "gettext.hpp"
21#include "gui/widgets/label.hpp"
25
26static lg::log_domain log_lobby("lobby");
27#define ERR_LB LOG_STREAM(err, log_lobby)
28
29namespace gui2
30{
31lobby_player_list_helper::sub_list::sub_list(tree_view* parent_tree, const std::string& lbl, const bool unfolded)
32{
33 widget_data tree_group_item;
34 tree_group_item["tree_view_node_label"]["label"] = lbl;
35
36 root = &parent_tree->add_node("player_group", tree_group_item);
37
38 if(unfolded) {
39 root->unfold();
40 }
41
42 label_player_count = root->find_widget<label>("player_count", false, true);
43 assert(label_player_count);
44}
45
47{
48 label_player_count->set_label(std::to_string(root->count_children()));
49}
50
51namespace
52{
53struct update_pod
54{
55 /** The raw data used to mass-construct player tree nodes. */
56 std::vector<widget_data> node_data;
57
58 /** The associated user data for each node, index-to-index. */
59 std::vector<const mp::user_info*> user_data;
60};
61} // namespace
62
63void lobby_player_list_helper::update(const std::vector<mp::user_info>& user_info, int focused_game)
64{
65 const unsigned scrollbar_position = tree->get_vertical_scrollbar_item_position();
66 std::array<update_pod, std::tuple_size<decltype(player_lists)>::value> inputs{};
67
68 for(const auto& user : user_info) {
69 std::string name = user.name;
70
71 std::stringstream icon_ss;
72 icon_ss << "lobby/status";
73
74 switch(user.get_state(focused_game)) {
76 icon_ss << "-lobby";
77 break;
79 name = markup::span_color(color_t(0, 255, 255), name);
80 icon_ss << (user.observing ? "-obs" : "-playing");
81 break;
84 icon_ss << (user.observing ? "-obs" : "-playing");
85 break;
86 }
87
88 switch(user.get_relation()) {
90 icon_ss << "-s";
91 break;
93 icon_ss << "-n";
94 break;
96 icon_ss << "-f";
97 break;
99 icon_ss << "-i";
100 break;
101 }
102
103 icon_ss << ".png";
104
105 widget_item tree_group_field;
106 widget_data tree_group_item;
107
108 /*** Add tree item ***/
109 tree_group_field["label"] = icon_ss.str();
110 tree_group_item["icon"] = tree_group_field;
111
112 tree_group_field["label"] = name;
113 tree_group_field["use_markup"] = "true";
114 tree_group_item["name"] = tree_group_field;
115
116 // Indices here must match the order of the lists in the player_lists array (see `init`)
117 switch(user.get_state(focused_game)) {
119 inputs[0].node_data.push_back(std::move(tree_group_item));
120 inputs[0].user_data.push_back(&user);
121
122 break;
124 inputs[1].node_data.push_back(std::move(tree_group_item));
125 inputs[1].user_data.push_back(&user);
126
127 break;
129 inputs[2].node_data.push_back(std::move(tree_group_item));
130 inputs[2].user_data.push_back(&user);
131
132 break;
133 }
134 }
135
136 info_map.clear();
137
138 for(std::size_t i = 0; i < player_lists.size(); ++i) {
139 assert(inputs[i].node_data.size() == inputs[i].user_data.size());
140
141 // Add the player nodes
142 const auto new_nodes = player_lists[i].root->replace_children("player", inputs[i].node_data);
143
144 for(std::size_t k = 0; k < new_nodes.size(); ++k) {
145 auto* node = new_nodes[k].get();
146 auto* info = inputs[i].user_data[k];
147
148 // Note the user_info associated with this node
149 info_map.try_emplace(node, info);
150
151 connect_signal_mouse_left_double_click(
152 node->find_widget<toggle_panel>("tree_view_node_label"),
153 std::bind(user_callback, info)
154 );
155 }
156
157 player_lists[i].update_player_count_label();
158 }
159
160 // Don't attempt to restore the scroll position if the window hasn't been laid out yet
161 if(tree->get_origin() != point{-1, -1}) {
162 tree->set_vertical_scrollbar_item_position(scrollbar_position);
163 }
164}
165
167{
168 tree = w.find_widget<tree_view>("player_tree", false, true);
169
170 // Jump to a newly-selected player's game if they are in one.
171 // A non-visible game will just leave no game selected.
172 connect_signal_notify_modified(*tree, std::bind(&lobby_player_list_helper::selected_node_changed, this));
173
174 player_lists = {
175 sub_list{tree, _("Selected Game"), true},
176 sub_list{tree, _("Lobby"), true},
177 sub_list{tree, _("Other Games"), false}
178 };
179}
180
185
187{
188 // Group headers ('Selected Game', 'Lobby', 'Other Games') are tree_view_nodes
189 // too and can become the selected node (eg by clicking one to fold/unfold it)
190 // but only player leaf nodes are ever added to info_map. So look up here
191 // rather than using get_selected_info(), which would throw an exception via
192 // info_map.at() in that case.
193 if(const auto it = info_map.find(tree->selected_item()); it != info_map.end()) {
194 game_select_callback(it->second);
195 }
196}
197
198} // namespace gui2
std::map< const tree_view_node *, const mp::user_info * > info_map
Node-to-info mappings for easy access.
const mp::user_info * get_selected_info() const
std::array< sub_list, 3 > player_lists
Main subsections of the player tree.
std::function< void(const mp::user_info *)> game_select_callback
Called with the newly-selected player when the tree's selection changes to one.
void update(const std::vector< mp::user_info > &user_info, int focused_game)
Updates the tree contents based on the given user data.
void selected_node_changed()
Connected to the tree's modified signal; forwards to game_select_callback when a player is selected.
std::function< void(const mp::user_info *)> user_callback
The double click callback bound to each player's tree node.
unsigned get_vertical_scrollbar_item_position() const
Returns current position of the vertical scrollbar.
void set_vertical_scrollbar_item_position(const unsigned position)
Move the vertical scrollbar to a position.
void unfold(const bool recursive=false)
tree_view_node * selected_item()
tree_view_node & add_node(const std::string &id, const widget_data &data, const int index=-1)
Definition tree_view.cpp:57
T * find_widget(const std::string_view id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition widget.hpp:747
point get_origin() const
Returns the screen origin of the widget.
Definition widget.cpp:311
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition window.hpp:59
static std::string _(const char *str)
Definition gettext.hpp:100
This file contains the window object, this object is a top level container which has the event manage...
static lg::log_domain log_lobby("lobby")
std::vector< widget_data > node_data
The raw data used to mass-construct player tree nodes.
std::vector< const mp::user_info * > user_data
The associated user data for each node, index-to-index.
const color_t GRAY_COLOR
Generic file dialog.
std::map< std::string, widget_item > widget_data
Definition widget.hpp:36
std::map< std::string, t_string > widget_item
Definition widget.hpp:33
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
Definition markup.hpp:110
int w
Definition pathfind.cpp:188
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
The basic class for representing 8-bit RGB or RGBA colour values.
Definition color.hpp:51
This class represents the information a client has about another player.
Holds a 2D point.
Definition point.hpp:25
#define i