Cockatrice/cockatrice/src/interface/widgets/tabs/tab_game.h
BruebachL 7c550ee505
[Game] Add an invite button to non-started and not full games (#7143)
* [Client] Send game invites from the user context menu via a private message

The user context menu gains an "Invite to Game" submenu listing the
inviteable games in the room (the inviter's own games, honoring the
buddy-only setting). Picking one opens a private message to the target
user with a cockatrice://joingame link naming the game, so the target
gets a clickable invite instead of a raw URL. Multi-game rooms offer a
picker; a single inviteable game sends directly. Sending a message to
an offline user no longer swallows the draft — it reports that the
user is offline and keeps the typed text.

Took 1 minute

* [Client] Add invite-to-game dialog to the game window

Took 15 seconds

* [Client] Open the invite dialog taller by default without enforcing a minimum size

* Move button to bottom

Took 3 minutes

* Address comments.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-08-17 21:37:15 +02:00

217 lines
6.6 KiB
C++

/**
* @file tab_game.h
* @ingroup Tabs
* @ingroup GameWidgets
* @ingroup Lobby
*/
//! \todo Document this file.
#ifndef TAB_GAME_H
#define TAB_GAME_H
#include "../game/abstract_game.h"
#include "../game/player/player_logic.h"
#include "../game_graphics/log/message_log_widget.h"
#include "../interface/widgets/menus/tearoff_menu.h"
#include "../interface/widgets/replay/replay_widget.h"
#include "tab.h"
#include <QCompleter>
#include <QLoggingCategory>
#include <QMap>
#include <QStringListModel>
class CardMenu;
class ServerInfo_PlayerProperties;
class TabbedDeckViewContainer;
inline Q_LOGGING_CATEGORY(TabGameLog, "tab_game");
class UserListProxy;
class DeckViewContainer;
class AbstractClient;
class CardDatabase;
class GameView;
class GameScene;
class ReplayWidget;
class CardInfoFrameWidget;
class QTimer;
class QSplitter;
class QLabel;
class QPushButton;
class QToolButton;
class QMenu;
class ZoneViewLayout;
class ZoneViewWidget;
class PhasesToolbar;
class PlayerListWidget;
class ReplayTimelineWidget;
class CardZone;
class AbstractCardItem;
class CardItem;
class QVBoxLayout;
class QHBoxLayout;
class GameReplay;
class LineEditCompleter;
class QDockWidget;
class QStackedWidget;
class TabGame : public Tab
{
Q_OBJECT
private:
AbstractGame *game;
const UserListProxy *userListProxy;
ReplayWidget *replayWidget = nullptr;
QStringList gameTypes;
QCompleter *mentionCompleter;
QStringListModel *mentionModel;
QStringList autocompleteUserList;
QStackedWidget *mainWidget;
CardInfoFrameWidget *cardInfoFrameWidget;
PlayerListWidget *playerListWidget;
QPushButton *inviteButton = nullptr;
QLabel *timeElapsedLabel;
MessageLogWidget *messageLog;
QLabel *sayLabel;
LineEditCompleter *sayEdit;
PhasesToolbar *phasesToolbar;
GameScene *scene;
GameView *gameView;
QMap<int, TabbedDeckViewContainer *> deckViewContainers;
QVBoxLayout *deckViewContainerLayout;
QWidget *gamePlayAreaWidget, *deckViewContainerWidget;
QDockWidget *cardInfoDock, *messageLayoutDock, *playerListDock, *replayDock;
QAction *playersSeparator;
QMenu *gameMenu, *viewMenu;
TearOffMenu *phasesMenu;
QAction *aGameInfo, *aConcede, *aCopyGameLink, *aLeaveGame, *aNextPhase, *aNextPhaseAction, *aNextTurn,
*aReverseTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout;
QAction *aFocusChat;
QAction *aInviteToGame = nullptr;
QList<QAction *> phaseActions;
QAction *aCardMenu;
/**
* @brief The actions associated with managing a QDockWidget
*/
struct DockActions
{
QMenu *menu;
QAction *aVisible;
QAction *aFloating;
QSize defaultSize;
};
QMap<QDockWidget *, DockActions> dockToActions;
PlayerLogic *addPlayer(PlayerLogic *newPlayer);
void addLocalPlayer(PlayerLogic *newPlayer, int playerId);
void processRemotePlayerDeckSelect(QString deckList, int playerId, QString playerName);
void processMultipleRemotePlayerDeckSelect(QVector<QPair<int, QPair<QString, QString>>> playerIdDeckMap);
void processLocalPlayerDeckSelect(PlayerLogic *localPlayer, int playerId, ServerInfo_Player playerInfo);
void loadDeckForLocalPlayer(PlayerLogic *localPlayer, int playerId, ServerInfo_Player playerInfo);
void processLocalPlayerReady(int playerId, ServerInfo_Player playerInfo);
void createZoneForPlayer(PlayerLogic *newPlayer, int playerId);
void startGame(bool resuming);
void stopGame();
void closeGame();
bool leaveGame();
PlayerLogic *setActivePlayer(int id);
void setActivePhase(int phase);
void createMenuItems();
void createReplayMenuItems();
void createViewMenuItems();
void registerDockWidget(QMenu *_viewMenu, QDockWidget *widget, const QSize &defaultSize);
void createCardInfoDock(bool bReplay = false);
void createPlayerListDock(bool bReplay = false);
void createMessageDock(bool bReplay = false);
void createPlayAreaWidget(bool bReplay = false);
void createDeckViewContainerWidget(bool bReplay = false);
void createReplayDock(GameReplay *replay);
void updateInviteButtonState();
signals:
void gameClosing(TabGame *tab);
void containerProcessingStarted(const GameEventContext &context);
void containerProcessingDone();
void openMessageDialog(const QString &userName, bool focus);
void openDeckEditor(const LoadedDeck &deck);
void notIdle();
void phaseChanged(int phase);
void gameLeft();
void chatMessageSent(QString chatMessage);
void turnAdvanced();
private slots:
void adminLockChanged(bool lock);
void newCardAdded(AbstractCardItem *card);
void setCardMenu(CardMenu *menu);
void actGameInfo();
void actInviteToGame();
void actConcede();
void actCopyGameLink();
void actRemoveLocalArrows();
void actRotateViewCW();
void actRotateViewCCW();
void actSay();
void actPhaseAction();
void actNextPhase();
void actNextPhaseAction();
void addMentionTag(const QString &value);
void linkCardToChat(const QString &cardName);
void refreshShortcuts();
void loadLayout();
void actCompleterChanged();
void notifyPlayerJoin(QString playerName);
void notifyPlayerKicked();
void processPlayerLeave(PlayerLogic *leavingPlayer);
void actResetLayout();
void hideEvent(QHideEvent *event) override;
protected slots:
void closeEvent(QCloseEvent *event) override;
public:
TabGame(TabSupervisor *_tabSupervisor,
QList<AbstractClient *> &_clients,
const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes);
void connectToGameState();
void connectToPlayerManager();
void connectToGameEventHandler();
void connectMessageLogToGameEventHandler();
void connectPlayerListToGameEventHandler();
TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay);
~TabGame() override;
void retranslateUi() override;
void updatePlayerListDockTitle();
bool closeRequest() override;
[[nodiscard]] QString getTabText() const override;
[[nodiscard]] AbstractGame *getGame() const
{
return game;
}
public slots:
void viewCardInfo(const CardRef &cardRef = {}) const;
void resetChatAndPhase();
void updateTimeElapsedLabel(QString newTime);
void addPlayerToAutoCompleteList(QString playerName);
void removePlayerFromAutoCompleteList(QString playerName);
void removeSpectator(int spectatorId, ServerInfo_User spectator);
void processLocalPlayerSideboardLocked(int playerId, bool sideboardLocked);
void processLocalPlayerReadyStateChanged(int playerId, bool ready);
void emitUserEvent();
};
#endif