mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Add a member list expandable to the lobby. Fix issue with hosting more than once.
This commit is contained in:
		
							parent
							
								
									f346a9d372
								
							
						
					
					
						commit
						2d1efcc36b
					
				
					 8 changed files with 106 additions and 54 deletions
				
			
		|  | @ -117,6 +117,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||
|     default_theme_paths = QIcon::themeSearchPaths(); | ||||
|     UpdateUITheme(); | ||||
| 
 | ||||
|     Network::Init(); | ||||
| 
 | ||||
|     InitializeWidgets(); | ||||
|     InitializeDebugWidgets(); | ||||
|     InitializeRecentFileMenuActions(); | ||||
|  | @ -131,8 +133,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||
| 
 | ||||
|     SetupUIStrings(); | ||||
| 
 | ||||
|     Network::Init(); | ||||
| 
 | ||||
|     setWindowTitle(QString("Citra %1| %2-%3") | ||||
|                        .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | ||||
|     show(); | ||||
|  |  | |||
|  | @ -96,6 +96,8 @@ void ClientRoomWindow::Disconnect() { | |||
|     if (auto member = Network::GetRoomMember().lock()) { | ||||
|         member->Leave(); | ||||
|         ui->chat->AppendStatusMessage(tr("Disconnected")); | ||||
|         close(); | ||||
|         emit Closed(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -79,6 +79,8 @@ void HostRoomWindow::Host() { | |||
|                 return; | ||||
|             } else { | ||||
|                 member->Leave(); | ||||
|                 auto parent = static_cast<MultiplayerState*>(parentWidget()); | ||||
|                 parent->OnCloseRoom(); | ||||
|             } | ||||
|         } | ||||
|         ui->host->setDisabled(true); | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, | |||
|     proxy->setFilterCaseSensitivity(Qt::CaseInsensitive); | ||||
|     proxy->setSortLocaleAware(true); | ||||
|     ui->room_list->setModel(proxy); | ||||
|     ui->room_list->header()->setSectionResizeMode(QHeaderView::ResizeToContents); | ||||
|     ui->room_list->header()->setSectionResizeMode(QHeaderView::Interactive); | ||||
|     ui->room_list->header()->stretchLastSection(); | ||||
|     ui->room_list->setAlternatingRowColors(true); | ||||
|     ui->room_list->setSelectionMode(QHeaderView::SingleSelection); | ||||
|  | @ -45,7 +45,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, | |||
|     ui->room_list->setSortingEnabled(true); | ||||
|     ui->room_list->setEditTriggers(QHeaderView::NoEditTriggers); | ||||
|     ui->room_list->setExpandsOnDoubleClick(false); | ||||
|     ui->room_list->setUniformRowHeights(true); | ||||
|     // ui->room_list->setUniformRowHeights(true);
 | ||||
|     ui->room_list->setContextMenuPolicy(Qt::CustomContextMenu); | ||||
| 
 | ||||
|     ui->nickname->setValidator(Validation::nickname); | ||||
|  | @ -61,6 +61,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, | |||
|     connect(ui->search, &QLineEdit::textChanged, proxy, | ||||
|             &LobbyFilterProxyModel::setFilterFixedString); | ||||
|     connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); | ||||
|     connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom); | ||||
| 
 | ||||
|     // Actions
 | ||||
|     connect(this, &Lobby::LobbyRefreshed, this, &Lobby::OnRefreshLobby); | ||||
|  | @ -99,6 +100,11 @@ const QString Lobby::PasswordPrompt() { | |||
|     return ok ? text : QString(); | ||||
| } | ||||
| 
 | ||||
| void Lobby::OnExpandRoom(const QModelIndex& index) { | ||||
|     QModelIndex member_index = proxy->index(index.row(), Column::MEMBER); | ||||
|     auto member_list = proxy->data(member_index, LobbyItemMemberList::MemberListRole).toList(); | ||||
| } | ||||
| 
 | ||||
| void Lobby::OnJoinRoom(const QModelIndex& index) { | ||||
|     if (!ui->nickname->hasAcceptableInput()) { | ||||
|         NetworkMessage::ShowError(NetworkMessage::USERNAME_NOT_VALID); | ||||
|  | @ -123,7 +129,6 @@ void Lobby::OnJoinRoom(const QModelIndex& index) { | |||
|     // attempt to connect in a different thread
 | ||||
|     QFuture<void> f = QtConcurrent::run([&, password] { | ||||
|         if (auto room_member = Network::GetRoomMember().lock()) { | ||||
| 
 | ||||
|             QModelIndex connection_index = proxy->index(index.row(), Column::HOST); | ||||
|             const std::string nickname = ui->nickname->text().toStdString(); | ||||
|             const std::string ip = | ||||
|  | @ -161,7 +166,6 @@ void Lobby::ResetModel() { | |||
|     model->setHeaderData(Column::GAME_NAME, Qt::Horizontal, tr("Preferred Game"), Qt::DisplayRole); | ||||
|     model->setHeaderData(Column::HOST, Qt::Horizontal, tr("Host"), Qt::DisplayRole); | ||||
|     model->setHeaderData(Column::MEMBER, Qt::Horizontal, tr("Players"), Qt::DisplayRole); | ||||
|     ui->room_list->header()->stretchLastSection(); | ||||
| } | ||||
| 
 | ||||
| void Lobby::RefreshLobby() { | ||||
|  | @ -181,7 +185,7 @@ void Lobby::OnRefreshLobby() { | |||
|         // find the icon for the game if this person owns that game.
 | ||||
|         QPixmap smdh_icon; | ||||
|         for (int r = 0; r < game_list->rowCount(); ++r) { | ||||
|             auto index = QModelIndex(game_list->index(r, 0)); | ||||
|             auto index = game_list->index(r, 0); | ||||
|             auto game_id = game_list->data(index, GameListItemPath::ProgramIdRole).toULongLong(); | ||||
|             if (game_id != 0 && room.preferred_game_id == game_id) { | ||||
|                 smdh_icon = game_list->data(index, Qt::DecorationRole).value<QPixmap>(); | ||||
|  | @ -196,17 +200,41 @@ void Lobby::OnRefreshLobby() { | |||
|             members.append(var); | ||||
|         } | ||||
| 
 | ||||
|         model->appendRow(QList<QStandardItem*>( | ||||
|             {new LobbyItemPassword(room.has_password), | ||||
|              new LobbyItemName(QString::fromStdString(room.name)), | ||||
|              new LobbyItemGame(room.preferred_game_id, QString::fromStdString(room.preferred_game), | ||||
|                                smdh_icon), | ||||
|              new LobbyItemHost(QString::fromStdString(room.owner), QString::fromStdString(room.ip), | ||||
|                                room.port), | ||||
|              new LobbyItemMemberList(members, room.max_player)})); | ||||
|         auto first_item = new LobbyItemPassword(room.has_password); | ||||
|         auto row = QList<QStandardItem*>({ | ||||
|             first_item, | ||||
|             new LobbyItemName(QString::fromStdString(room.name)), | ||||
|             new LobbyItemGame(room.preferred_game_id, QString::fromStdString(room.preferred_game), | ||||
|                               smdh_icon), | ||||
|             new LobbyItemHost(QString::fromStdString(room.owner), QString::fromStdString(room.ip), | ||||
|                               room.port), | ||||
|             new LobbyItemMemberList(members, room.max_player), | ||||
|         }); | ||||
|         model->appendRow(row); | ||||
|         // To make the rows expandable, add the member data as a child of the first column of the
 | ||||
|         // rows with people in them and have qt set them to colspan after the model is finished
 | ||||
|         // resetting
 | ||||
|         if (room.members.size() > 0) { | ||||
|             first_item->appendRow(new LobbyItemExpandedMemberList(members)); | ||||
|         } | ||||
|     } | ||||
|     ui->room_list->setModel(model); | ||||
| 
 | ||||
|     // Reenable the refresh button and resize the columns
 | ||||
|     ui->refresh_list->setEnabled(true); | ||||
|     ui->refresh_list->setText(tr("Refresh List")); | ||||
|     ui->room_list->header()->stretchLastSection(); | ||||
|     for (int i = 0; i < Column::TOTAL - 1; ++i) { | ||||
|         ui->room_list->resizeColumnToContents(i); | ||||
|     } | ||||
| 
 | ||||
|     // Set the member list child items to span all columns
 | ||||
|     for (int i = 0; i < model->rowCount(); i++) { | ||||
|         auto parent = model->item(i, 0); | ||||
|         if (parent->hasChildren()) { | ||||
|             ui->room_list->setFirstColumnSpanned(0, parent->index(), true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list) | ||||
|  |  | |||
|  | @ -46,6 +46,14 @@ private slots: | |||
|      */ | ||||
|     void OnRefreshLobby(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Handler for single clicking on a room in the list. Expands the treeitem to show player | ||||
|      * information for the people in the room | ||||
|      * | ||||
|      * index - The row of the proxy model that the user wants to join. | ||||
|      */ | ||||
|     void OnExpandRoom(const QModelIndex&); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Handler for double clicking on a room in the list. Gathers the host ip and port and attempts | ||||
|      * to connect. Will also prompt for a password in case one is required. | ||||
|  | @ -99,7 +107,7 @@ private: | |||
| 
 | ||||
|     std::future<AnnounceMultiplayerRoom::RoomList> room_list_future; | ||||
|     std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; | ||||
|     std::unique_ptr<Ui::Lobby> ui; | ||||
|     Ui::Lobby* ui; | ||||
|     QFutureWatcher<void>* watcher; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>707</width> | ||||
|     <width>850</width> | ||||
|     <height>487</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|  |  | |||
|  | @ -23,16 +23,17 @@ enum List { | |||
| 
 | ||||
| class LobbyItem : public QStandardItem { | ||||
| public: | ||||
|     LobbyItem() : QStandardItem() {} | ||||
|     LobbyItem(const QString& string) : QStandardItem(string) {} | ||||
|     LobbyItem() = default; | ||||
|     explicit LobbyItem(const QString& string) : QStandardItem(string) {} | ||||
|     virtual ~LobbyItem() override {} | ||||
| }; | ||||
| 
 | ||||
| class LobbyItemPassword : public LobbyItem { | ||||
| public: | ||||
|     static const int PasswordRole = Qt::UserRole + 1; | ||||
|     LobbyItemPassword() : LobbyItem() {} | ||||
|     LobbyItemPassword(const bool has_password) : LobbyItem() { | ||||
| 
 | ||||
|     LobbyItemPassword() = default; | ||||
|     explicit LobbyItemPassword(const bool has_password) : LobbyItem() { | ||||
|         setData(has_password, PasswordRole); | ||||
|     } | ||||
| 
 | ||||
|  | @ -52,8 +53,9 @@ public: | |||
| class LobbyItemName : public LobbyItem { | ||||
| public: | ||||
|     static const int NameRole = Qt::UserRole + 1; | ||||
|     LobbyItemName() : LobbyItem() {} | ||||
|     LobbyItemName(QString name) : LobbyItem() { | ||||
| 
 | ||||
|     LobbyItemName() = default; | ||||
|     explicit LobbyItemName(QString name) : LobbyItem() { | ||||
|         setData(name, NameRole); | ||||
|     } | ||||
| 
 | ||||
|  | @ -74,8 +76,8 @@ public: | |||
|     static const int GameNameRole = Qt::UserRole + 2; | ||||
|     static const int GameIconRole = Qt::UserRole + 3; | ||||
| 
 | ||||
|     LobbyItemGame() : LobbyItem() {} | ||||
|     LobbyItemGame(u64 title_id, QString game_name, QPixmap smdh_icon) : LobbyItem() { | ||||
|     LobbyItemGame() = default; | ||||
|     explicit LobbyItemGame(u64 title_id, QString game_name, QPixmap smdh_icon) : LobbyItem() { | ||||
|         setData(static_cast<unsigned long long>(title_id), TitleIDRole); | ||||
|         setData(game_name, GameNameRole); | ||||
|         if (!smdh_icon.isNull()) { | ||||
|  | @ -109,8 +111,8 @@ public: | |||
|     static const int HostIPRole = Qt::UserRole + 2; | ||||
|     static const int HostPortRole = Qt::UserRole + 3; | ||||
| 
 | ||||
|     LobbyItemHost() : LobbyItem() {} | ||||
|     LobbyItemHost(QString username, QString ip, u16 port) : LobbyItem() { | ||||
|     LobbyItemHost() = default; | ||||
|     explicit LobbyItemHost(QString username, QString ip, u16 port) : LobbyItem() { | ||||
|         setData(username, HostUsernameRole); | ||||
|         setData(ip, HostIPRole); | ||||
|         setData(port, HostPortRole); | ||||
|  | @ -132,15 +134,15 @@ public: | |||
| 
 | ||||
| class LobbyMember { | ||||
| public: | ||||
|     LobbyMember() {} | ||||
|     LobbyMember() = default; | ||||
|     LobbyMember(const LobbyMember& other) { | ||||
|         username = other.username; | ||||
|         title_id = other.title_id; | ||||
|         game_name = other.game_name; | ||||
|     } | ||||
|     LobbyMember(const QString username, u64 title_id, const QString game_name) | ||||
|     explicit LobbyMember(const QString username, u64 title_id, const QString game_name) | ||||
|         : username(username), title_id(title_id), game_name(game_name) {} | ||||
|     ~LobbyMember() {} | ||||
|     ~LobbyMember() = default; | ||||
| 
 | ||||
|     QString GetUsername() const { | ||||
|         return username; | ||||
|  | @ -165,8 +167,8 @@ public: | |||
|     static const int MemberListRole = Qt::UserRole + 1; | ||||
|     static const int MaxPlayerRole = Qt::UserRole + 2; | ||||
| 
 | ||||
|     LobbyItemMemberList() : LobbyItem() {} | ||||
|     LobbyItemMemberList(QList<QVariant> members, u32 max_players) : LobbyItem() { | ||||
|     LobbyItemMemberList() = default; | ||||
|     explicit LobbyItemMemberList(QList<QVariant> members, u32 max_players) : LobbyItem() { | ||||
|         setData(members, MemberListRole); | ||||
|         setData(max_players, MaxPlayerRole); | ||||
|     } | ||||
|  | @ -187,3 +189,34 @@ public: | |||
|         return left_members < right_members; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Member information for when a lobby is expanded in the UI | ||||
|  */ | ||||
| class LobbyItemExpandedMemberList : public LobbyItem { | ||||
| public: | ||||
|     static const int MemberListRole = Qt::UserRole + 1; | ||||
| 
 | ||||
|     LobbyItemExpandedMemberList() = default; | ||||
|     explicit LobbyItemExpandedMemberList(QList<QVariant> members) : LobbyItem() { | ||||
|         setData(members, MemberListRole); | ||||
|     } | ||||
| 
 | ||||
|     QVariant data(int role) const override { | ||||
|         if (role != Qt::DisplayRole) { | ||||
|             return LobbyItem::data(role); | ||||
|         } | ||||
|         auto members = data(MemberListRole).toList(); | ||||
|         QString out = QObject::tr("Current Players in the room"); | ||||
|         for (const auto& member : members) { | ||||
|             const auto& m = member.value<LobbyMember>(); | ||||
|             if (m.GetGameName().isEmpty()) { | ||||
|                 out += QString(QObject::tr("\n%1 is not playing a game")).arg(m.GetUsername()); | ||||
|             } else { | ||||
|                 out += QString(QObject::tr("\n%1 is playing %2")) | ||||
|                            .arg(m.GetUsername(), m.GetGameName()); | ||||
|             } | ||||
|         } | ||||
|         return out; | ||||
|     } | ||||
| }; | ||||
|  |  | |||
|  | @ -20,13 +20,13 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis | |||
|     : QWidget(parent), game_list_model(game_list_model) { | ||||
|     if (auto member = Network::GetRoomMember().lock()) { | ||||
|         // register the network structs to use in slots and signals
 | ||||
|         qRegisterMetaType<Network::RoomMember::State>(); | ||||
|         state_callback_handle = member->BindOnStateChanged( | ||||
|             [this](const Network::RoomMember::State& state) { emit NetworkStateChanged(state); }); | ||||
|         connect(this, &MultiplayerState::NetworkStateChanged, this, | ||||
|                 &MultiplayerState::OnNetworkStateChanged); | ||||
|     } | ||||
| 
 | ||||
|     qRegisterMetaType<Network::RoomMember::State>(); | ||||
|     qRegisterMetaType<Common::WebResult>(); | ||||
|     announce_multiplayer_session = std::make_shared<Core::AnnounceMultiplayerSession>(); | ||||
|     announce_multiplayer_session->BindErrorCallback( | ||||
|  | @ -91,11 +91,6 @@ static void BringWidgetToFront(QWidget* widget) { | |||
| void MultiplayerState::OnViewLobby() { | ||||
|     if (lobby == nullptr) { | ||||
|         lobby = new Lobby(this, game_list_model, announce_multiplayer_session); | ||||
|         connect(lobby, &Lobby::Closed, [&] { | ||||
|             LOG_INFO(Frontend, "Destroying lobby"); | ||||
|             // lobby->close();
 | ||||
|             lobby = nullptr; | ||||
|         }); | ||||
|     } | ||||
|     BringWidgetToFront(lobby); | ||||
| } | ||||
|  | @ -103,11 +98,6 @@ void MultiplayerState::OnViewLobby() { | |||
| void MultiplayerState::OnCreateRoom() { | ||||
|     if (host_room == nullptr) { | ||||
|         host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session); | ||||
|         connect(host_room, &HostRoomWindow::Closed, [&] { | ||||
|             // host_room->close();
 | ||||
|             LOG_INFO(Frontend, "Destroying host room"); | ||||
|             host_room = nullptr; | ||||
|         }); | ||||
|     } | ||||
|     BringWidgetToFront(host_room); | ||||
| } | ||||
|  | @ -118,7 +108,6 @@ void MultiplayerState::OnCloseRoom() { | |||
|             if (NetworkMessage::WarnCloseRoom()) { | ||||
|                 room->Destroy(); | ||||
|                 announce_multiplayer_session->Stop(); | ||||
|                 // host_room->close();
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -129,11 +118,6 @@ void MultiplayerState::OnOpenNetworkRoom() { | |||
|         if (member->IsConnected()) { | ||||
|             if (client_room == nullptr) { | ||||
|                 client_room = new ClientRoomWindow(this); | ||||
|                 connect(client_room, &ClientRoomWindow::Closed, [&] { | ||||
|                     LOG_INFO(Frontend, "Destroying client room"); | ||||
|                     // client_room->close();
 | ||||
|                     client_room = nullptr; | ||||
|                 }); | ||||
|             } | ||||
|             BringWidgetToFront(client_room); | ||||
|             return; | ||||
|  | @ -147,11 +131,6 @@ void MultiplayerState::OnOpenNetworkRoom() { | |||
| void MultiplayerState::OnDirectConnectToRoom() { | ||||
|     if (direct_connect == nullptr) { | ||||
|         direct_connect = new DirectConnectWindow(this); | ||||
|         connect(direct_connect, &DirectConnectWindow::Closed, [&] { | ||||
|             LOG_INFO(Frontend, "Destroying direct connect"); | ||||
|             // direct_connect->close();
 | ||||
|             direct_connect = nullptr; | ||||
|         }); | ||||
|     } | ||||
|     BringWidgetToFront(direct_connect); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue