diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 46b7bd4a4..4f4564b4c 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -412,6 +412,18 @@ "message": "Show", "description": "Command under Window menu, to show the window" }, + "hide": { + "message": "Hide", + "description": "Command in the tray icon menu, to hide the window" + }, + "quit": { + "message": "Quit", + "description": "Command in the tray icon menu, to quit the application" + }, + "trayTooltip": { + "message": "Signal Desktop", + "description": "Tooltip for the tray icon" + }, "searchForPeopleOrGroups": { "message": "Search...", "description": "Placeholder text in the search input" diff --git a/app/tray_icon.js b/app/tray_icon.js new file mode 100644 index 000000000..535e9504d --- /dev/null +++ b/app/tray_icon.js @@ -0,0 +1,70 @@ +const electron = require('electron') +const path = require('path'); + +const app = electron.app; +const Menu = electron.Menu; +const Tray = electron.Tray; + +let trayContextMenu = null; +let tray = null; + +function createTrayIcon(getMainWindow, messages) { + + // A smaller icon is needed on macOS + tray = new Tray( + process.platform == "darwin" ? + path.join(__dirname, '..', 'images', 'icon_16.png') : + path.join(__dirname, '..', 'images', 'icon_256.png')); + + tray.toggleWindowVisibility = function () { + var mainWindow = getMainWindow(); + if (mainWindow) { + if (mainWindow.isVisible()) { + mainWindow.hide(); + } else { + mainWindow.show(); + + // On some versions of GNOME the window may not be on top when restored. + // This trick should fix it. + // Thanks to: https://github.com/Enrico204/Whatsapp-Desktop/commit/6b0dc86b64e481b455f8fce9b4d797e86d000dc1 + mainWindow.setAlwaysOnTop(true); + mainWindow.focus(); + mainWindow.setAlwaysOnTop(false); + } + } + tray.updateContextMenu(); + } + + tray.updateContextMenu = function () { + + var mainWindow = getMainWindow(); + + // NOTE: we want to have the show/hide entry available in the tray icon + // context menu, since the 'click' event may not work on all platforms. + // For details please refer to: + // https://github.com/electron/electron/blob/master/docs/api/tray.md. + trayContextMenu = Menu.buildFromTemplate([ + { + id: 'toggleWindowVisibility', + label: messages[mainWindow.isVisible() ? 'hide' : 'show'].message, + click: tray.toggleWindowVisibility + }, + { + id: 'quit', + label: messages.quit.message, + click: app.quit.bind(app) + } + ]); + + tray.setContextMenu(trayContextMenu); + } + + tray.on('click', tray.toggleWindowVisibility); + + tray.setToolTip(messages.trayTooltip.message); + tray.updateContextMenu(); + + return tray; +} + +module.exports = createTrayIcon; diff --git a/main.js b/main.js index b6b51eb77..0c7d2d345 100644 --- a/main.js +++ b/main.js @@ -29,6 +29,11 @@ function getMainWindow() { return mainWindow; } +// Tray icon and related objects +let tray = null; +const startInTray = process.argv.find(arg => arg === '--start-in-tray'); +const usingTrayIcon = startInTray || process.argv.find(arg => arg === '--use-tray-icon'); + const config = require("./app/config"); // Very important to put before the single instance check, since it is based on the @@ -107,6 +112,7 @@ function captureClicks(window) { function createWindow () { const windowOptions = Object.assign({ + show: !startInTray, // allow to start minimised in tray width: 800, height: 610, minWidth: 700, @@ -187,9 +193,22 @@ function createWindow () { // Emitted when the window is about to be closed. mainWindow.on('close', function (e) { - if (process.platform === 'darwin' && !windowState.shouldQuit() && config.environment !== 'test') { + + // If the application is terminating, just do the default + if (windowState.shouldQuit() || config.environment === 'test') { + return; + } + + // On Mac, or on other platforms when the tray icon is in use, the window + // should be only hidden, not closed, when the user clicks the close button + if (usingTrayIcon || process.platform === 'darwin') { e.preventDefault(); mainWindow.hide(); + + // toggle the visibility of the show/hide tray icon menu entries + if (tray) { + tray.updateContextMenu(); + } } }); @@ -211,6 +230,11 @@ function createWindow () { } else { mainWindow.show(); } + + // toggle the visibility of the show/hide tray icon menu entries + if (tray) { + tray.updateContextMenu(); + } }); } @@ -296,6 +320,11 @@ app.on('ready', function() { createWindow(); + if (usingTrayIcon) { + const createTrayIcon = require("./app/tray_icon"); + tray = createTrayIcon(getMainWindow, locale.messages); + } + const options = { showDebugLog, showWindow,