mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-23 02:00:20 +07:00
add new icons error & fix logic
This commit is contained in:
@@ -425,6 +425,11 @@ void CoreSignalHandlers::initNotificationHandler()
|
|||||||
|
|
||||||
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_coreController->m_notificationHandler);
|
auto* trayHandler = qobject_cast<SystemTrayNotificationHandler*>(m_coreController->m_notificationHandler);
|
||||||
connect(m_coreController, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
connect(m_coreController, &CoreController::websiteUrlChanged, trayHandler, &SystemTrayNotificationHandler::updateWebsiteUrl);
|
||||||
|
|
||||||
|
connect(m_coreController->m_connectionUiController, &ConnectionUiController::connectionErrorOccurred, trayHandler,
|
||||||
|
&SystemTrayNotificationHandler::setConnectionError);
|
||||||
|
connect(m_coreController->m_pageController, &PageController::errorMessageClosed, trayHandler,
|
||||||
|
&SystemTrayNotificationHandler::clearConnectionError);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@
|
|||||||
<file>tray/off-light.svg</file>
|
<file>tray/off-light.svg</file>
|
||||||
<file>tray/on-black.svg</file>
|
<file>tray/on-black.svg</file>
|
||||||
<file>tray/on-white.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>
|
<file>controls/monitor.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</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 |
@@ -22,7 +22,7 @@ class MacOSStatusIcon final : public QObject {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void setIcon(const QString& iconUrl);
|
void setIcon(const QString& iconUrl);
|
||||||
void setIconFromData(const QByteArray& imageData);
|
void setIconFromData(const QByteArray& imageData, bool asTemplate = true);
|
||||||
void setMenu(QMenu* menu);
|
void setMenu(QMenu* menu);
|
||||||
void rebuildNativeMenu();
|
void rebuildNativeMenu();
|
||||||
void setToolTip(const QString& tooltip);
|
void setToolTip(const QString& tooltip);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
@property(retain) NSMenu* nativeMenu;
|
@property(retain) NSMenu* nativeMenu;
|
||||||
@property(retain) NSMutableArray* menuActionTargets;
|
@property(retain) NSMutableArray* menuActionTargets;
|
||||||
|
|
||||||
- (void)setIcon:(NSData*)imageData;
|
- (void)setIcon:(NSData*)imageData asTemplate:(BOOL)asTemplate;
|
||||||
- (void)setToolTip:(NSString*)tooltip;
|
- (void)setToolTip:(NSString*)tooltip;
|
||||||
- (void)rebuildMenuFromQMenu:(QMenu*)menu;
|
- (void)rebuildMenuFromQMenu:(QMenu*)menu;
|
||||||
@end
|
@end
|
||||||
@@ -71,11 +71,14 @@
|
|||||||
/**
|
/**
|
||||||
* Sets the image for the status icon.
|
* 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];
|
NSImage* image = [[NSImage alloc] initWithData:imageData];
|
||||||
[image setTemplate:true];
|
[image setTemplate:asTemplate];
|
||||||
|
|
||||||
[self.statusItem.button setImage:image];
|
[self.statusItem.button setImage:image];
|
||||||
[image release];
|
[image release];
|
||||||
@@ -169,10 +172,10 @@ void MacOSStatusIcon::setIcon(const QString& iconPath) {
|
|||||||
QResource imageResource = QResource(iconPath);
|
QResource imageResource = QResource(iconPath);
|
||||||
Q_ASSERT(imageResource.isValid());
|
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";
|
logger.debug() << "Set icon from rendered data";
|
||||||
|
|
||||||
if (imageData.isEmpty()) {
|
if (imageData.isEmpty()) {
|
||||||
@@ -180,7 +183,7 @@ void MacOSStatusIcon::setIconFromData(const QByteArray& imageData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSData* data = [NSData dataWithBytes:imageData.constData() length:imageData.size()];
|
NSData* data = [NSData dataWithBytes:imageData.constData() length:imageData.size()];
|
||||||
[m_statusBarIcon setIcon:data];
|
[m_statusBarIcon setIcon:data asTemplate:asTemplate];
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacOSStatusIcon::setMenu(QMenu* menu) {
|
void MacOSStatusIcon::setMenu(QMenu* menu) {
|
||||||
|
|||||||
@@ -23,7 +23,13 @@ void MacTrayIconBackend::show()
|
|||||||
|
|
||||||
void MacTrayIconBackend::applyVisual(const TrayIconVisual &visual)
|
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)
|
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);
|
emit showErrorMessage(fullMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PageController::onErrorMessageClosed()
|
||||||
|
{
|
||||||
|
emit errorMessageClosed();
|
||||||
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public slots:
|
|||||||
int getDrawerDepth() const;
|
int getDrawerDepth() const;
|
||||||
int incrementDrawerDepth();
|
int incrementDrawerDepth();
|
||||||
int decrementDrawerDepth();
|
int decrementDrawerDepth();
|
||||||
|
void onErrorMessageClosed();
|
||||||
bool isEdgeToEdgeEnabled();
|
bool isEdgeToEdgeEnabled();
|
||||||
int getStatusBarHeight();
|
int getStatusBarHeight();
|
||||||
int getNavigationBarHeight();
|
int getNavigationBarHeight();
|
||||||
@@ -162,7 +162,7 @@ signals:
|
|||||||
void showErrorMessage(amnezia::ErrorCode);
|
void showErrorMessage(amnezia::ErrorCode);
|
||||||
void showErrorMessage(const QString &errorMessage);
|
void showErrorMessage(const QString &errorMessage);
|
||||||
void showNotificationMessage(const QString &message);
|
void showNotificationMessage(const QString &message);
|
||||||
|
void errorMessageClosed();
|
||||||
void showBusyIndicator(bool visible);
|
void showBusyIndicator(bool visible);
|
||||||
void disableControls(bool disabled);
|
void disableControls(bool disabled);
|
||||||
void disableTabBar(bool disabled);
|
void disableTabBar(bool disabled);
|
||||||
|
|||||||
@@ -203,6 +203,13 @@ Window {
|
|||||||
PopupType {
|
PopupType {
|
||||||
id: popupErrorMessage
|
id: popupErrorMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: popupErrorMessage
|
||||||
|
function onClosed() {
|
||||||
|
PageController.onErrorMessageClosed()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|||||||
@@ -93,11 +93,27 @@ void SystemTrayNotificationHandler::refreshTheme()
|
|||||||
TrayIconVisual SystemTrayNotificationHandler::currentTrayVisual() const
|
TrayIconVisual SystemTrayNotificationHandler::currentTrayVisual() const
|
||||||
{
|
{
|
||||||
TrayIconVisual visual;
|
TrayIconVisual visual;
|
||||||
visual.connectionState = m_trayState;
|
visual.connectionState = m_errorLatched ? Vpn::ConnectionState::Error : m_trayState;
|
||||||
visual.darkTheme = m_isDarkTheme;
|
visual.darkTheme = m_isDarkTheme;
|
||||||
return visual;
|
return visual;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SystemTrayNotificationHandler::setConnectionError()
|
||||||
|
{
|
||||||
|
m_errorLatched = true;
|
||||||
|
updateTrayIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemTrayNotificationHandler::clearConnectionError()
|
||||||
|
{
|
||||||
|
if (!m_errorLatched) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_errorLatched = false;
|
||||||
|
updateTrayIcon();
|
||||||
|
}
|
||||||
|
|
||||||
void SystemTrayNotificationHandler::updateTrayIcon()
|
void SystemTrayNotificationHandler::updateTrayIcon()
|
||||||
{
|
{
|
||||||
if (!m_trayIcon) {
|
if (!m_trayIcon) {
|
||||||
@@ -118,6 +134,20 @@ void SystemTrayNotificationHandler::onTrayActivated(QSystemTrayIcon::ActivationR
|
|||||||
|
|
||||||
void SystemTrayNotificationHandler::setTrayState(Vpn::ConnectionState state)
|
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;
|
m_trayState = state;
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ public:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateWebsiteUrl(const QString &newWebsiteUrl);
|
void updateWebsiteUrl(const QString &newWebsiteUrl);
|
||||||
|
void setConnectionError();
|
||||||
|
void clearConnectionError();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void notify(Message type, const QString& title,
|
void notify(Message type, const QString& title,
|
||||||
@@ -51,6 +53,7 @@ private:
|
|||||||
|
|
||||||
Vpn::ConnectionState m_trayState = Vpn::ConnectionState::Unknown;
|
Vpn::ConnectionState m_trayState = Vpn::ConnectionState::Unknown;
|
||||||
bool m_isDarkTheme = false;
|
bool m_isDarkTheme = false;
|
||||||
|
bool m_errorLatched = false;
|
||||||
|
|
||||||
QString websiteUrl = "https://amnezia.org";
|
QString websiteUrl = "https://amnezia.org";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ QString resourcePathForState(Vpn::ConnectionState state, bool darkTheme)
|
|||||||
{
|
{
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Vpn::ConnectionState::Error:
|
case Vpn::ConnectionState::Error:
|
||||||
return QString::fromLatin1(kIconError);
|
return QString::fromLatin1(darkTheme ? kIconErrorWhite : kIconErrorBlack);
|
||||||
case Vpn::ConnectionState::Connected:
|
case Vpn::ConnectionState::Connected:
|
||||||
return QString::fromLatin1(darkTheme ? kIconOnWhite : kIconOnBlack);
|
return QString::fromLatin1(darkTheme ? kIconOnWhite : kIconOnBlack);
|
||||||
case Vpn::ConnectionState::Disconnected:
|
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)
|
QPixmap renderIcon(const QString &resourcePath, int size)
|
||||||
{
|
{
|
||||||
QSvgRenderer renderer(resourcePath);
|
QSvgRenderer renderer(resourcePath);
|
||||||
@@ -53,10 +58,8 @@ QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme)
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray buildTemplatePng(Vpn::ConnectionState state)
|
QByteArray pixmapToPng(const QPixmap &pixmap)
|
||||||
{
|
{
|
||||||
const QPixmap pixmap = renderIcon(resourcePathForState(state, /*darkTheme*/ true), kDefaultTrayIconSize);
|
|
||||||
|
|
||||||
QByteArray bytes;
|
QByteArray bytes;
|
||||||
QBuffer buffer(&bytes);
|
QBuffer buffer(&bytes);
|
||||||
buffer.open(QIODevice::WriteOnly);
|
buffer.open(QIODevice::WriteOnly);
|
||||||
@@ -64,4 +67,14 @@ QByteArray buildTemplatePng(Vpn::ConnectionState state)
|
|||||||
return bytes;
|
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
|
} // namespace TrayIconCommon
|
||||||
|
|||||||
@@ -16,15 +16,19 @@ namespace TrayIconCommon
|
|||||||
constexpr char kIconOffLight[] = ":/images/tray/off-light.svg";
|
constexpr char kIconOffLight[] = ":/images/tray/off-light.svg";
|
||||||
constexpr char kIconOnBlack[] = ":/images/tray/on-black.svg";
|
constexpr char kIconOnBlack[] = ":/images/tray/on-black.svg";
|
||||||
constexpr char kIconOnWhite[] = ":/images/tray/on-white.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);
|
QString resourcePathForState(Vpn::ConnectionState state, bool darkTheme);
|
||||||
|
|
||||||
|
bool isColoredState(Vpn::ConnectionState state);
|
||||||
|
|
||||||
QPixmap renderIcon(const QString &resourcePath, int size);
|
QPixmap renderIcon(const QString &resourcePath, int size);
|
||||||
|
|
||||||
QPixmap buildPixmap(int size, Vpn::ConnectionState state, bool darkTheme);
|
QPixmap buildPixmap(int size, Vpn::ConnectionState state, bool darkTheme);
|
||||||
QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme);
|
QIcon buildIcon(Vpn::ConnectionState state, bool darkTheme);
|
||||||
QByteArray buildTemplatePng(Vpn::ConnectionState state);
|
QByteArray buildTemplatePng(Vpn::ConnectionState state);
|
||||||
|
QByteArray buildColorPng(Vpn::ConnectionState state, bool darkTheme);
|
||||||
|
|
||||||
} // namespace TrayIconCommon
|
} // namespace TrayIconCommon
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user