1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-27 08:21:03 +02:00

Electron: Added tray icon support

This commit is contained in:
Laurent Cozic 2018-01-31 20:10:32 +00:00
parent dac1cd7668
commit cca43624e4
3 changed files with 67 additions and 5 deletions

View File

@ -1,5 +1,5 @@
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const { BrowserWindow } = require('electron'); const { BrowserWindow, Menu, Tray } = require('electron');
const { shim } = require('lib/shim'); const { shim } = require('lib/shim');
const url = require('url') const url = require('url')
const path = require('path') const path = require('path')
@ -12,6 +12,7 @@ class ElectronAppWrapper {
this.env_ = env; this.env_ = env;
this.win_ = null; this.win_ = null;
this.willQuitApp_ = false; this.willQuitApp_ = false;
this.tray_ = null;
} }
electronApp() { electronApp() {
@ -62,12 +63,28 @@ class ElectronAppWrapper {
if (this.env_ === 'dev') this.win_.webContents.openDevTools(); if (this.env_ === 'dev') this.win_.webContents.openDevTools();
this.win_.on('close', (event) => { this.win_.on('close', (event) => {
if (this.willQuitApp_ || process.platform !== 'darwin') { // If it's on macOS, the app is completely closed only if the user chooses to close the app (willQuitApp_ will be true)
// otherwise the window is simply hidden, and will be re-open once the app is "activated" (which happens when the
// user clicks on the icon in the task bar).
// On Windows and Linux, the app is closed when the window is closed *except* if the tray icon is used. In which
// case the app must be explicitely closed with Ctrl+Q or by right-clicking on the tray icon and selecting "Exit".
if (process.platform === 'darwin') {
if (this.willQuitApp_) {
this.win_ = null; this.win_ = null;
} else { } else {
event.preventDefault(); event.preventDefault();
this.win_.hide(); this.win_.hide();
} }
} else {
if (this.trayShown() && !this.willQuitApp_) {
event.preventDefault();
this.win_.hide();
} else {
this.win_ = null;
}
}
}) })
// Let us register listeners on the window, so we can update the state // Let us register listeners on the window, so we can update the state
@ -93,6 +110,27 @@ class ElectronAppWrapper {
this.electronApp_.quit(); this.electronApp_.quit();
} }
trayShown() {
return !!this.tray_;
}
// Note: this must be called only after the "ready" event of the app has been dispatched
createTray(contextMenu) {
this.tray_ = new Tray(__dirname + '/build/icons/16x16.png')
this.tray_.setToolTip(this.electronApp_.getName())
this.tray_.setContextMenu(contextMenu)
this.tray_.on('click', () => {
this.window().show();
});
}
destroyTray() {
if (!this.tray_) return;
this.tray_.destroy();
this.tray_ = null;
}
async start() { async start() {
// Since we are doing other async things before creating the window, we might miss // Since we are doing other async things before creating the window, we might miss
// the "ready" event. So we use the function below to make sure that the app is ready. // the "ready" event. So we use the function below to make sure that the app is ready.

View File

@ -140,6 +140,10 @@ class Application extends BaseApplication {
this.refreshMenu(); this.refreshMenu();
} }
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'showTrayIcon' || action.type == 'SETTING_UPDATE_ALL') {
this.updateTray();
}
if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) { if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) {
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync(); if (!await reg.syncTarget().syncStarted()) reg.scheduleSync();
} }
@ -349,6 +353,23 @@ class Application extends BaseApplication {
this.lastMenuScreen_ = screen; this.lastMenuScreen_ = screen;
} }
updateTray() {
const app = bridge().electronApp();
if (app.trayShown() === Setting.value('showTrayIcon')) return;
if (!Setting.value('showTrayIcon')) {
app.destroyTray();
} else {
const contextMenu = Menu.buildFromTemplate([
{ label: _('Open %s', app.electronApp().getName()), click: () => { app.window().show(); } },
{ type: 'separator' },
{ label: _('Exit'), click: () => { app.exit() } },
])
app.createTray(contextMenu);
}
}
async start(argv) { async start(argv) {
argv = await super.start(argv); argv = await super.start(argv);
@ -405,6 +426,8 @@ class Application extends BaseApplication {
setInterval(() => { runAutoUpdateCheck() }, 2 * 60 * 60 * 1000); setInterval(() => { runAutoUpdateCheck() }, 2 * 60 * 60 * 1000);
} }
this.updateTray();
setTimeout(() => { setTimeout(() => {
AlarmService.garbageCollect(); AlarmService.garbageCollect();
}, 1000 * 60 * 60); }, 1000 * 60 * 60);

View File

@ -72,6 +72,7 @@ class Setting extends BaseModel {
'body': _('Focus body'), 'body': _('Focus body'),
}; };
}}, }},
'showTrayIcon': { value: true, type: Setting.TYPE_BOOL, public: true, appTypes: ['desktop'], label: () => _('Show tray icon') },
'encryption.enabled': { value: false, type: Setting.TYPE_BOOL, public: false }, 'encryption.enabled': { value: false, type: Setting.TYPE_BOOL, public: false },
'encryption.activeMasterKeyId': { value: '', type: Setting.TYPE_STRING, public: false }, 'encryption.activeMasterKeyId': { value: '', type: Setting.TYPE_STRING, public: false },
'encryption.passwordCache': { value: {}, type: Setting.TYPE_OBJECT, public: false }, 'encryption.passwordCache': { value: {}, type: Setting.TYPE_OBJECT, public: false },