mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-21 17:15:09 -07:00
Merge branch 'master' into tooomm-qt5
This commit is contained in:
commit
3bc08ef94c
357 changed files with 23069 additions and 2916 deletions
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../intents/intent_join_server_game.h"
|
||||
#include "../interface/pixel_map_generator.h"
|
||||
#include "../interface/widgets/server/game_link.h"
|
||||
#include "../interface/widgets/server/user/user_list_manager.h"
|
||||
#include "../interface/widgets/server/user/user_list_widget.h"
|
||||
#include "../main.h"
|
||||
|
|
@ -17,7 +19,9 @@
|
|||
#include "tab_home.h"
|
||||
#include "tab_logs.h"
|
||||
#include "tab_message.h"
|
||||
#include "tab_moderation.h"
|
||||
#include "tab_replays.h"
|
||||
#include "tab_report.h"
|
||||
#include "tab_room.h"
|
||||
#include "tab_server.h"
|
||||
#include "tab_visual_database_display.h"
|
||||
|
|
@ -30,6 +34,7 @@
|
|||
#include <QPainter>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/network/client/remote/remote_client.h>
|
||||
#include <libcockatrice/protocol/pb/event_game_joined.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_notify_user.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_user_message.pb.h>
|
||||
|
|
@ -38,6 +43,7 @@
|
|||
#include <libcockatrice/protocol/pb/room_event.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_room.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/chat_settings.h>
|
||||
#include <libcockatrice/settings/deck_editor_settings.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
|
|
@ -100,7 +106,7 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/)
|
|||
TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *parent)
|
||||
: QTabWidget(parent), userInfo(nullptr), client(_client), tabsMenu(tabsMenu), tabVisualDeckStorage(nullptr),
|
||||
tabServer(nullptr), tabAccount(nullptr), tabDeckStorage(nullptr), tabReplays(nullptr), tabAdmin(nullptr),
|
||||
tabLog(nullptr), isLocalGame(false)
|
||||
tabLog(nullptr), tabReport(nullptr), tabModeration(nullptr), isLocalGame(false)
|
||||
{
|
||||
setElideMode(Qt::ElideRight);
|
||||
setMovable(true);
|
||||
|
|
@ -117,6 +123,7 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *
|
|||
connect(client, &AbstractClient::gameJoinedEventReceived, this, &TabSupervisor::gameJoined);
|
||||
connect(client, &AbstractClient::userMessageEventReceived, this, &TabSupervisor::processUserMessageEvent);
|
||||
connect(client, &AbstractClient::maxPingTime, this, &TabSupervisor::updatePingTime);
|
||||
connect(client, &AbstractClient::pingStatsUpdated, this, &TabSupervisor::updateLatencyTooltip);
|
||||
connect(client, &AbstractClient::notifyUserEventReceived, this, &TabSupervisor::processNotifyUserEvent);
|
||||
|
||||
// create tabs menu actions
|
||||
|
|
@ -171,6 +178,14 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *
|
|||
aTabLog->setCheckable(true);
|
||||
connect(aTabLog, &QAction::triggered, this, &TabSupervisor::actTabLog);
|
||||
|
||||
aTabReport = new QAction(this);
|
||||
aTabReport->setCheckable(true);
|
||||
connect(aTabReport, &QAction::triggered, this, &TabSupervisor::actTabReport);
|
||||
|
||||
aTabModeration = new QAction(this);
|
||||
aTabModeration->setCheckable(true);
|
||||
connect(aTabModeration, &QAction::triggered, this, &TabSupervisor::actTabModeration);
|
||||
|
||||
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
||||
&TabSupervisor::refreshShortcuts);
|
||||
refreshShortcuts();
|
||||
|
|
@ -210,6 +225,8 @@ void TabSupervisor::retranslateUi()
|
|||
aTabReplays->setText(tr("Game Replays"));
|
||||
aTabAdmin->setText(tr("Administration"));
|
||||
aTabLog->setText(tr("Logs"));
|
||||
aTabReport->setText(tr("Report Queue"));
|
||||
aTabModeration->setText(tr("Moderation"));
|
||||
|
||||
// tabs
|
||||
QList<Tab *> tabs;
|
||||
|
|
@ -219,6 +236,8 @@ void TabSupervisor::retranslateUi()
|
|||
tabs.append(tabAdmin);
|
||||
tabs.append(tabAccount);
|
||||
tabs.append(tabLog);
|
||||
tabs.append(tabReport);
|
||||
tabs.append(tabModeration);
|
||||
QMapIterator<int, TabRoom *> roomIterator(roomTabs);
|
||||
while (roomIterator.hasNext()) {
|
||||
tabs.append(roomIterator.next().value());
|
||||
|
|
@ -265,6 +284,8 @@ void TabSupervisor::refreshShortcuts()
|
|||
aTabReplays->setShortcuts(shortcuts.getShortcut("Tabs/aTabReplays"));
|
||||
aTabAdmin->setShortcuts(shortcuts.getShortcut("Tabs/aTabAdmin"));
|
||||
aTabLog->setShortcuts(shortcuts.getShortcut("Tabs/aTabLog"));
|
||||
aTabReport->setShortcuts(shortcuts.getShortcut("Tabs/aTabReport"));
|
||||
aTabModeration->setShortcuts(shortcuts.getShortcut("Tabs/aTabModeration"));
|
||||
}
|
||||
|
||||
void TabSupervisor::closeEvent(QCloseEvent *event)
|
||||
|
|
@ -317,7 +338,12 @@ static void checkAndTrigger(QAction *checkableAction, bool checked)
|
|||
}
|
||||
|
||||
/**
|
||||
* Opens the always-available tabs, depending on settings.
|
||||
* Opens the always-available tabs, depending on settings, and lands on the configured startup tab.
|
||||
*
|
||||
* The startup destination is a request: tabs that were not open before (deck editors, storage
|
||||
* tabs disabled in the Tabs menu) are opened as part of the startup flow. Destinations that
|
||||
* require a server connection (Server, Server Room) are handled asynchronously by MainWindow
|
||||
* through the intent system, since this class has no RemoteClient.
|
||||
*/
|
||||
void TabSupervisor::initStartupTabs()
|
||||
{
|
||||
|
|
@ -333,6 +359,42 @@ void TabSupervisor::initStartupTabs()
|
|||
if (SettingsCache::instance().tabs().getTabReplaysOpen()) {
|
||||
openTabReplays();
|
||||
}
|
||||
|
||||
switch (SettingsCache::instance().tabs().getStartupTabIndex()) {
|
||||
case StartupTab::StartupTabVisualDeckStorage:
|
||||
if (!tabVisualDeckStorage) {
|
||||
openTabVisualDeckStorage();
|
||||
}
|
||||
setCurrentWidget(tabVisualDeckStorage);
|
||||
break;
|
||||
case StartupTab::StartupTabDeckStorage:
|
||||
if (!tabDeckStorage) {
|
||||
openTabDeckStorage();
|
||||
}
|
||||
setCurrentWidget(tabDeckStorage);
|
||||
break;
|
||||
case StartupTab::StartupTabReplays:
|
||||
if (!tabReplays) {
|
||||
openTabReplays();
|
||||
}
|
||||
setCurrentWidget(tabReplays);
|
||||
break;
|
||||
case StartupTab::StartupTabDeckEditor:
|
||||
addDeckEditorTab(LoadedDeck());
|
||||
break;
|
||||
case StartupTab::StartupTabVisualDeckEditor:
|
||||
addVisualDeckEditorTab(LoadedDeck());
|
||||
break;
|
||||
case StartupTab::StartupTabServer:
|
||||
case StartupTab::StartupTabServerRoom:
|
||||
// Handled asynchronously by MainWindow::applyStartupDestination(); Home stays selected
|
||||
// until the server connection succeeds.
|
||||
break;
|
||||
case StartupTab::StartupTabHome:
|
||||
default:
|
||||
setCurrentWidget(tabHome);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -347,6 +409,7 @@ int TabSupervisor::myAddTab(Tab *tab, QAction *manager)
|
|||
{
|
||||
connect(tab, &TabGame::userEvent, this, &TabSupervisor::tabUserEvent);
|
||||
connect(tab, &TabGame::tabTextChanged, this, &TabSupervisor::updateTabText);
|
||||
connect(tab, &TabGame::cockatriceLinkActivated, this, &TabSupervisor::cockatriceLinkActivated);
|
||||
|
||||
QString tabText = tab->getTabText();
|
||||
int idx = addTab(tab, sanitizeTabName(tabText));
|
||||
|
|
@ -424,6 +487,8 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo)
|
|||
tabsMenu->addAction(aTabAdmin);
|
||||
tabsMenu->addAction(aTabLog);
|
||||
tabsMenu->addAction(aTabCardArtRules);
|
||||
tabsMenu->addAction(aTabReport);
|
||||
tabsMenu->addAction(aTabModeration);
|
||||
|
||||
if (SettingsCache::instance().tabs().getTabAdminOpen()) {
|
||||
openTabAdmin();
|
||||
|
|
@ -431,6 +496,12 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo)
|
|||
if (SettingsCache::instance().tabs().getTabLogOpen()) {
|
||||
openTabLog();
|
||||
}
|
||||
if (SettingsCache::instance().tabs().getTabReportOpen()) {
|
||||
openTabReport();
|
||||
}
|
||||
if (SettingsCache::instance().tabs().getTabModerationOpen()) {
|
||||
openTabModeration();
|
||||
}
|
||||
openTabCardArtRules();
|
||||
}
|
||||
|
||||
|
|
@ -444,6 +515,8 @@ void TabSupervisor::startLocal(const QList<AbstractClient *> &_clients)
|
|||
tabAccount = nullptr;
|
||||
tabAdmin = nullptr;
|
||||
tabLog = nullptr;
|
||||
tabReport = nullptr;
|
||||
tabModeration = nullptr;
|
||||
isLocalGame = true;
|
||||
userInfo = new ServerInfo_User;
|
||||
localClients = _clients;
|
||||
|
|
@ -485,6 +558,12 @@ void TabSupervisor::stop()
|
|||
if (tabLog) {
|
||||
tabLog->close();
|
||||
}
|
||||
if (tabReport) {
|
||||
tabReport->close();
|
||||
}
|
||||
if (tabModeration) {
|
||||
tabModeration->close();
|
||||
}
|
||||
}
|
||||
|
||||
QList<Tab *> tabsToDelete;
|
||||
|
|
@ -722,6 +801,59 @@ void TabSupervisor::openTabLog()
|
|||
aTabLog->setChecked(true);
|
||||
}
|
||||
|
||||
void TabSupervisor::actTabReport(bool checked)
|
||||
{
|
||||
SettingsCache::instance().tabs().setTabReportOpen(checked);
|
||||
if (checked && !tabReport) {
|
||||
openTabReport();
|
||||
setCurrentWidget(tabReport);
|
||||
} else if (!checked && tabReport) {
|
||||
tabReport->closeRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void TabSupervisor::openTabReport()
|
||||
{
|
||||
tabReport = new TabReport(this, client);
|
||||
myAddTab(tabReport, aTabReport);
|
||||
connect(tabReport, &TabReport::openReplay, this, &TabSupervisor::openReplay);
|
||||
connect(tabReport, &TabReport::requestJoinGame, this, &TabSupervisor::joinReportGame);
|
||||
connect(tabReport, &QObject::destroyed, this, [this] {
|
||||
tabReport = nullptr;
|
||||
aTabReport->setChecked(false);
|
||||
});
|
||||
aTabReport->setChecked(true);
|
||||
}
|
||||
|
||||
void TabSupervisor::actTabModeration(bool checked)
|
||||
{
|
||||
SettingsCache::instance().tabs().setTabModerationOpen(checked);
|
||||
if (checked && !tabModeration) {
|
||||
openTabModeration();
|
||||
setCurrentWidget(tabModeration);
|
||||
} else if (!checked && tabModeration) {
|
||||
tabModeration->closeRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void TabSupervisor::openTabModeration(const QString &userName)
|
||||
{
|
||||
if (tabModeration) {
|
||||
setCurrentWidget(tabModeration);
|
||||
if (!userName.isEmpty()) {
|
||||
tabModeration->investigate(userName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
tabModeration = new TabModeration(this, client, userName);
|
||||
myAddTab(tabModeration, aTabModeration);
|
||||
connect(tabModeration, &QObject::destroyed, this, [this] {
|
||||
tabModeration = nullptr;
|
||||
aTabModeration->setChecked(false);
|
||||
});
|
||||
aTabModeration->setChecked(true);
|
||||
}
|
||||
|
||||
void TabSupervisor::updatePingTime(int value, int max)
|
||||
{
|
||||
if (!tabServer) {
|
||||
|
|
@ -734,6 +866,23 @@ void TabSupervisor::updatePingTime(int value, int max)
|
|||
setTabIcon(indexOf(tabServer), QIcon(PingPixmapGenerator::generatePixmap(15, value, max)));
|
||||
}
|
||||
|
||||
void TabSupervisor::updateLatencyTooltip(const LatencyTracker::Stats &stats)
|
||||
{
|
||||
if (!tabServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.sampleCount == 0) {
|
||||
setTabToolTip(indexOf(tabServer), QString());
|
||||
return;
|
||||
}
|
||||
|
||||
setTabToolTip(indexOf(tabServer),
|
||||
tr("Connection quality over the last %n sample(s):", "", stats.sampleCount) + "\n" +
|
||||
tr("Last: %1 ms").arg(stats.lastMs) + "\n" + tr("Median: %1 ms").arg(stats.medianMs) + "\n" +
|
||||
tr("95th percentile: %1 ms").arg(stats.p95Ms) + "\n" + tr("Maximum: %1 ms").arg(stats.maxMs));
|
||||
}
|
||||
|
||||
void TabSupervisor::gameJoined(const Event_GameJoined &event)
|
||||
{
|
||||
QMap<int, QString> roomGameTypes;
|
||||
|
|
@ -792,6 +941,7 @@ void TabSupervisor::addRoomTab(const ServerInfo_Room &info, bool setCurrent)
|
|||
connect(tab, &TabRoom::maximizeClient, this, &TabSupervisor::maximizeMainWindow);
|
||||
connect(tab, &TabRoom::roomClosing, this, &TabSupervisor::roomLeft);
|
||||
connect(tab, &TabRoom::openMessageDialog, this, &TabSupervisor::addMessageTab);
|
||||
connect(tab, &TabRoom::cockatriceLinkActivated, this, &TabSupervisor::cockatriceLinkActivated);
|
||||
myAddTab(tab);
|
||||
roomTabs.insert(info.room_id(), tab);
|
||||
if (setCurrent) {
|
||||
|
|
@ -838,6 +988,30 @@ void TabSupervisor::replayLeft(TabGame *tab)
|
|||
replayTabs.removeOne(tab);
|
||||
}
|
||||
|
||||
void TabSupervisor::joinReportGame(const int gameId, const int roomId)
|
||||
{
|
||||
auto *remoteClient = qobject_cast<RemoteClient *>(client);
|
||||
if (!remoteClient) {
|
||||
actShowPopup(tr("Report joins are only available on a remote server."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto ctx = std::make_unique<ContextJoinGame>();
|
||||
ctx->roomContext.serverContext.hostname = remoteClient->peerName();
|
||||
ctx->roomContext.serverContext.port = QString::number(remoteClient->peerPort());
|
||||
ctx->roomContext.roomId = roomId;
|
||||
ctx->gameId = gameId;
|
||||
ctx->asSpectator = true;
|
||||
|
||||
auto *joinGameIntent = new IntentJoinServerGame(this, remoteClient, std::move(ctx));
|
||||
joinGameIntent->setParent(this);
|
||||
connect(joinGameIntent, &Intent::failed, this, [gameId](const QString &reason) {
|
||||
actShowPopup(tr("Could not join game %1.\n%2").arg(gameId).arg(reason));
|
||||
});
|
||||
|
||||
joinGameIntent->execute();
|
||||
}
|
||||
|
||||
TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus)
|
||||
{
|
||||
if (receiverName == QString::fromStdString(userInfo->name())) {
|
||||
|
|
@ -845,8 +1019,10 @@ TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus
|
|||
}
|
||||
|
||||
ServerInfo_User otherUser;
|
||||
bool userOnline = false;
|
||||
if (auto user = userListManager->getOnlineUser(receiverName)) {
|
||||
otherUser = ServerInfo_User(*user);
|
||||
userOnline = true;
|
||||
} else {
|
||||
otherUser.set_name(receiverName.toStdString());
|
||||
}
|
||||
|
|
@ -860,9 +1036,10 @@ TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus
|
|||
return tab;
|
||||
}
|
||||
|
||||
tab = new TabMessage(this, client, *userInfo, otherUser);
|
||||
tab = new TabMessage(this, client, *userInfo, otherUser, userOnline);
|
||||
connect(tab, &TabMessage::talkClosing, this, &TabSupervisor::talkLeft);
|
||||
connect(tab, &TabMessage::maximizeClient, this, &TabSupervisor::maximizeMainWindow);
|
||||
connect(tab, &TabMessage::cockatriceLinkActivated, this, &TabSupervisor::cockatriceLinkActivated);
|
||||
myAddTab(tab);
|
||||
messageTabs.insert(receiverName, tab);
|
||||
if (focus) {
|
||||
|
|
@ -886,6 +1063,53 @@ void TabSupervisor::talkLeft(TabMessage *tab)
|
|||
removeTab(indexOf(tab));
|
||||
}
|
||||
|
||||
QList<GameInviteOption> TabSupervisor::getGameInviteLinksForRoom(int roomId) const
|
||||
{
|
||||
QList<GameInviteOption> options;
|
||||
if (isLocalGame) {
|
||||
return options;
|
||||
}
|
||||
|
||||
// The inviter may be in several games of the same room (hosting one and
|
||||
// spectating another, for example). Return every game so the caller can
|
||||
// let the user choose which one to invite to.
|
||||
for (TabGame *tab : gameTabs) {
|
||||
GameMetaInfo *metaInfo = tab->getGame()->getGameMetaInfo();
|
||||
if (metaInfo->proto().room_id() != roomId) {
|
||||
continue;
|
||||
}
|
||||
// A closed game is a dead end — drop it. Started/full games stay
|
||||
// listed: an invite to them is a legitimate "come spectate" offer.
|
||||
if (metaInfo->proto().closed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int gameId = metaInfo->gameId();
|
||||
const QString description = QString::fromStdString(metaInfo->proto().description());
|
||||
|
||||
GameInviteOption option{
|
||||
.gameId = gameId,
|
||||
.label =
|
||||
description.isEmpty() ? tr("Game #%1").arg(gameId) : tr("Game #%1 — %2").arg(gameId).arg(description),
|
||||
.url = makeGameJoinLink(client->serverName(), client->serverPort(), roomId, gameId, description),
|
||||
.description = description,
|
||||
.onlyBuddies = metaInfo->proto().only_buddies(),
|
||||
.creatorName = QString::fromStdString(metaInfo->proto().creator_info().name()),
|
||||
};
|
||||
options.append(option);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
void TabSupervisor::sendInviteToUser(const QString &userName, const QString &inviteText)
|
||||
{
|
||||
TabMessage *tab = addMessageTab(userName, true);
|
||||
if (tab) {
|
||||
tab->sendInviteMessage(inviteText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new deck editor tab and loads the deck into it.
|
||||
* Creates either a classic or visual deck editor tab depending on settings
|
||||
|
|
@ -1172,6 +1396,24 @@ void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case Event_NotifyUser::REPORT_RESOLVED: {
|
||||
QString title = QString::fromStdString(event.custom_title()).simplified();
|
||||
QString content = QString::fromStdString(event.custom_content()).trimmed();
|
||||
if (!title.isEmpty() && !content.isEmpty()) {
|
||||
actShowPopup(title + "\n" + content);
|
||||
QApplication::alert(this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Event_NotifyUser::REPORT_COMMENT: {
|
||||
QString title = QString::fromStdString(event.custom_title()).simplified();
|
||||
QString content = QString::fromStdString(event.custom_content()).trimmed();
|
||||
if (!title.isEmpty() && !content.isEmpty()) {
|
||||
actShowPopup(title + "\n" + content);
|
||||
QApplication::alert(this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue