feat(BRIDGE-142): bridge icon can be removed from the menu bar on macOS.

This commit is contained in:
Xavier Michelon
2024-10-03 15:09:12 +02:00
parent f5bc6ad1f0
commit 3710dff0cd
7 changed files with 88 additions and 7 deletions

View File

@ -25,6 +25,7 @@
#include <bridgepp/GRPC/GRPCClient.h> #include <bridgepp/GRPC/GRPCClient.h>
#include <bridgepp/Worker/Overseer.h> #include <bridgepp/Worker/Overseer.h>
#include "Settings.h"
#define HANDLE_EXCEPTION(x) try { x } \ #define HANDLE_EXCEPTION(x) try { x } \
catch (Exception const &e) { emit fatalError(e); } \ catch (Exception const &e) { emit fatalError(e); } \
@ -59,15 +60,19 @@ QMLBackend::QMLBackend()
/// \param[in] serviceConfig /// \param[in] serviceConfig
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
void QMLBackend::init(GRPCConfig const &serviceConfig) { void QMLBackend::init(GRPCConfig const &serviceConfig) {
Log &log = app().log();
log.info(QString("Connecting to gRPC service"));
trayIcon_.reset(new TrayIcon()); 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(); this->setNormalTrayIcon();
connect(this, &QMLBackend::fatalError, &app(), &AppController::onFatalError); connect(this, &QMLBackend::fatalError, &app(), &AppController::onFatalError);
users_ = new UserList(this); users_ = new UserList(this);
Log &log = app().log();
log.info(QString("Connecting to gRPC service"));
app().grpc().setLog(&log); app().grpc().setLog(&log);
this->connectGrpcEvents(); 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. /// \param[in] active Should we activate autostart.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************

View File

@ -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(QVariantList bugQuestions READ bugQuestions NOTIFY bugQuestionsChanged)
Q_PROPERTY(UserList *users MEMBER users_ NOTIFY usersChanged) Q_PROPERTY(UserList *users MEMBER users_ NOTIFY usersChanged)
Q_PROPERTY(bool dockIconVisible READ dockIconVisible WRITE setDockIconVisible NOTIFY dockIconVisibleChanged) 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. // Qt Property system setters & getters.
bool showOnStartup() const; ///< Getter for the 'showOnStartup' property. 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. QVariantList bugQuestions() const; ///< Getter for the 'bugQuestions' property.
void setDockIconVisible(bool visible); ///< Setter for the 'dockIconVisible' property. void setDockIconVisible(bool visible); ///< Setter for the 'dockIconVisible' property.
bool dockIconVisible() const;; ///< Getter 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. 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); ///<Signal for the change of the 'showSplashScreen' property. void showSplashScreenChanged(bool value); ///<Signal for the change of the 'showSplashScreen' property.
@ -175,6 +179,7 @@ signals: // Signal used by the Qt property system. Many of them are unused but r
void isAutostartOnChanged(bool value); ///<Signal for the change of the 'isAutostartOn' property. void isAutostartOnChanged(bool value); ///<Signal for the change of the 'isAutostartOn' property.
void usersChanged(UserList *users); ///<Signal for the change of the 'users' property. void usersChanged(UserList *users); ///<Signal for the change of the 'users' property.
void dockIconVisibleChanged(bool value); ///<Signal for the change of the 'dockIconVisible' property. void dockIconVisibleChanged(bool value); ///<Signal for the change of the 'dockIconVisible' property.
void trayIconVisibleChanged(bool value); ///< Signal for the change of the 'trayIconVisible' property.
void receivedUserNotification(bridgepp::UserNotification const& notification); ///< Signal to display the userNotification modal void receivedUserNotification(bridgepp::UserNotification const& notification); ///< Signal to display the userNotification modal

View File

@ -28,6 +28,7 @@ namespace {
QString const settingsFileName = "bridge-gui.ini"; ///< The name of the settings file. QString const settingsFileName = "bridge-gui.ini"; ///< The name of the settings file.
QString const keyUseSoftwareRenderer = "UseSoftwareRenderer"; ///< The key for storing the 'Use software rendering' setting. QString const keyUseSoftwareRenderer = "UseSoftwareRenderer"; ///< The key for storing the 'Use software rendering' setting.
QString const keyTrayIconVisible = "TrayIconVisible"; ///< The key for storing the 'Tray icon visible' setting.
} }
@ -36,7 +37,7 @@ QString const keyUseSoftwareRenderer = "UseSoftwareRenderer"; ///< The key for s
// //
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
Settings::Settings() Settings::Settings()
: settings_(QDir(userConfigDir()).absoluteFilePath("bridge-gui.ini"), QSettings::Format::IniFormat) { : settings_(QDir(userConfigDir()).absoluteFilePath(settingsFileName), QSettings::Format::IniFormat) {
} }
@ -54,3 +55,17 @@ bool Settings::useSoftwareRenderer() const {
void Settings::setUseSoftwareRenderer(bool value) { void Settings::setUseSoftwareRenderer(bool value) {
settings_.setValue(keyUseSoftwareRenderer, value); settings_.setValue(keyUseSoftwareRenderer, value);
} }
//****************************************************************************************************************************************************
/// \param[in] value The value for the 'Tray icon visible' setting.
//****************************************************************************************************************************************************
void Settings::setTrayIconVisible(bool value) {
settings_.setValue(keyTrayIconVisible, value);
}
//****************************************************************************************************************************************************
/// \return The value for the 'Tray icon visible' setting.
//****************************************************************************************************************************************************
bool Settings::trayIconVisible() const {
return settings_.value(keyTrayIconVisible, true).toBool();
}

View File

@ -33,6 +33,8 @@ public: // member functions.
bool useSoftwareRenderer() const; ///< Get the 'Use software renderer' settings value. bool useSoftwareRenderer() const; ///< Get the 'Use software renderer' settings value.
void setUseSoftwareRenderer(bool value); ///< Set the 'Use software renderer' settings value. void setUseSoftwareRenderer(bool value); ///< Set the 'Use software renderer' settings value.
void setTrayIconVisible(bool value); ///< Get the 'Tray icon visible' setting value.
bool trayIconVisible() const; ///< Set the 'Tray icon visible' setting value.
private: // member functions. private: // member functions.
Settings(); ///< Default constructor. Settings(); ///< Default constructor.

View File

@ -21,6 +21,7 @@
#include <bridgepp/Exception/Exception.h> #include <bridgepp/Exception/Exception.h>
#include <bridgepp/BridgeUtils.h> #include <bridgepp/BridgeUtils.h>
#include "Settings.h"
using namespace bridgepp; using namespace bridgepp;
@ -195,7 +196,7 @@ TrayIcon::TrayIcon()
} }
this->setIcon(); 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. // TrayIcon does not expose its screen, so we connect relevant screen events to our DPI change handler.
for (QScreen *screen: QGuiApplication::screens()) { for (QScreen *screen: QGuiApplication::screens()) {
@ -209,7 +210,6 @@ TrayIcon::TrayIcon()
connect(&iconRefreshTimer_, &QTimer::timeout, this, &TrayIcon::onIconRefreshTimer); 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)); 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] title The title.
/// \param[in] message The message. /// \param[in] message The message.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
void TrayIcon::showErrorPopupNotification(QString const &title, QString const &message) { void TrayIcon::showErrorPopupNotification(QString const &title, QString const &message) {
ScopedTrayVisibility visible(*this);
this->showMessage(title, message, notificationErrorIcon_); this->showMessage(title, message, notificationErrorIcon_);
} }
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// Used only by user notifications received from the event loop /// Used only by user notifications received from the event loop
/// \param[in] title The title. /// \param[in] title The title.
/// \param[in] subtitle The subtitle. /// \param[in] subtitle The subtitle.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
void TrayIcon::showUserNotification(QString const &title, QString const &subtitle) { void TrayIcon::showUserNotification(QString const &title, QString const &subtitle) {
ScopedTrayVisibility visible(*this);
this->showMessage(title, subtitle, QSystemTrayIcon::NoIcon); this->showMessage(title, subtitle, QSystemTrayIcon::NoIcon);
} }

View File

@ -43,8 +43,6 @@ public: // data members
void setState(State state, QString const& stateString, QString const &statusIconPath); ///< Set the state of the icon 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 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). void showUserNotification(QString const& title, QString const &subtitle); ///< Display an OS pop up notification (without icon).
signals: signals:
void selectUser(QString const& userID, bool forceShowWindow); ///< Signal for selecting a user with a given userID void selectUser(QString const& userID, bool forceShowWindow); ///< Signal for selecting a user with a given userID

View File

@ -147,6 +147,19 @@ SettingsView {
onClicked: Backend.changeColorScheme(darkMode.checked ? "light" : "dark") 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 { SettingsItem {
id: allMail id: allMail
Layout.fillWidth: true Layout.fillWidth: true