From cd50fe3123990840e65963a468a8fc168ac970a2 Mon Sep 17 00:00:00 2001 From: Martino Pilia Date: Mon, 27 Nov 2017 23:48:09 +0100 Subject: [PATCH] Add a tray icon to the application (#1676) This commit adds a tray icon to the application, shown in the system tray bar, that can be used to minimise the application window. This is a common feature on most desktop messaging apps (e.g. Telegram Desktop or Slack) and allows to save space in the system task bar. The tray icon provides a context menu that contains a button to show/hide the application window, and a button to quit the application. When the tray icon is clicked, the visibility of the window is toggled. When the close (x) button of the window is pressed, the application is not terminated but minimised to the tray icon instead (it can be terminated by using the "Quit" entry in the File menu or in the context menu of the tray icon). The tray icon is disabled by default, and two command line arguments are available to enable it: --use-tray-icon: enables the tray icon --start-in-tray: enables the tray icon and the application starts minimised in the tray bar Resolves: #1480 --- _locales/en/messages.json | 12 +++++++ app/tray_icon.js | 70 +++++++++++++++++++++++++++++++++++++++ main.js | 31 ++++++++++++++++- 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 app/tray_icon.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 46b7bd4a4f94..4f4564b4c3e2 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 000000000000..535e9504df7a --- /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 b6b51eb776f8..0c7d2d345fc6 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,