From 8b8b39ec0e3a5322a85fdd7c1bb5f0d22d2c433e Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Sat, 1 Dec 2018 09:28:55 +0800 Subject: [PATCH] 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. --- dist/license.md | 3 ++ .../icons/16x16/connected_notification.png | Bin 0 -> 607 bytes dist/qt_themes/colorful/style.qrc | 1 + dist/qt_themes/colorful_dark/style.qrc | 1 + dist/qt_themes/default/default.qrc | 2 ++ .../icons/16x16/connected_notification.png | Bin 0 -> 517 bytes .../icons/16x16/connected_notification.png | Bin 0 -> 526 bytes dist/qt_themes/qdarkstyle/style.qrc | 1 + license.txt | 1 + src/citra_qt/multiplayer/chat_room.cpp | 34 ++++++++++++++++-- src/citra_qt/multiplayer/chat_room.h | 1 + src/citra_qt/multiplayer/client_room.cpp | 1 + src/citra_qt/multiplayer/client_room.h | 1 + src/citra_qt/multiplayer/state.cpp | 29 ++++++++++++++- src/citra_qt/multiplayer/state.h | 4 +++ 15 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 dist/qt_themes/colorful/icons/16x16/connected_notification.png create mode 100644 dist/qt_themes/default/icons/16x16/connected_notification.png create mode 100644 dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png diff --git a/dist/license.md b/dist/license.md index f7f74ab5a..3300388a7 100644 --- a/dist/license.md +++ b/dist/license.md @@ -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 diff --git a/dist/qt_themes/colorful/icons/16x16/connected_notification.png b/dist/qt_themes/colorful/icons/16x16/connected_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..0dfe032d58160f1a24f54040964b5b5f2e97a724 GIT binary patch literal 607 zcmV-l0-*hgP)Al}+;~l5Q zvTbMg9^UuieCIpx-&Ifi=FyAs#93blXAAVk6T1cLkfNhvWKhv_s&Zmhz^+*5hT57% zOF_;n8U!$wS#BAtE4F_}x9_uN3h#||R;qIF^}7#eT+d|uz94mKnfhXH31H9huIfo8 zi^zay+&fR64K1E@-JxlR%=;^#xv6PIV?+G_s+UCMg=h4^)8}bd)f^GE)~e{qV!srN z3)gS0wp1Shp|S7zWIB^sFUGWeiInyCIgX))T9mw`_&DPPf>r?`06RRtwXvb@^0G)- zvhTjLk|#o z6b%F40B icons/index.theme icons/16x16/connected.png + icons/16x16/connected_notification.png icons/16x16/disconnected.png icons/16x16/lock.png icons/48x48/bad_folder.png diff --git a/dist/qt_themes/colorful_dark/style.qrc b/dist/qt_themes/colorful_dark/style.qrc index 00a7598fe..9c531fe1b 100644 --- a/dist/qt_themes/colorful_dark/style.qrc +++ b/dist/qt_themes/colorful_dark/style.qrc @@ -2,6 +2,7 @@ icons/index.theme ../colorful/icons/16x16/connected.png + ../colorful/icons/16x16/connected_notification.png ../colorful/icons/16x16/disconnected.png icons/16x16/lock.png ../colorful/icons/48x48/bad_folder.png diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc index 4840532a2..cf011680f 100644 --- a/dist/qt_themes/default/default.qrc +++ b/dist/qt_themes/default/default.qrc @@ -10,6 +10,8 @@ icons/16x16/disconnected.png + icons/16x16/connected_notification.png + icons/16x16/lock.png icons/48x48/bad_folder.png diff --git a/dist/qt_themes/default/icons/16x16/connected_notification.png b/dist/qt_themes/default/icons/16x16/connected_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..e64901378b000db1871d03db88af5f06c454cb01 GIT binary patch literal 517 zcmV+g0{Z=lP)=oH5kv??W6KBf~W^uD5aJbuU z?1_l^zTebLSsabzI+llOtNV%$o|VX}coVWS{*HQ<)uxI9Ck6S4=9S_D*7>#60 zKOeWZFLYif{X3q+e8sGmn$7ygk(FzSW2Em%_GsHe=P~>O_ZNK92NF;*00000NkvXX Hu0mjf^1s=j literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png b/dist/qt_themes/qdarkstyle/icons/16x16/connected_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..7cd8b9d2930d99340778360c68bf28923a66609c GIT binary patch literal 526 zcmV+p0`dKcP)2WpUc5F_z--H^xh8V++PPa z33XEKY?gdgT~jBj|8aa2I1da0uYrTWBVZ~bo|TbKsi966PC1e~wUOg4^}G7fa*Xuu zD}ChCz!%^SaA1RG7s}j2{h+R>H`N~{31-R!rn93J^+qAGq+VB#t4(!Zk5kRaL%_XE zZwD|7j010%$4)l2G8(}uq%QEa{|BSMKwqd&%+I&Gt@=}pSAmByUBvjj+&Xa?AhHBA zg=3C-cdUL$odY1E5y>+Kw}I7x%KDK!!OK4KI`Cf7rGGvQYAIF5bAb`SY)*g}wJhau z3ikh)8lzs_F#r7`PS@PYUsN&*fWd_-?rE zoG=u!$6)85fB0bU!s5G7mw@{bF;~)+PNPxVI=FT#;4H+?AU)r`u=oPtH#>3~=2X*b QzyJUM07*qoM6N<$g7iq=m;e9( literal 0 HcmV?d00001 diff --git a/dist/qt_themes/qdarkstyle/style.qrc b/dist/qt_themes/qdarkstyle/style.qrc index c151238e1..0a4424b7f 100644 --- a/dist/qt_themes/qdarkstyle/style.qrc +++ b/dist/qt_themes/qdarkstyle/style.qrc @@ -3,6 +3,7 @@ icons/index.theme icons/16x16/connected.png icons/16x16/disconnected.png + icons/16x16/connected_notification.png icons/16x16/lock.png icons/48x48/bad_folder.png icons/48x48/chip.png diff --git a/license.txt b/license.txt index fe47594db..93078088c 100644 --- a/license.txt +++ b/license.txt @@ -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 diff --git a/src/citra_qt/multiplayer/chat_room.cpp b/src/citra_qt/multiplayer/chat_room.cpp index 94dc6c357..4c4150d6a 100644 --- a/src/citra_qt/multiplayer/chat_room.cpp +++ b/src/citra_qt/multiplayer/chat_room.cpp @@ -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] <%3> %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] <%3> %5") + .arg(timestamp, color, name.toHtmlEscaped(), style, message.toHtmlEscaped()); } private: static constexpr std::array 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)); } } diff --git a/src/citra_qt/multiplayer/chat_room.h b/src/citra_qt/multiplayer/chat_room.h index 58cb25e83..70f786dbe 100644 --- a/src/citra_qt/multiplayer/chat_room.h +++ b/src/citra_qt/multiplayer/chat_room.h @@ -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; diff --git a/src/citra_qt/multiplayer/client_room.cpp b/src/citra_qt/multiplayer/client_room.cpp index 84a425189..d87a3e6e1 100644 --- a/src/citra_qt/multiplayer/client_room.cpp +++ b/src/citra_qt/multiplayer/client_room.cpp @@ -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(); } diff --git a/src/citra_qt/multiplayer/client_room.h b/src/citra_qt/multiplayer/client_room.h index 7d4f2b238..c40d324c3 100644 --- a/src/citra_qt/multiplayer/client_room.h +++ b/src/citra_qt/multiplayer/client_room.h @@ -27,6 +27,7 @@ public slots: signals: void RoomInformationChanged(const Network::RoomInformation&); void StateChanged(const Network::RoomMember::State&); + void ShowNotification(); private: void Disconnect(); diff --git a/src/citra_qt/multiplayer/state.cpp b/src/citra_qt/multiplayer/state.cpp index 2970c0691..b14e79f9b 100644 --- a/src/citra_qt/multiplayer/state.cpp +++ b/src/citra_qt/multiplayer/state.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -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::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()) { diff --git a/src/citra_qt/multiplayer/state.h b/src/citra_qt/multiplayer/state.h index a49288e83..8061d18cf 100644 --- a/src/citra_qt/multiplayer/state.h +++ b/src/citra_qt/multiplayer/state.h @@ -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 state_callback_handle; Network::RoomMember::CallbackHandle error_callback_handle; + + bool show_notification = false; }; Q_DECLARE_METATYPE(Common::WebResult);