From 3710dff0cd96f284e51a2bb1652252fbee262c0b Mon Sep 17 00:00:00 2001 From: Xavier Michelon Date: Thu, 3 Oct 2024 15:09:12 +0200 Subject: [PATCH] feat(BRIDGE-142): bridge icon can be removed from the menu bar on macOS. --- .../bridge-gui/bridge-gui/QMLBackend.cpp | 35 +++++++++++++++++-- .../bridge-gui/bridge-gui/QMLBackend.h | 5 +++ .../bridge-gui/bridge-gui/Settings.cpp | 17 ++++++++- .../frontend/bridge-gui/bridge-gui/Settings.h | 2 ++ .../bridge-gui/bridge-gui/TrayIcon.cpp | 21 +++++++++-- .../frontend/bridge-gui/bridge-gui/TrayIcon.h | 2 -- .../bridge-gui/qml/GeneralSettings.qml | 13 +++++++ 7 files changed, 88 insertions(+), 7 deletions(-) diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp index 97e9ac34..06a0d673 100644 --- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp @@ -25,6 +25,7 @@ #include #include +#include "Settings.h" #define HANDLE_EXCEPTION(x) try { x } \ catch (Exception const &e) { emit fatalError(e); } \ @@ -59,15 +60,19 @@ QMLBackend::QMLBackend() /// \param[in] serviceConfig //**************************************************************************************************************************************************** void QMLBackend::init(GRPCConfig const &serviceConfig) { + Log &log = app().log(); + log.info(QString("Connecting to gRPC service")); + trayIcon_.reset(new TrayIcon()); + connect(this, &QMLBackend::trayIconVisibleChanged, trayIcon_.get(), &TrayIcon::setVisible); + log.info(QString("Tray icon is visible: %1").arg(trayIcon_->isVisible() ? "true" : "false")); this->setNormalTrayIcon(); + connect(this, &QMLBackend::fatalError, &app(), &AppController::onFatalError); users_ = new UserList(this); - Log &log = app().log(); - log.info(QString("Connecting to gRPC service")); app().grpc().setLog(&log); this->connectGrpcEvents(); @@ -731,6 +736,32 @@ void QMLBackend::setDockIconVisible(bool visible) { } +//**************************************************************************************************************************************************** +/// \param[in] visible Should the tray icon be visible. +//**************************************************************************************************************************************************** +void QMLBackend::setTrayIconVisible(bool visible) { + HANDLE_EXCEPTION( + AppController& app = ::app(); + if (visible == app.settings().trayIconVisible()) { + return; + } + app.settings().setTrayIconVisible(visible); + emit trayIconVisibleChanged(visible); + app.log().info(QString("Changing tray icon visibility to %1").arg(visible ? "true" : "false")); + ) +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +bool QMLBackend::trayIconVisible() const { + HANDLE_EXCEPTION_RETURN_BOOL( + return app().settings().trayIconVisible(); + ) +} + + //**************************************************************************************************************************************************** /// \param[in] active Should we activate autostart. //**************************************************************************************************************************************************** diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h index 992daf09..55ad0e8f 100644 --- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h +++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h @@ -102,6 +102,7 @@ public: // Qt/QML properties. Note that the NOTIFY-er signal is required even fo Q_PROPERTY(QVariantList bugQuestions READ bugQuestions NOTIFY bugQuestionsChanged) Q_PROPERTY(UserList *users MEMBER users_ NOTIFY usersChanged) Q_PROPERTY(bool dockIconVisible READ dockIconVisible WRITE setDockIconVisible NOTIFY dockIconVisibleChanged) + Q_PROPERTY(bool trayIconVisible READ trayIconVisible WRITE setTrayIconVisible NOTIFY trayIconVisibleChanged) // Qt Property system setters & getters. bool showOnStartup() const; ///< Getter for the 'showOnStartup' property. @@ -141,6 +142,9 @@ public: // Qt/QML properties. Note that the NOTIFY-er signal is required even fo QVariantList bugQuestions() const; ///< Getter for the 'bugQuestions' property. void setDockIconVisible(bool visible); ///< Setter for the 'dockIconVisible' property. bool dockIconVisible() const;; ///< Getter for the 'dockIconVisible' property. + void setTrayIconVisible(bool visible); ///< Setter for the 'trayIconVisible' property. + bool trayIconVisible() const; ///< Getter for the 'trayIconVisible' property. + signals: // Signal used by the Qt property system. Many of them are unused but required to avoid warning from the QML engine. void showSplashScreenChanged(bool value); /// #include +#include "Settings.h" using namespace bridgepp; @@ -195,7 +196,7 @@ TrayIcon::TrayIcon() } this->setIcon(); - this->show(); + this->setVisible(app().settings().trayIconVisible()); // TrayIcon does not expose its screen, so we connect relevant screen events to our DPI change handler. for (QScreen *screen: QGuiApplication::screens()) { @@ -209,7 +210,6 @@ TrayIcon::TrayIcon() connect(&iconRefreshTimer_, &QTimer::timeout, this, &TrayIcon::onIconRefreshTimer); } - //**************************************************************************************************************************************************** // //**************************************************************************************************************************************************** @@ -322,21 +322,38 @@ void TrayIcon::setState(TrayIcon::State state, QString const &stateString, QStri this->generateStatusIcon(statusIconPath, stateColor(state)); } +//**************************************************************************************************************************************************** +/// \brief A helper struct to temporarily force the tray to be visible. Useful for operations that do not work when the tray is not visible, +/// such as showMessage(). +//**************************************************************************************************************************************************** +struct ScopedTrayVisibility { + explicit ScopedTrayVisibility(TrayIcon& trayIcon) : trayIcon_(trayIcon), wasVisible_(app().settings().trayIconVisible()) { + trayIcon_.setVisible(true); + } + ~ScopedTrayVisibility() { trayIcon_.setVisible(wasVisible_); } + +private: + TrayIcon &trayIcon_; + bool wasVisible_; +}; //**************************************************************************************************************************************************** /// \param[in] title The title. /// \param[in] message The message. //**************************************************************************************************************************************************** void TrayIcon::showErrorPopupNotification(QString const &title, QString const &message) { + ScopedTrayVisibility visible(*this); this->showMessage(title, message, notificationErrorIcon_); } + //**************************************************************************************************************************************************** /// Used only by user notifications received from the event loop /// \param[in] title The title. /// \param[in] subtitle The subtitle. //**************************************************************************************************************************************************** void TrayIcon::showUserNotification(QString const &title, QString const &subtitle) { + ScopedTrayVisibility visible(*this); this->showMessage(title, subtitle, QSystemTrayIcon::NoIcon); } diff --git a/internal/frontend/bridge-gui/bridge-gui/TrayIcon.h b/internal/frontend/bridge-gui/bridge-gui/TrayIcon.h index 3bbbbd91..77ba70b2 100644 --- a/internal/frontend/bridge-gui/bridge-gui/TrayIcon.h +++ b/internal/frontend/bridge-gui/bridge-gui/TrayIcon.h @@ -43,8 +43,6 @@ public: // data members void setState(State state, QString const& stateString, QString const &statusIconPath); ///< Set the state of the icon void showErrorPopupNotification(QString const& title, QString const &message); ///< Display a pop up notification. void showUserNotification(QString const& title, QString const &subtitle); ///< Display an OS pop up notification (without icon). - - signals: void selectUser(QString const& userID, bool forceShowWindow); ///< Signal for selecting a user with a given userID diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/GeneralSettings.qml b/internal/frontend/bridge-gui/bridge-gui/qml/GeneralSettings.qml index 0ef91bc4..76955ad9 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/GeneralSettings.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/GeneralSettings.qml @@ -147,6 +147,19 @@ SettingsView { onClicked: Backend.changeColorScheme(darkMode.checked ? "light" : "dark") } + SettingsItem { + id: trayIconVisible + Layout.fillWidth: true + checked: Backend.trayIconVisible + colorScheme: root.colorScheme + description: qsTr("Show the Bridge icon in the menu bar. When the Bridge icon is not visible, launch the " + + "application again to display the main window.") + text: qsTr("Show the Bridge icon in the menu bar") + type: SettingsItem.Toggle + visible: (Backend.goos === "darwin") && root._isAdvancedShown + + onClicked: Backend.trayIconVisible = !trayIconVisible.checked + } SettingsItem { id: allMail Layout.fillWidth: true