diff --git a/src/mumble/Log.cpp b/src/mumble/Log.cpp index 8800a9401d..255d879639 100644 --- a/src/mumble/Log.cpp +++ b/src/mumble/Log.cpp @@ -421,6 +421,8 @@ Log::Log(QObject *p) : QObject(p) { #endif uiLastId = 0; qdDate = QDate::currentDate(); + + QObject::connect(this, &Log::highlightSpawned, Global::get().mw, &MainWindow::highlightWindow); } // Display order in settingsscreen, allows to insert new events without breaking config-compatibility with older @@ -812,7 +814,7 @@ void Log::log(MsgType mt, const QString &console, const QString &terse, bool own if (!(Global::get().mw->isActiveWindow() && Global::get().mw->qdwLog->isVisible())) { // Message notification with window highlight if (flags & Settings::LogHighlight) { - QApplication::alert(Global::get().mw); + emit highlightSpawned(); } // Message notification with balloon tooltips diff --git a/src/mumble/Log.h b/src/mumble/Log.h index c8c2cffe7a..18f4252ea8 100644 --- a/src/mumble/Log.h +++ b/src/mumble/Log.h @@ -161,6 +161,9 @@ public slots: signals: /// Signal emitted when there was a message received whose type was configured to spawn a notification void notificationSpawned(QString title, QString body, QSystemTrayIcon::MessageIcon icon); + + /// Signal emitted when there was a message received whose type was configured to highlight the application + void highlightSpawned(); }; class LogMessage { diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index 19eeca9b17..23b33317fc 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -111,6 +111,7 @@ MainWindow::MainWindow(QWidget *p) SvgIcon::addSvgPixmapsToIcon(qiTalkingOn, QLatin1String("skin:talking_on.svg")); SvgIcon::addSvgPixmapsToIcon(qiTalkingShout, QLatin1String("skin:talking_alt.svg")); SvgIcon::addSvgPixmapsToIcon(qiTalkingWhisper, QLatin1String("skin:talking_whisper.svg")); + SvgIcon::addSvgPixmapsToIcon(m_iconInformation, QLatin1String("skin:Information_icon.svg")); #ifdef Q_OS_MAC if (QFile::exists(QLatin1String("skin:mumble.icns"))) @@ -718,6 +719,14 @@ void MainWindow::changeEvent(QEvent *e) { } } + // Unhighlight the tray icon when receiving focus + if (e->type() == QEvent::ActivationChange) { + if (isActiveWindow() && Global::get().trayIcon != nullptr) { + emit windowActivated(); + } + return; + } + QWidget::changeEvent(e); } @@ -3633,6 +3642,10 @@ void MainWindow::showRaiseWindow() { }); } +void MainWindow::highlightWindow() { + QApplication::alert(this); +} + void MainWindow::on_qaTalkingUIToggle_triggered() { if (!Global::get().talkingUI) { qCritical("MainWindow: Attempting to show Talking UI before it has been created!"); diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h index 69b7968e8e..29badc9f48 100644 --- a/src/mumble/MainWindow.h +++ b/src/mumble/MainWindow.h @@ -80,6 +80,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindow { QIcon qiIcon, qiIconMutePushToMute, qiIconMuteSelf, qiIconMuteServer, qiIconDeafSelf, qiIconDeafServer, qiIconMuteSuppressed; QIcon qiTalkingOn, qiTalkingWhisper, qiTalkingShout, qiTalkingOff; + QIcon m_iconInformation; std::unordered_map< unsigned int, qt_unique_ptr< UserLocalNicknameDialog > > qmUserNicknameTracker; /// "Action" for when there are no actions available @@ -387,6 +388,8 @@ public slots: void toggleSearchDialogVisibility(); /// Enables or disables the recording feature void enableRecording(bool recordingAllowed); + /// Invokes OS native window highlighting + void highlightWindow(); signals: /// Signal emitted when the server and the client have finished /// synchronizing (after a new connection). @@ -404,6 +407,8 @@ public slots: /// Signal emitted when the window manager notifies the Mumble MainWindow that the application was just minimized void windowMinimized(); + /// Signal emitted whenever the Mumble MainWindow regains the active state from the window manager + void windowActivated(); public: MainWindow(QWidget *parent); diff --git a/src/mumble/widgets/TrayIcon.cpp b/src/mumble/widgets/TrayIcon.cpp index c361bcfc32..186d92eef2 100644 --- a/src/mumble/widgets/TrayIcon.cpp +++ b/src/mumble/widgets/TrayIcon.cpp @@ -13,7 +13,7 @@ #include TrayIcon::TrayIcon() : QSystemTrayIcon(Global::get().mw), m_statusIcon(Global::get().mw->qiIcon) { - setIcon(m_statusIcon); + applyIcon(m_statusIcon); setToolTip("Mumble"); @@ -27,6 +27,13 @@ TrayIcon::TrayIcon() : QSystemTrayIcon(Global::get().mw), m_statusIcon(Global::g Global::get().l, &Log::notificationSpawned, this, [this](QString title, QString body, QSystemTrayIcon::MessageIcon icon) { showMessage(title, body, icon); }); + QObject::connect(Global::get().l, &Log::highlightSpawned, this, &TrayIcon::on_timer_triggered); + QObject::connect(Global::get().mw, &MainWindow::windowActivated, this, &TrayIcon::on_tray_unhighlight); + + m_highlightTimer = new QTimer(this); + m_highlightTimer->setSingleShot(true); + QObject::connect(m_highlightTimer, &QTimer::timeout, this, &TrayIcon::on_timer_triggered); + QObject::connect(this, &QSystemTrayIcon::activated, this, &TrayIcon::on_icon_clicked); // messageClicked is buggy in Qt on some platforms and we can not do anything about this (QTBUG-87329) @@ -88,7 +95,7 @@ void TrayIcon::on_icon_update() { if (&newIcon.get() != &m_statusIcon.get()) { m_statusIcon = newIcon; - setIcon(m_statusIcon); + applyIcon(m_statusIcon); } } @@ -129,6 +136,11 @@ void TrayIcon::updateContextMenu() { m_contextMenu->addAction(Global::get().mw->qaQuit); } +void TrayIcon::applyIcon(QIcon &icon) { + setIcon(icon); + m_currentIcon = &icon; +} + void TrayIcon::toggleShowHide() { if (Global::get().mw->isVisible() && !Global::get().mw->isMinimized()) { on_hideAction_triggered(); @@ -175,3 +187,31 @@ void TrayIcon::on_windowMinimized() { on_hideAction_triggered(); } + +void TrayIcon::on_tray_unhighlight() { + if (m_highlightTimer == nullptr || !m_highlightTimer->isActive()) { + return; + } + + m_highlightTimer->stop(); + applyIcon(m_statusIcon); +} + +void TrayIcon::on_timer_triggered() { + // We implement tray icon "highlighting" by blinking the + // current status icon every few seconds until the MainWindow + // receives focus. + // This will only be happening, if the user selects "highlight" + // for a specific message in the messages settings table. + // Normal window highlighting - which desktops usually implement + // by blinking the application in the task bar - is invisible + // if the application is hidden to tray. + + if (m_currentIcon == &m_statusIcon.get()) { + applyIcon(Global::get().mw->m_iconInformation); + m_highlightTimer->start(500); + } else { + applyIcon(m_statusIcon); + m_highlightTimer->start(2000); + } +} diff --git a/src/mumble/widgets/TrayIcon.h b/src/mumble/widgets/TrayIcon.h index 369927e3fa..e01a6e8dce 100644 --- a/src/mumble/widgets/TrayIcon.h +++ b/src/mumble/widgets/TrayIcon.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -24,17 +25,24 @@ public slots: void on_hideAction_triggered(); void on_showAction_triggered(); + void on_icon_update(); + void on_tray_unhighlight(); + private: std::reference_wrapper< QIcon > m_statusIcon; - QMenu *m_contextMenu = nullptr; - QAction *m_showAction = nullptr; - QAction *m_hideAction = nullptr; + QIcon *m_currentIcon = nullptr; + QMenu *m_contextMenu = nullptr; + QAction *m_showAction = nullptr; + QAction *m_hideAction = nullptr; + QTimer *m_highlightTimer = nullptr; void updateContextMenu(); + void applyIcon(QIcon &icon); private slots: void on_icon_clicked(QSystemTrayIcon::ActivationReason reason); void on_windowMinimized(); + void on_timer_triggered(); }; #endif // MUMBLE_MUMBLE_WIDGETS_TRAYICON_H_