feat: Add proper tray icons for Windows dark mode (#2476)
|
@ -170,8 +170,6 @@ class AppearanceModeSwitch extends React.Component<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { INBOX_ZERO_ICON, INBOX_FULL_ICON, INBOX_FULL_NEW_ICON, INBOX_FULL_UNREAD_ICON } = SystemTrayIconStore;
|
|
||||||
|
|
||||||
class TrayIconStylePicker extends React.Component<{ config: ConfigLike }> {
|
class TrayIconStylePicker extends React.Component<{ config: ConfigLike }> {
|
||||||
kp = 'core.workspace.trayIconStyle';
|
kp = 'core.workspace.trayIconStyle';
|
||||||
|
|
||||||
|
@ -180,11 +178,12 @@ class TrayIconStylePicker extends React.Component<{ config: ConfigLike }> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let systemTrayIconScore = new SystemTrayIconStore();
|
||||||
const val = this.props.config.get(this.kp) || 'blue';
|
const val = this.props.config.get(this.kp) || 'blue';
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
['blue', localized('Blue icon for new and unread messages'), localized('(The same blue tray icon is used whether you have new or old unread messages.)'), INBOX_FULL_UNREAD_ICON],
|
['blue', localized('Blue icon for new and unread messages'), localized('(The same blue tray icon is used whether you have new or old unread messages.)'), systemTrayIconScore.inboxFullUnreadIcon()],
|
||||||
['red', localized('Red icon for new and blue icon for unread messages'), localized('(A red tray icon is displayed for new messages and a blue icon for older unread messages.)'), INBOX_FULL_NEW_ICON],
|
['red', localized('Red icon for new and blue icon for unread messages'), localized('(A red tray icon is displayed for new messages and a blue icon for older unread messages.)'), systemTrayIconScore.inboxFullNewIcon()],
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
After Width: | Height: | Size: 937 B |
After Width: | Height: | Size: 740 B |
After Width: | Height: | Size: 879 B |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 6 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 6 KiB |
After Width: | Height: | Size: 6.3 KiB |
|
@ -5,22 +5,7 @@ import { BadgeStore } from 'mailspring-exports';
|
||||||
// Must be absolute real system path
|
// Must be absolute real system path
|
||||||
// https://github.com/atom/electron/issues/1299
|
// https://github.com/atom/electron/issues/1299
|
||||||
const { platform } = process;
|
const { platform } = process;
|
||||||
const INBOX_ZERO_ICON = path.join(__dirname, '..', 'assets', platform, 'MenuItem-Inbox-Zero.png');
|
const { nativeTheme } = require("@electron/remote");
|
||||||
const INBOX_FULL_ICON = path.join(__dirname, '..', 'assets', platform, 'MenuItem-Inbox-Full.png');
|
|
||||||
const INBOX_FULL_NEW_ICON = path.join(
|
|
||||||
__dirname,
|
|
||||||
'..',
|
|
||||||
'assets',
|
|
||||||
platform,
|
|
||||||
'MenuItem-Inbox-Full-NewItems.png'
|
|
||||||
);
|
|
||||||
const INBOX_FULL_UNREAD_ICON = path.join(
|
|
||||||
__dirname,
|
|
||||||
'..',
|
|
||||||
'assets',
|
|
||||||
platform,
|
|
||||||
'MenuItem-Inbox-Full-UnreadItems.png'
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Current / Intended Behavior:
|
Current / Intended Behavior:
|
||||||
|
@ -38,13 +23,6 @@ Current / Intended Behavior:
|
||||||
it will switch to blue.)
|
it will switch to blue.)
|
||||||
*/
|
*/
|
||||||
class SystemTrayIconStore {
|
class SystemTrayIconStore {
|
||||||
static INBOX_ZERO_ICON = INBOX_ZERO_ICON;
|
|
||||||
|
|
||||||
static INBOX_FULL_ICON = INBOX_FULL_ICON;
|
|
||||||
|
|
||||||
static INBOX_FULL_NEW_ICON = INBOX_FULL_NEW_ICON;
|
|
||||||
|
|
||||||
static INBOX_FULL_UNREAD_ICON = INBOX_FULL_UNREAD_ICON;
|
|
||||||
|
|
||||||
_windowBackgrounded = false;
|
_windowBackgrounded = false;
|
||||||
_unsubscribers: (() => void)[];
|
_unsubscribers: (() => void)[];
|
||||||
|
@ -66,6 +44,11 @@ class SystemTrayIconStore {
|
||||||
window.removeEventListener('browser-window-hide', this._onWindowBackgrounded);
|
window.removeEventListener('browser-window-hide', this._onWindowBackgrounded);
|
||||||
window.removeEventListener('browser-window-blur', this._onWindowBackgrounded);
|
window.removeEventListener('browser-window-blur', this._onWindowBackgrounded);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If the theme changes from bright to dark mode or vice versa, we need to update the tray icon
|
||||||
|
nativeTheme.on('updated', () => {
|
||||||
|
this._updateIcon();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
|
@ -84,6 +67,44 @@ class SystemTrayIconStore {
|
||||||
this._updateIcon();
|
this._updateIcon();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This implementation is windows only.
|
||||||
|
// On Mac the icon color is automatically inverted
|
||||||
|
// Linux ships with the icons used for a dark tray only
|
||||||
|
_dark = () => {
|
||||||
|
if (nativeTheme.shouldUseDarkColors && process.platform === 'win32') {
|
||||||
|
return "-dark";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
inboxZeroIcon = () => {
|
||||||
|
return path.join(__dirname, '..', 'assets', platform, `MenuItem-Inbox-Zero${this._dark()}.png`);
|
||||||
|
}
|
||||||
|
|
||||||
|
inboxFullIcon = () => {
|
||||||
|
return path.join(__dirname, '..', 'assets', platform, `MenuItem-Inbox-Full${this._dark()}.png`);
|
||||||
|
}
|
||||||
|
|
||||||
|
inboxFullNewIcon = () => {
|
||||||
|
return path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'assets',
|
||||||
|
platform,
|
||||||
|
`MenuItem-Inbox-Full-NewItems${this._dark()}.png`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inboxFullUnreadIcon = () => {
|
||||||
|
return path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'assets',
|
||||||
|
platform,
|
||||||
|
`MenuItem-Inbox-Full-UnreadItems${this._dark()}.png`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_updateIcon = () => {
|
_updateIcon = () => {
|
||||||
const unread = BadgeStore.unread();
|
const unread = BadgeStore.unread();
|
||||||
const unreadString = (+unread).toLocaleString();
|
const unreadString = (+unread).toLocaleString();
|
||||||
|
@ -91,17 +112,17 @@ class SystemTrayIconStore {
|
||||||
|
|
||||||
const newMessagesIconStyle = AppEnv.config.get('core.workspace.trayIconStyle') || 'blue';
|
const newMessagesIconStyle = AppEnv.config.get('core.workspace.trayIconStyle') || 'blue';
|
||||||
|
|
||||||
let icon = { path: INBOX_FULL_ICON, isTemplateImg: true };
|
let icon = { path: this.inboxFullIcon(), isTemplateImg: true };
|
||||||
if (isInboxZero) {
|
if (isInboxZero) {
|
||||||
icon = { path: INBOX_ZERO_ICON, isTemplateImg: true };
|
icon = { path: this.inboxZeroIcon(), isTemplateImg: true };
|
||||||
} else if (unread !== 0) {
|
} else if (unread !== 0) {
|
||||||
if (newMessagesIconStyle === 'blue') {
|
if (newMessagesIconStyle === 'blue') {
|
||||||
icon = { path: INBOX_FULL_UNREAD_ICON, isTemplateImg: false };
|
icon = { path: this.inboxFullUnreadIcon(), isTemplateImg: false };
|
||||||
} else {
|
} else {
|
||||||
if (this._windowBackgrounded) {
|
if (this._windowBackgrounded) {
|
||||||
icon = { path: INBOX_FULL_NEW_ICON, isTemplateImg: false };
|
icon = { path: this.inboxFullNewIcon(), isTemplateImg: false };
|
||||||
} else {
|
} else {
|
||||||
icon = { path: INBOX_FULL_UNREAD_ICON, isTemplateImg: false };
|
icon = { path: this.inboxFullUnreadIcon(), isTemplateImg: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@ import { ipcRenderer } from 'electron';
|
||||||
import { BadgeStore } from 'mailspring-exports';
|
import { BadgeStore } from 'mailspring-exports';
|
||||||
import SystemTrayIconStore from '../lib/system-tray-icon-store';
|
import SystemTrayIconStore from '../lib/system-tray-icon-store';
|
||||||
|
|
||||||
const { INBOX_ZERO_ICON, INBOX_FULL_ICON, INBOX_FULL_NEW_ICON, INBOX_FULL_UNREAD_ICON } = SystemTrayIconStore;
|
|
||||||
|
|
||||||
describe('SystemTrayIconStore', function systemTrayIconStore() {
|
describe('SystemTrayIconStore', function systemTrayIconStore() {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(ipcRenderer, 'send');
|
spyOn(ipcRenderer, 'send');
|
||||||
|
@ -20,7 +18,7 @@ describe('SystemTrayIconStore', function systemTrayIconStore() {
|
||||||
spyOn(BadgeStore, 'unread').andReturn(0);
|
spyOn(BadgeStore, 'unread').andReturn(0);
|
||||||
spyOn(BadgeStore, 'total').andReturn(0);
|
spyOn(BadgeStore, 'total').andReturn(0);
|
||||||
this.iconStore._updateIcon();
|
this.iconStore._updateIcon();
|
||||||
expect(getCallData()).toEqual({ path: INBOX_ZERO_ICON, isTemplateImg: true });
|
expect(getCallData()).toEqual({ path: this.iconStore.inboxZeroIcon(), isTemplateImg: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows inbox zero icon when isInboxZero and window is blurred', () => {
|
it('shows inbox zero icon when isInboxZero and window is blurred', () => {
|
||||||
|
@ -28,7 +26,7 @@ describe('SystemTrayIconStore', function systemTrayIconStore() {
|
||||||
spyOn(BadgeStore, 'unread').andReturn(0);
|
spyOn(BadgeStore, 'unread').andReturn(0);
|
||||||
spyOn(BadgeStore, 'total').andReturn(0);
|
spyOn(BadgeStore, 'total').andReturn(0);
|
||||||
this.iconStore._updateIcon();
|
this.iconStore._updateIcon();
|
||||||
expect(getCallData()).toEqual({ path: INBOX_ZERO_ICON, isTemplateImg: true });
|
expect(getCallData()).toEqual({ path: this.iconStore.inboxZeroIcon(), isTemplateImg: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows inbox full icon when not isInboxZero and window is focused', () => {
|
it('shows inbox full icon when not isInboxZero and window is focused', () => {
|
||||||
|
@ -36,7 +34,7 @@ describe('SystemTrayIconStore', function systemTrayIconStore() {
|
||||||
spyOn(BadgeStore, 'unread').andReturn(102);
|
spyOn(BadgeStore, 'unread').andReturn(102);
|
||||||
spyOn(BadgeStore, 'total').andReturn(123123);
|
spyOn(BadgeStore, 'total').andReturn(123123);
|
||||||
this.iconStore._updateIcon();
|
this.iconStore._updateIcon();
|
||||||
expect(getCallData()).toEqual({ path: INBOX_FULL_ICON, isTemplateImg: true });
|
expect(getCallData()).toEqual({ path: this.iconStore.inboxFullIcon(), isTemplateImg: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows inbox full /alt/ icon when not isInboxZero and window is blurred', () => {
|
it('shows inbox full /alt/ icon when not isInboxZero and window is blurred', () => {
|
||||||
|
@ -44,7 +42,7 @@ describe('SystemTrayIconStore', function systemTrayIconStore() {
|
||||||
spyOn(BadgeStore, 'unread').andReturn(102);
|
spyOn(BadgeStore, 'unread').andReturn(102);
|
||||||
spyOn(BadgeStore, 'total').andReturn(123123);
|
spyOn(BadgeStore, 'total').andReturn(123123);
|
||||||
this.iconStore._updateIcon();
|
this.iconStore._updateIcon();
|
||||||
expect(getCallData()).toEqual({ path: INBOX_FULL_UNREAD_ICON, isTemplateImg: false });
|
expect(getCallData()).toEqual({ path: this.iconStore.inboxFullUnreadIcon(), isTemplateImg: false });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -53,7 +51,7 @@ describe('SystemTrayIconStore', function systemTrayIconStore() {
|
||||||
spyOn(BadgeStore, 'total').andReturn(1);
|
spyOn(BadgeStore, 'total').andReturn(1);
|
||||||
this.iconStore._onWindowFocus();
|
this.iconStore._onWindowFocus();
|
||||||
const { path } = getCallData();
|
const { path } = getCallData();
|
||||||
expect(path).toBe(INBOX_FULL_ICON);
|
expect(path).toBe(this.iconStore.inboxFullIcon());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows inbox full /alt/ icon ONLY when window is currently blurred and total count changes', () => {
|
it('shows inbox full /alt/ icon ONLY when window is currently blurred and total count changes', () => {
|
||||||
|
@ -66,7 +64,7 @@ describe('SystemTrayIconStore', function systemTrayIconStore() {
|
||||||
this.iconStore._updateIcon();
|
this.iconStore._updateIcon();
|
||||||
|
|
||||||
const { path } = getCallData();
|
const { path } = getCallData();
|
||||||
expect(path).toBe(INBOX_FULL_UNREAD_ICON);
|
expect(path).toBe(this.iconStore.inboxFullUnreadIcon());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not show inbox full /alt/ icon when window is currently focused and total count changes', () => {
|
it('does not show inbox full /alt/ icon when window is currently focused and total count changes', () => {
|
||||||
|
@ -77,7 +75,7 @@ describe('SystemTrayIconStore', function systemTrayIconStore() {
|
||||||
this.iconStore._updateIcon();
|
this.iconStore._updateIcon();
|
||||||
|
|
||||||
const { path } = getCallData();
|
const { path } = getCallData();
|
||||||
expect(path).toBe(INBOX_FULL_ICON);
|
expect(path).toBe(this.iconStore.inboxFullIcon());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|