citra_qt/multiplayer: Add user ping support

The user would be notified if the message contains "@" followed by the user's nickname or forum username. An alert would be shown, and the icon and message in the status bar would be changed. All notification is only shown if the chat window currently does not have focus.

Also added a connected_notification icon for showing in the status bar.
This commit is contained in:
zhupengfei 2018-12-01 09:28:55 +08:00
parent 6feeaed77e
commit 8b8b39ec0e
No known key found for this signature in database
GPG Key ID: DD129E108BD09378
15 changed files with 76 additions and 3 deletions

3
dist/license.md vendored
View File

@ -4,6 +4,7 @@ Icon Name | License | Origin/Author
--- | --- | ---
qt_themes/default/icons/16x16/checked.png | Free for non-commercial use
qt_themes/default/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/disconnected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/failed.png | Free for non-commercial use
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
@ -16,6 +17,7 @@ qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/checked.png | Free for non-commercial use
qt_themes/qdarkstyle/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/disconnected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/failed.png | Free for non-commercial use
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
@ -27,6 +29,7 @@ qt_themes/qdarkstyle/icons/48x48/no_avatar.png | CC BY-ND 3.0 | https://icons8.c
qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/connected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/connected_notification.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/disconnected.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

View File

@ -2,6 +2,7 @@
<qresource prefix="icons/colorful">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>

View File

@ -2,6 +2,7 @@
<qresource prefix="icons/colorful_dark">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/connected.png">../colorful/icons/16x16/connected.png</file>
<file alias="16x16/connected_notification.png">../colorful/icons/16x16/connected_notification.png</file>
<file alias="16x16/disconnected.png">../colorful/icons/16x16/disconnected.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>

View File

@ -10,6 +10,8 @@
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

View File

@ -3,6 +3,7 @@
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>

View File

@ -345,6 +345,7 @@ Icon Name | License | Origin/Author
--- | --- | ---
checked.png | Free for non-commercial use
connected.png | CC BY-ND 3.0 | https://icons8.com
connected_notification.png | CC BY-ND 3.0 | https://icons8.com
disconnected.png | CC BY-ND 3.0 | https://icons8.com
failed.png | Free for non-commercial use
lock.png | CC BY-ND 3.0 | https://icons8.com

View File

@ -34,6 +34,24 @@ public:
nickname = QString::fromStdString(chat.nickname);
username = QString::fromStdString(chat.username);
message = QString::fromStdString(chat.message);
// Check for user pings
QString cur_nickname, cur_username;
if (auto room = Network::GetRoomMember().lock()) {
cur_nickname = QString::fromStdString(room->GetNickname());
cur_username = QString::fromStdString(room->GetUsername());
}
if (message.contains(QString("@").append(cur_nickname)) ||
(!cur_username.isEmpty() && message.contains(QString("@").append(cur_username)))) {
contains_ping = true;
} else {
contains_ping = false;
}
}
bool ContainsPing() const {
return contains_ping;
}
/// Format the message using the players color
@ -45,19 +63,28 @@ public:
} else {
name = QString("%1 (%2)").arg(nickname, username);
}
return QString("[%1] <font color='%2'>&lt;%3&gt;</font> %4")
.arg(timestamp, color, name.toHtmlEscaped(), message.toHtmlEscaped());
QString style;
if (ContainsPing()) {
// Add a background color to these messages
style = QString("background-color: %1").arg(ping_color);
}
return QString("[%1] <font color='%2'>&lt;%3&gt;</font> <font style='%4'>%5</font>")
.arg(timestamp, color, name.toHtmlEscaped(), style, message.toHtmlEscaped());
}
private:
static constexpr std::array<const char*, 16> player_color = {
{"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222",
"#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "FFFF00"}};
static constexpr char ping_color[] = "#FFFF00";
QString timestamp;
QString nickname;
QString username;
QString message;
bool contains_ping;
};
class StatusMessage {
@ -240,6 +267,9 @@ void ChatRoom::OnChatReceive(const Network::ChatEntry& chat) {
}
auto player = std::distance(members.begin(), it);
ChatMessage m(chat);
if (m.ContainsPing()) {
emit UserPinged();
}
AppendChatMessage(m.GetPlayerChatMessage(player));
}
}

View File

@ -51,6 +51,7 @@ public slots:
signals:
void ChatReceived(const Network::ChatEntry&);
void StatusMessageReceived(const Network::StatusMessageEntry&);
void UserPinged();
private:
static constexpr u32 max_chat_lines = 1000;

View File

@ -49,6 +49,7 @@ ClientRoomWindow::ClientRoomWindow(QWidget* parent)
});
ui->moderation->setDefault(false);
ui->moderation->setAutoDefault(false);
connect(ui->chat, &ChatRoom::UserPinged, this, &ClientRoomWindow::ShowNotification);
UpdateView();
}

View File

@ -27,6 +27,7 @@ public slots:
signals:
void RoomInformationChanged(const Network::RoomInformation&);
void StateChanged(const Network::RoomMember::State&);
void ShowNotification();
private:
void Disconnect();

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <QAction>
#include <QApplication>
#include <QIcon>
#include <QMessageBox>
#include <QStandardItemModel>
@ -49,6 +50,13 @@ MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_lis
connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom);
connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom);
connect(static_cast<QApplication*>(QApplication::instance()), &QApplication::focusChanged, this,
[this](QWidget* /*old*/, QWidget* now) {
if (client_room && client_room->isAncestorOf(now)) {
HideNotification();
}
});
}
MultiplayerState::~MultiplayerState() {
@ -173,7 +181,9 @@ void MultiplayerState::OnAnnounceFailed(const Common::WebResult& result) {
}
void MultiplayerState::UpdateThemedIcons() {
if (current_state == Network::RoomMember::State::Joined) {
if (show_notification) {
status_icon->setPixmap(QIcon::fromTheme("connected_notification").pixmap(16));
} else if (current_state == Network::RoomMember::State::Joined) {
status_icon->setPixmap(QIcon::fromTheme("connected").pixmap(16));
} else {
status_icon->setPixmap(QIcon::fromTheme("disconnected").pixmap(16));
@ -225,11 +235,28 @@ bool MultiplayerState::OnCloseRoom() {
return true;
}
void MultiplayerState::ShowNotification() {
if (client_room && client_room->isAncestorOf(QApplication::focusWidget()))
return; // Do not show notification if the chat window currently has focus
show_notification = true;
QApplication::alert(nullptr);
status_icon->setPixmap(QIcon::fromTheme("connected_notification").pixmap(16));
status_text->setText(tr("New Messages Received"));
}
void MultiplayerState::HideNotification() {
show_notification = false;
status_icon->setPixmap(QIcon::fromTheme("connected").pixmap(16));
status_text->setText(tr("Connected"));
}
void MultiplayerState::OnOpenNetworkRoom() {
if (auto member = Network::GetRoomMember().lock()) {
if (member->IsConnected()) {
if (client_room == nullptr) {
client_room = new ClientRoomWindow(this);
connect(client_room, &ClientRoomWindow::ShowNotification, this,
&MultiplayerState::ShowNotification);
}
const std::string host_username = member->GetRoomInformation().host_username;
if (host_username.empty()) {

View File

@ -48,6 +48,8 @@ public slots:
void OnDirectConnectToRoom();
void OnAnnounceFailed(const Common::WebResult&);
void UpdateThemedIcons();
void ShowNotification();
void HideNotification();
signals:
void NetworkStateChanged(const Network::RoomMember::State&);
@ -69,6 +71,8 @@ private:
bool has_mod_perms = false;
Network::RoomMember::CallbackHandle<Network::RoomMember::State> state_callback_handle;
Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle;
bool show_notification = false;
};
Q_DECLARE_METATYPE(Common::WebResult);