[Client] Route cockatrice:// link clicks from chat to the intent chain

A cockatrice:// link clicked in chat is currently handed to the OS (or
does nothing in-process). Clicks now emit a cockatriceLinkActivated signal
that travels ChatView -> Tab -> TabSupervisor -> MainWindow, which feeds
the URL through the same IntentUrlParser the OS activation path uses, so
the join runs entirely in-process. card/user schemes and all other links
behave as before.
This commit is contained in:
Lukas Brübach 2026-08-16 21:09:17 +02:00
parent 3de7882f0c
commit a9ca41acaa
9 changed files with 21 additions and 0 deletions

View file

@ -777,6 +777,11 @@ void ChatView::mouseReleaseEvent(QMouseEvent *event)
void ChatView::openLink(const QUrl &link) void ChatView::openLink(const QUrl &link)
{ {
if (link.scheme() == "cockatrice") {
emit cockatriceLinkActivated(link.toString(QUrl::FullyEncoded));
return;
}
if ((link.scheme() == "card") || (link.scheme() == "user")) { if ((link.scheme() == "card") || (link.scheme() == "user")) {
return; return;
} }

View file

@ -122,6 +122,7 @@ signals:
void addMentionTag(QString mentionTag); void addMentionTag(QString mentionTag);
void messageClickedSignal(); void messageClickedSignal();
void showMentionPopup(const QString &userName); void showMentionPopup(const QString &userName);
void cockatriceLinkActivated(const QString &url);
}; };
#endif #endif

View file

@ -20,6 +20,7 @@ class Tab : public QMainWindow
signals: signals:
void userEvent(bool globalEvent = true); void userEvent(bool globalEvent = true);
void tabTextChanged(Tab *tab, const QString &newTabText); void tabTextChanged(Tab *tab, const QString &newTabText);
void cockatriceLinkActivated(const QString &url);
protected: protected:
TabSupervisor *tabSupervisor; TabSupervisor *tabSupervisor;

View file

@ -32,6 +32,7 @@ TabMessage::TabMessage(TabSupervisor *_tabSupervisor,
connect(chatView, &ChatView::showCardInfoPopup, this, &TabMessage::showCardInfoPopup); connect(chatView, &ChatView::showCardInfoPopup, this, &TabMessage::showCardInfoPopup);
connect(chatView, &ChatView::deleteCardInfoPopup, this, &TabMessage::deleteCardInfoPopup); connect(chatView, &ChatView::deleteCardInfoPopup, this, &TabMessage::deleteCardInfoPopup);
connect(chatView, &ChatView::addMentionTag, this, &TabMessage::addMentionTag); connect(chatView, &ChatView::addMentionTag, this, &TabMessage::addMentionTag);
connect(chatView, &ChatView::cockatriceLinkActivated, this, &TabMessage::cockatriceLinkActivated);
sayEdit = new LineEditUnfocusable; sayEdit = new LineEditUnfocusable;
sayEdit->setMaxLength(MAX_TEXT_LENGTH); sayEdit->setMaxLength(MAX_TEXT_LENGTH);
connect(sayEdit, &LineEditUnfocusable::returnPressed, this, &TabMessage::sendMessage); connect(sayEdit, &LineEditUnfocusable::returnPressed, this, &TabMessage::sendMessage);

View file

@ -70,6 +70,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor,
connect(chatView, &ChatView::showMentionPopup, this, &TabRoom::actShowMentionPopup); connect(chatView, &ChatView::showMentionPopup, this, &TabRoom::actShowMentionPopup);
connect(chatView, &ChatView::messageClickedSignal, this, &TabRoom::focusTab); connect(chatView, &ChatView::messageClickedSignal, this, &TabRoom::focusTab);
connect(chatView, &ChatView::openMessageDialog, this, &TabRoom::openMessageDialog); connect(chatView, &ChatView::openMessageDialog, this, &TabRoom::openMessageDialog);
connect(chatView, &ChatView::cockatriceLinkActivated, this, &TabRoom::cockatriceLinkActivated);
connect(chatView, &ChatView::showCardInfoPopup, this, &TabRoom::showCardInfoPopup); connect(chatView, &ChatView::showCardInfoPopup, this, &TabRoom::showCardInfoPopup);
connect(chatView, &ChatView::deleteCardInfoPopup, this, &TabRoom::deleteCardInfoPopup); connect(chatView, &ChatView::deleteCardInfoPopup, this, &TabRoom::deleteCardInfoPopup);
connect(chatView, &ChatView::addMentionTag, this, &TabRoom::addMentionTag); connect(chatView, &ChatView::addMentionTag, this, &TabRoom::addMentionTag);

View file

@ -851,6 +851,7 @@ void TabSupervisor::addRoomTab(const ServerInfo_Room &info, bool setCurrent)
connect(tab, &TabRoom::maximizeClient, this, &TabSupervisor::maximizeMainWindow); connect(tab, &TabRoom::maximizeClient, this, &TabSupervisor::maximizeMainWindow);
connect(tab, &TabRoom::roomClosing, this, &TabSupervisor::roomLeft); connect(tab, &TabRoom::roomClosing, this, &TabSupervisor::roomLeft);
connect(tab, &TabRoom::openMessageDialog, this, &TabSupervisor::addMessageTab); connect(tab, &TabRoom::openMessageDialog, this, &TabSupervisor::addMessageTab);
connect(tab, &TabRoom::cockatriceLinkActivated, this, &TabSupervisor::cockatriceLinkActivated);
myAddTab(tab); myAddTab(tab);
roomTabs.insert(info.room_id(), tab); roomTabs.insert(info.room_id(), tab);
if (setCurrent) { if (setCurrent) {
@ -924,6 +925,7 @@ TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus
tab = new TabMessage(this, client, *userInfo, otherUser, userOnline); tab = new TabMessage(this, client, *userInfo, otherUser, userOnline);
connect(tab, &TabMessage::talkClosing, this, &TabSupervisor::talkLeft); connect(tab, &TabMessage::talkClosing, this, &TabSupervisor::talkLeft);
connect(tab, &TabMessage::maximizeClient, this, &TabSupervisor::maximizeMainWindow); connect(tab, &TabMessage::maximizeClient, this, &TabSupervisor::maximizeMainWindow);
connect(tab, &TabMessage::cockatriceLinkActivated, this, &TabSupervisor::cockatriceLinkActivated);
myAddTab(tab); myAddTab(tab);
messageTabs.insert(receiverName, tab); messageTabs.insert(receiverName, tab);
if (focus) { if (focus) {

View file

@ -169,6 +169,7 @@ signals:
void localGameEnded(); void localGameEnded();
void adminLockChanged(bool lock); void adminLockChanged(bool lock);
void showWindowIfHidden(); void showWindowIfHidden();
void cockatriceLinkActivated(const QString &url);
public slots: public slots:
void openDeckInNewTab(const LoadedDeck &deckToOpen); void openDeckInNewTab(const LoadedDeck &deckToOpen);

View file

@ -40,6 +40,7 @@
#include "intents/intent_connect_to_server.h" #include "intents/intent_connect_to_server.h"
#include "intents/intent_login.h" #include "intents/intent_login.h"
#include "intents/intent_open_server_room_by_name.h" #include "intents/intent_open_server_room_by_name.h"
#include "intents/url_parser.h"
#include "logger.h" #include "logger.h"
#include "version_string.h" #include "version_string.h"
#include "widgets/dialogs/dlg_connect.h" #include "widgets/dialogs/dlg_connect.h"
@ -508,6 +509,7 @@ MainWindow::MainWindow(QWidget *parent)
connect(tabSupervisor, &TabSupervisor::setMenu, this, &MainWindow::updateTabMenu); connect(tabSupervisor, &TabSupervisor::setMenu, this, &MainWindow::updateTabMenu);
connect(tabSupervisor, &TabSupervisor::localGameEnded, this, &MainWindow::localGameEnded); connect(tabSupervisor, &TabSupervisor::localGameEnded, this, &MainWindow::localGameEnded);
connect(tabSupervisor, &TabSupervisor::showWindowIfHidden, this, &MainWindow::showWindowIfHidden); connect(tabSupervisor, &TabSupervisor::showWindowIfHidden, this, &MainWindow::showWindowIfHidden);
connect(tabSupervisor, &TabSupervisor::cockatriceLinkActivated, this, &MainWindow::handleCockatriceLink);
connect(connectionController, &ConnectionController::tabSupervisorStartRequested, tabSupervisor, connect(connectionController, &ConnectionController::tabSupervisorStartRequested, tabSupervisor,
&TabSupervisor::start); &TabSupervisor::start);
connect(connectionController, &ConnectionController::tabSupervisorStopRequested, tabSupervisor, connect(connectionController, &ConnectionController::tabSupervisorStopRequested, tabSupervisor,
@ -861,6 +863,12 @@ void MainWindow::showWindowIfHidden()
show(); show();
} }
void MainWindow::handleCockatriceLink(const QString &url)
{
auto urlParser = new IntentUrlParser(this, this);
urlParser->handle(url);
}
void MainWindow::cardDatabaseLoadingFailed() void MainWindow::cardDatabaseLoadingFailed()
{ {
if (askedForDbUpdater) { if (askedForDbUpdater) {

View file

@ -84,6 +84,7 @@ private slots:
void actOpenSettingsFolder(); void actOpenSettingsFolder();
void actShow(); void actShow();
void showWindowIfHidden(); void showWindowIfHidden();
void handleCockatriceLink(const QString &url);
void cardUpdateError(QProcess::ProcessError err); void cardUpdateError(QProcess::ProcessError err);
void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus); void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus);