diff --git a/client/images/images.qrc b/client/images/images.qrc index 51574688d..c00cebd59 100644 --- a/client/images/images.qrc +++ b/client/images/images.qrc @@ -65,10 +65,11 @@ controls/text-cursor.svg controls/trash.svg controls/x-circle.svg - tray/active.png - tray/default.png - tray/error.png - tray/icon.svg + tray/off-black.svg + tray/off-light.svg + tray/on-black.svg + tray/on-white.svg + tray/error.svg controls/monitor.svg diff --git a/client/images/tray/active.png b/client/images/tray/active.png deleted file mode 100644 index 7ceac4b4e..000000000 Binary files a/client/images/tray/active.png and /dev/null differ diff --git a/client/images/tray/default.png b/client/images/tray/default.png deleted file mode 100644 index 254ac14a7..000000000 Binary files a/client/images/tray/default.png and /dev/null differ diff --git a/client/images/tray/error.png b/client/images/tray/error.png deleted file mode 100644 index a24a32df9..000000000 Binary files a/client/images/tray/error.png and /dev/null differ diff --git a/client/images/tray/error.svg b/client/images/tray/error.svg new file mode 100644 index 000000000..ffa1e7dd4 --- /dev/null +++ b/client/images/tray/error.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/images/tray/icon.svg b/client/images/tray/icon.svg deleted file mode 100644 index c96c97d98..000000000 --- a/client/images/tray/icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/client/images/tray/off-black.svg b/client/images/tray/off-black.svg new file mode 100644 index 000000000..fe9cfbb33 --- /dev/null +++ b/client/images/tray/off-black.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/client/images/tray/off-light.svg b/client/images/tray/off-light.svg new file mode 100644 index 000000000..7aa8194ef --- /dev/null +++ b/client/images/tray/off-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/images/tray/on-black.svg b/client/images/tray/on-black.svg new file mode 100644 index 000000000..f84defbe9 --- /dev/null +++ b/client/images/tray/on-black.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/client/images/tray/on-white.svg b/client/images/tray/on-white.svg new file mode 100644 index 000000000..79d876650 --- /dev/null +++ b/client/images/tray/on-white.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/client/platforms/linux/linuxtrayiconbackend.cpp b/client/platforms/linux/linuxtrayiconbackend.cpp index 7bf326d02..ec9e3b104 100644 --- a/client/platforms/linux/linuxtrayiconbackend.cpp +++ b/client/platforms/linux/linuxtrayiconbackend.cpp @@ -32,9 +32,7 @@ void LinuxTrayIconBackend::show() void LinuxTrayIconBackend::applyVisual(const TrayIconVisual &visual) { - const qreal opacity = TrayIconCommon::opacityForState(visual.connectionState); - const QColor indicatorColor = TrayIconCommon::indicatorColorForState(visual.connectionState); - const QIcon icon = buildTrayIcon(opacity, visual.darkTheme, indicatorColor); + const QIcon icon = buildTrayIcon(visual.connectionState, visual.darkTheme); // Some tray implementations cache the first icon; clear before applying an update. if (m_trayIcon.isVisible()) { @@ -47,8 +45,7 @@ void LinuxTrayIconBackend::showMessage(const QString &title, const QString &mess int timerMsec) { m_trayIcon.showMessage(title, message, - buildTrayIcon(TrayIconCommon::kConnectedOpacity, visual.darkTheme, - TrayIconCommon::indicatorColorForState(Vpn::ConnectionState::Connected)), + buildTrayIcon(Vpn::ConnectionState::Connected, visual.darkTheme), timerMsec); } @@ -66,11 +63,11 @@ void LinuxTrayIconBackend::setActivatedHandler(std::function handler) override; private: - QIcon buildTrayIcon(qreal opacity, bool darkTheme, const QColor &indicatorColor) const; + QIcon buildTrayIcon(Vpn::ConnectionState state, bool darkTheme) const; QSystemTrayIcon m_trayIcon; }; diff --git a/client/platforms/macos/macosstatusicon.h b/client/platforms/macos/macosstatusicon.h index e67c3d98b..d77dda037 100644 --- a/client/platforms/macos/macosstatusicon.h +++ b/client/platforms/macos/macosstatusicon.h @@ -23,7 +23,6 @@ class MacOSStatusIcon final : public QObject { public: void setIcon(const QString& iconUrl); void setIconFromData(const QByteArray& imageData); - void setIndicatorColor(const QColor& indicatorColor); void setMenu(QMenu* menu); void rebuildNativeMenu(); void setToolTip(const QString& tooltip); diff --git a/client/platforms/macos/macosstatusicon.mm b/client/platforms/macos/macosstatusicon.mm index 0edb13b44..483db3c7a 100644 --- a/client/platforms/macos/macosstatusicon.mm +++ b/client/platforms/macos/macosstatusicon.mm @@ -29,21 +29,17 @@ @end /** - * Creates a NSStatusItem with that can hold an icon. Additionally a NSView is - * set as a subview to the button item of the status item. The view serves as - * an indicator that can be displayed in color eventhough the icon is set as a - * template. In that way we give the system control over it’s effective - * appearance. + * Creates a NSStatusItem that holds the tray icon. The icon is set as a + * template image, so the system controls its effective appearance for the + * current menu bar theme. The connection status is baked into the artwork, so + * no separate colored indicator is drawn. */ @interface MacOSStatusIconDelegate : NSObject @property(assign) NSStatusItem* statusItem; -@property(assign) NSView* statusIndicator; @property(retain) NSMenu* nativeMenu; @property(retain) NSMutableArray* menuActionTargets; - (void)setIcon:(NSData*)imageData; -- (void)setIndicator; -- (void)setIndicatorColor:(NSColor*)color; - (void)setToolTip:(NSString*)tooltip; - (void)rebuildMenuFromQMenu:(QMenu*)menu; @end @@ -62,8 +58,6 @@ self.statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; self.statusItem.visible = true; - // Add the indicator as a subview - [self setIndicator]; return self; } @@ -87,34 +81,6 @@ [image release]; } -/** - * Adds status indicator as a subview to the status item button. - */ -- (void)setIndicator { - float viewHeight = NSHeight([self.statusItem.button bounds]); - float dotSize = viewHeight * 0.35; - float dotOrigin = (viewHeight - dotSize) * 0.8; - - NSView* dot = [[NSView alloc] initWithFrame:NSMakeRect(dotOrigin, dotOrigin, dotSize, dotSize)]; - self.statusIndicator = dot; - self.statusIndicator.wantsLayer = true; - self.statusIndicator.layer.cornerRadius = dotSize * 0.5; - - [self.statusItem.button addSubview:self.statusIndicator]; - [dot release]; -} - -/** - * Sets the color if the indicator. - * - * @param color The indicator background color. - */ -- (void)setIndicatorColor:(NSColor*)color { - if (self.statusIndicator) { - self.statusIndicator.layer.backgroundColor = color.CGColor; - } -} - /** * Sets the status bar menu to the status item. * @@ -217,21 +183,6 @@ void MacOSStatusIcon::setIconFromData(const QByteArray& imageData) { [m_statusBarIcon setIcon:data]; } -void MacOSStatusIcon::setIndicatorColor(const QColor& indicatorColor) { - logger.debug() << "Set indicator color"; - - if (!indicatorColor.isValid()) { - [m_statusBarIcon setIndicatorColor:[NSColor clearColor]]; - return; - } - - NSColor* color = [NSColor colorWithCalibratedRed:indicatorColor.red() / 255.0f - green:indicatorColor.green() / 255.0f - blue:indicatorColor.blue() / 255.0f - alpha:indicatorColor.alpha() / 255.0f]; - [m_statusBarIcon setIndicatorColor:color]; -} - void MacOSStatusIcon::setMenu(QMenu* menu) { m_qtMenu = menu; rebuildNativeMenu(); diff --git a/client/platforms/macos/mactrayiconbackend.mm b/client/platforms/macos/mactrayiconbackend.mm index 17d5b23d4..6a8e8663a 100644 --- a/client/platforms/macos/mactrayiconbackend.mm +++ b/client/platforms/macos/mactrayiconbackend.mm @@ -23,9 +23,7 @@ void MacTrayIconBackend::show() void MacTrayIconBackend::applyVisual(const TrayIconVisual &visual) { - const qreal opacity = TrayIconCommon::opacityForState(visual.connectionState); - m_statusIcon.setIconFromData(TrayIconCommon::buildTemplatePng(opacity)); - m_statusIcon.setIndicatorColor(TrayIconCommon::indicatorColorForState(visual.connectionState)); + m_statusIcon.setIconFromData(TrayIconCommon::buildTemplatePng(visual.connectionState)); } void MacTrayIconBackend::showMessage(const QString &title, const QString &message, const TrayIconVisual &visual, int timerMsec) diff --git a/client/platforms/windows/wintrayicon.cpp b/client/platforms/windows/wintrayicon.cpp index 1e1121aec..0562d7bb9 100644 --- a/client/platforms/windows/wintrayicon.cpp +++ b/client/platforms/windows/wintrayicon.cpp @@ -6,22 +6,19 @@ namespace WinTrayIcon { -QIcon buildIcon(qreal opacity, bool darkTheme, const QColor &indicatorColor) +QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme) { - return TrayIconCommon::buildIcon(opacity, darkTheme, indicatorColor); + return TrayIconCommon::buildIcon(state, darkTheme); } void applyTo(QSystemTrayIcon &trayIcon, Vpn::ConnectionState state, bool darkTheme) { - const qreal opacity = TrayIconCommon::opacityForState(state); - const QColor indicatorColor = TrayIconCommon::indicatorColorForState(state); - trayIcon.setIcon(buildIcon(opacity, darkTheme, indicatorColor)); + trayIcon.setIcon(buildIcon(state, darkTheme)); } QIcon buildNotifyIcon(bool darkTheme) { - return buildIcon(TrayIconCommon::kConnectedOpacity, darkTheme, - TrayIconCommon::indicatorColorForState(Vpn::ConnectionState::Connected)); + return buildIcon(Vpn::ConnectionState::Connected, darkTheme); } void configure(QSystemTrayIcon &trayIcon, QMenu *menu, const QString &tooltip) diff --git a/client/platforms/windows/wintrayicon.h b/client/platforms/windows/wintrayicon.h index 5d734025c..39225149e 100644 --- a/client/platforms/windows/wintrayicon.h +++ b/client/platforms/windows/wintrayicon.h @@ -12,7 +12,7 @@ class QString; namespace WinTrayIcon { -QIcon buildIcon(qreal opacity, bool darkTheme, const QColor &indicatorColor); +QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme); void applyTo(QSystemTrayIcon &trayIcon, Vpn::ConnectionState state, bool darkTheme); QIcon buildNotifyIcon(bool darkTheme); diff --git a/client/ui/utils/trayIconCommon.cpp b/client/ui/utils/trayIconCommon.cpp index 91458a36c..2ab098855 100644 --- a/client/ui/utils/trayIconCommon.cpp +++ b/client/ui/utils/trayIconCommon.cpp @@ -7,93 +7,55 @@ namespace TrayIconCommon { -qreal opacityForState(Vpn::ConnectionState state) +QString resourcePathForState(Vpn::ConnectionState state, bool darkTheme) { switch (state) { + case Vpn::ConnectionState::Error: + return QString::fromLatin1(kIconError); case Vpn::ConnectionState::Connected: - case Vpn::ConnectionState::Error: return kConnectedOpacity; + return QString::fromLatin1(darkTheme ? kIconOnWhite : kIconOnBlack); case Vpn::ConnectionState::Disconnected: case Vpn::ConnectionState::Preparing: case Vpn::ConnectionState::Connecting: case Vpn::ConnectionState::Disconnecting: case Vpn::ConnectionState::Reconnecting: case Vpn::ConnectionState::Unknown: - default: return kDisconnectedOpacity; + default: + return QString::fromLatin1(darkTheme ? kIconOffLight : kIconOffBlack); } } -QColor indicatorColorForState(Vpn::ConnectionState state) -{ - switch (state) { - case Vpn::ConnectionState::Connected: return QColor(52, 199, 89); - case Vpn::ConnectionState::Error: return QColor(235, 87, 87); - default: return QColor(); - } -} - -QPixmap renderTemplate(const QString &resourcePath, qreal opacity, int size) +QPixmap renderIcon(const QString &resourcePath, int size) { QSvgRenderer renderer(resourcePath); QPixmap pixmap(size, size); pixmap.fill(Qt::transparent); if (!renderer.isValid()) { - qWarning() << "Failed to load tray icon template:" << resourcePath; + qWarning() << "Failed to load tray icon:" << resourcePath; return pixmap; } QPainter painter(&pixmap); - painter.setOpacity(opacity); renderer.render(&painter, QRectF(0, 0, size, size)); return pixmap; } -QPixmap colorizeTemplate(const QPixmap &mask, const QColor &foreground, int size) +QPixmap buildPixmap(int size, Vpn::ConnectionState state, bool darkTheme) { - QPixmap result(size, size); - result.fill(Qt::transparent); - - QPainter painter(&result); - painter.fillRect(result.rect(), foreground); - painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); - painter.drawPixmap(0, 0, mask); - return result; + return renderIcon(resourcePathForState(state, darkTheme), size); } -void drawStatusIndicator(QPainter &painter, const QColor &color, int size) -{ - const qreal dotSize = size * 0.35; - const qreal dotOrigin = (size - dotSize) * 0.8; - - painter.setPen(Qt::NoPen); - painter.setBrush(color); - painter.drawEllipse(QRectF(dotOrigin, dotOrigin, dotSize, dotSize)); -} - -QPixmap buildPixmap(int size, qreal opacity, bool darkTheme, const QColor &indicatorColor) -{ - const QPixmap mask = renderTemplate(QString::fromLatin1(kTrayTemplateIconPath), opacity, size); - const QColor foreground = darkTheme ? Qt::white : Qt::black; - QPixmap pixmap = colorizeTemplate(mask, foreground, size); - - if (indicatorColor.isValid()) { - QPainter painter(&pixmap); - drawStatusIndicator(painter, indicatorColor, size); - } - - return pixmap; -} - -QIcon buildIcon(qreal opacity, bool darkTheme, const QColor &indicatorColor) +QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme) { QIcon icon; - icon.addPixmap(buildPixmap(kDefaultTrayIconSize, opacity, darkTheme, indicatorColor)); + icon.addPixmap(buildPixmap(kDefaultTrayIconSize, state, darkTheme)); return icon; } -QByteArray buildTemplatePng(qreal opacity) +QByteArray buildTemplatePng(Vpn::ConnectionState state) { - const QPixmap pixmap = renderTemplate(QString::fromLatin1(kTrayTemplateIconPath), opacity, kDefaultTrayIconSize); + const QPixmap pixmap = renderIcon(resourcePathForState(state, /*darkTheme*/ true), kDefaultTrayIconSize); QByteArray bytes; QBuffer buffer(&bytes); diff --git a/client/ui/utils/trayIconCommon.h b/client/ui/utils/trayIconCommon.h index a47d24a7d..27657d8b8 100644 --- a/client/ui/utils/trayIconCommon.h +++ b/client/ui/utils/trayIconCommon.h @@ -2,31 +2,29 @@ #define TRAYICONCOMMON_H #include -#include #include -#include #include +#include #include "core/protocols/vpnProtocol.h" namespace TrayIconCommon { constexpr int kDefaultTrayIconSize = 128; - constexpr char kTrayTemplateIconPath[] = ":/images/tray/icon.svg"; - constexpr qreal kDisconnectedOpacity = 0.5; - constexpr qreal kConnectedOpacity = 1.0; + constexpr char kIconOffBlack[] = ":/images/tray/off-black.svg"; + constexpr char kIconOffLight[] = ":/images/tray/off-light.svg"; + constexpr char kIconOnBlack[] = ":/images/tray/on-black.svg"; + constexpr char kIconOnWhite[] = ":/images/tray/on-white.svg"; + constexpr char kIconError[] = ":/images/tray/error.svg"; - qreal opacityForState(Vpn::ConnectionState state); - QColor indicatorColorForState(Vpn::ConnectionState state); + QString resourcePathForState(Vpn::ConnectionState state, bool darkTheme); - QPixmap renderTemplate(const QString &resourcePath, qreal opacity, int size); - QPixmap colorizeTemplate(const QPixmap &mask, const QColor &foreground, int size); - void drawStatusIndicator(QPainter &painter, const QColor &color, int size); + QPixmap renderIcon(const QString &resourcePath, int size); - QPixmap buildPixmap(int size, qreal opacity, bool darkTheme, const QColor &indicatorColor); - QIcon buildIcon(qreal opacity, bool darkTheme, const QColor &indicatorColor); - QByteArray buildTemplatePng(qreal opacity); + QPixmap buildPixmap(int size, Vpn::ConnectionState state, bool darkTheme); + QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme); + QByteArray buildTemplatePng(Vpn::ConnectionState state); } // namespace TrayIconCommon