add new icons error & fix logic

This commit is contained in:
dranik
2026-06-05 16:13:06 +03:00
parent b21d77a911
commit 6f34f3509b
15 changed files with 122 additions and 22 deletions
@@ -425,7 +425,12 @@ void CoreSignalHandlers::initNotificationHandler()
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_coreController->m_notificationHandler);
connect(m_coreController, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
#endif
connect(m_coreController->m_connectionUiController, &ConnectionUiController::connectionErrorOccurred, trayHandler,
&SystemTrayNotificationHandler::setConnectionError);
connect(m_coreController->m_pageController, &PageController::errorMessageClosed, trayHandler,
&SystemTrayNotificationHandler::clearConnectionError);
#endif
}
void CoreSignalHandlers::initUpdateFoundHandler()
+2 -1
View File
@@ -69,7 +69,8 @@
<file>tray/off-light.svg</file>
<file>tray/on-black.svg</file>
<file>tray/on-white.svg</file>
<file>tray/error.svg</file>
<file>tray/error-black.svg</file>
<file>tray/error-white.svg</file>
<file>controls/monitor.svg</file>
</qresource>
</RCC>
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.6 KiB

+1 -1
View File
@@ -22,7 +22,7 @@ class MacOSStatusIcon final : public QObject {
public:
void setIcon(const QString& iconUrl);
void setIconFromData(const QByteArray& imageData);
void setIconFromData(const QByteArray& imageData, bool asTemplate = true);
void setMenu(QMenu* menu);
void rebuildNativeMenu();
void setToolTip(const QString& tooltip);
+10 -7
View File
@@ -39,7 +39,7 @@
@property(retain) NSMenu* nativeMenu;
@property(retain) NSMutableArray* menuActionTargets;
- (void)setIcon:(NSData*)imageData;
- (void)setIcon:(NSData*)imageData asTemplate:(BOOL)asTemplate;
- (void)setToolTip:(NSString*)tooltip;
- (void)rebuildMenuFromQMenu:(QMenu*)menu;
@end
@@ -71,11 +71,14 @@
/**
* Sets the image for the status icon.
*
* @param iconPath The data for the icon image.
* @param imageData The data for the icon image.
* @param asTemplate When true the icon is a template image recolored by the
* system for the current menu bar appearance. When false the icon is
* rendered in its original colors (used for the colored error icon).
*/
- (void)setIcon:(NSData*)imageData {
- (void)setIcon:(NSData*)imageData asTemplate:(BOOL)asTemplate {
NSImage* image = [[NSImage alloc] initWithData:imageData];
[image setTemplate:true];
[image setTemplate:asTemplate];
[self.statusItem.button setImage:image];
[image release];
@@ -169,10 +172,10 @@ void MacOSStatusIcon::setIcon(const QString& iconPath) {
QResource imageResource = QResource(iconPath);
Q_ASSERT(imageResource.isValid());
[m_statusBarIcon setIcon:imageResource.uncompressedData().toNSData()];
[m_statusBarIcon setIcon:imageResource.uncompressedData().toNSData() asTemplate:true];
}
void MacOSStatusIcon::setIconFromData(const QByteArray& imageData) {
void MacOSStatusIcon::setIconFromData(const QByteArray& imageData, bool asTemplate) {
logger.debug() << "Set icon from rendered data";
if (imageData.isEmpty()) {
@@ -180,7 +183,7 @@ void MacOSStatusIcon::setIconFromData(const QByteArray& imageData) {
}
NSData* data = [NSData dataWithBytes:imageData.constData() length:imageData.size()];
[m_statusBarIcon setIcon:data];
[m_statusBarIcon setIcon:data asTemplate:asTemplate];
}
void MacOSStatusIcon::setMenu(QMenu* menu) {
+7 -1
View File
@@ -23,7 +23,13 @@ void MacTrayIconBackend::show()
void MacTrayIconBackend::applyVisual(const TrayIconVisual &visual)
{
m_statusIcon.setIconFromData(TrayIconCommon::buildTemplatePng(visual.connectionState));
if (TrayIconCommon::isColoredState(visual.connectionState)) {
// Error icon carries a red badge: render it in color, not as a template.
m_statusIcon.setIconFromData(TrayIconCommon::buildColorPng(visual.connectionState, visual.darkTheme),
/*asTemplate*/ false);
} else {
m_statusIcon.setIconFromData(TrayIconCommon::buildTemplatePng(visual.connectionState), /*asTemplate*/ true);
}
}
void MacTrayIconBackend::showMessage(const QString &title, const QString &message, const TrayIconVisual &visual, int timerMsec)
@@ -248,3 +248,8 @@ void PageController::onShowErrorMessage(ErrorCode errorCode)
emit showErrorMessage(fullMessage);
}
void PageController::onErrorMessageClosed()
{
emit errorMessageClosed();
}
+2 -2
View File
@@ -133,7 +133,7 @@ public slots:
int getDrawerDepth() const;
int incrementDrawerDepth();
int decrementDrawerDepth();
void onErrorMessageClosed();
bool isEdgeToEdgeEnabled();
int getStatusBarHeight();
int getNavigationBarHeight();
@@ -162,7 +162,7 @@ signals:
void showErrorMessage(amnezia::ErrorCode);
void showErrorMessage(const QString &errorMessage);
void showNotificationMessage(const QString &message);
void errorMessageClosed();
void showBusyIndicator(bool visible);
void disableControls(bool disabled);
void disableTabBar(bool disabled);
+7
View File
@@ -203,6 +203,13 @@ Window {
PopupType {
id: popupErrorMessage
}
Connections {
target: popupErrorMessage
function onClosed() {
PageController.onErrorMessageClosed()
}
}
}
Item {
@@ -93,11 +93,27 @@ void SystemTrayNotificationHandler::refreshTheme()
TrayIconVisual SystemTrayNotificationHandler::currentTrayVisual() const
{
TrayIconVisual visual;
visual.connectionState = m_trayState;
visual.connectionState = m_errorLatched ? Vpn::ConnectionState::Error : m_trayState;
visual.darkTheme = m_isDarkTheme;
return visual;
}
void SystemTrayNotificationHandler::setConnectionError()
{
m_errorLatched = true;
updateTrayIcon();
}
void SystemTrayNotificationHandler::clearConnectionError()
{
if (!m_errorLatched) {
return;
}
m_errorLatched = false;
updateTrayIcon();
}
void SystemTrayNotificationHandler::updateTrayIcon()
{
if (!m_trayIcon) {
@@ -118,6 +134,20 @@ void SystemTrayNotificationHandler::onTrayActivated(QSystemTrayIcon::ActivationR
void SystemTrayNotificationHandler::setTrayState(Vpn::ConnectionState state)
{
if (state == Vpn::ConnectionState::Error || state == Vpn::ConnectionState::Unknown) {
// Latch the error icon. Both Error and Unknown surface the error message
// in the UI. The connection is torn down to Disconnected right after, so
// treat the real state as Disconnected and let the latch keep the error
// icon visible until the error is acknowledged.
m_errorLatched = true;
state = Vpn::ConnectionState::Disconnected;
} else if (state != Vpn::ConnectionState::Disconnected) {
// A new (re)connecting/connected lifecycle clears a previous error.
// Plain Disconnected leaves the latch untouched so the auto-Disconnected
// that immediately follows an error does not drop the error icon.
m_errorLatched = false;
}
m_trayState = state;
switch (state) {
@@ -25,6 +25,8 @@ public:
public slots:
void updateWebsiteUrl(const QString &newWebsiteUrl);
void setConnectionError();
void clearConnectionError();
protected:
void notify(Message type, const QString& title,
@@ -51,6 +53,7 @@ private:
Vpn::ConnectionState m_trayState = Vpn::ConnectionState::Unknown;
bool m_isDarkTheme = false;
bool m_errorLatched = false;
QString websiteUrl = "https://amnezia.org";
};
+17 -4
View File
@@ -11,7 +11,7 @@ QString resourcePathForState(Vpn::ConnectionState state, bool darkTheme)
{
switch (state) {
case Vpn::ConnectionState::Error:
return QString::fromLatin1(kIconError);
return QString::fromLatin1(darkTheme ? kIconErrorWhite : kIconErrorBlack);
case Vpn::ConnectionState::Connected:
return QString::fromLatin1(darkTheme ? kIconOnWhite : kIconOnBlack);
case Vpn::ConnectionState::Disconnected:
@@ -25,6 +25,11 @@ QString resourcePathForState(Vpn::ConnectionState state, bool darkTheme)
}
}
bool isColoredState(Vpn::ConnectionState state)
{
return state == Vpn::ConnectionState::Error;
}
QPixmap renderIcon(const QString &resourcePath, int size)
{
QSvgRenderer renderer(resourcePath);
@@ -53,10 +58,8 @@ QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme)
return icon;
}
QByteArray buildTemplatePng(Vpn::ConnectionState state)
QByteArray pixmapToPng(const QPixmap &pixmap)
{
const QPixmap pixmap = renderIcon(resourcePathForState(state, /*darkTheme*/ true), kDefaultTrayIconSize);
QByteArray bytes;
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
@@ -64,4 +67,14 @@ QByteArray buildTemplatePng(Vpn::ConnectionState state)
return bytes;
}
QByteArray buildTemplatePng(Vpn::ConnectionState state)
{
return pixmapToPng(renderIcon(resourcePathForState(state, /*darkTheme*/ true), kDefaultTrayIconSize));
}
QByteArray buildColorPng(Vpn::ConnectionState state, bool darkTheme)
{
return pixmapToPng(renderIcon(resourcePathForState(state, darkTheme), kDefaultTrayIconSize));
}
} // namespace TrayIconCommon
+5 -1
View File
@@ -16,15 +16,19 @@ namespace TrayIconCommon
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";
constexpr char kIconErrorBlack[] = ":/images/tray/error-black.svg";
constexpr char kIconErrorWhite[] = ":/images/tray/error-white.svg";
QString resourcePathForState(Vpn::ConnectionState state, bool darkTheme);
bool isColoredState(Vpn::ConnectionState state);
QPixmap renderIcon(const QString &resourcePath, int size);
QPixmap buildPixmap(int size, Vpn::ConnectionState state, bool darkTheme);
QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme);
QByteArray buildTemplatePng(Vpn::ConnectionState state);
QByteArray buildColorPng(Vpn::ConnectionState state, bool darkTheme);
} // namespace TrayIconCommon