{"componentChunkName":"component---src-templates-blog-jsx","path":"/blog/how-to-position-electron-tray","result":{"data":{"mdx":{"frontmatter":{"title":"How to position the Electron tray window on all platforms","date":"2020-11-07","subtitle":"Creating an Electron tray app and correctly positioning it on Windows, Linux, and Mac","coverCredit":"Aziz Acharki","coverWebsite":"Unsplash","cover":{"publicURL":"/static/0164f957b15996eb7e31a4c5052be2b7/cover.jpg","childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwQA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABuOC0PFj/xAAZEAEBAQEBAQAAAAAAAAAAAAACAQASAxT/2gAIAQEAAQUC7eTc303BOUK5et6//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGRAAAQUAAAAAAAAAAAAAAAAAEAAREjFB/9oACAEBAAY/AqGJpH//xAAaEAACAwEBAAAAAAAAAAAAAAABEQAhMRBh/9oACAEBAAE/IUipD4nqD4AsKQgEG46SC8n/2gAMAwEAAgADAAAAEBMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBABAQACAwEBAAAAAAAAAAAAAREAITFxwVFh/9oACAEBAAE/EAYuht0+4fftS7edYnpKOaJiiZak5/MVOht7xIAlAhz/2Q==","aspectRatio":1.5,"src":"/static/0164f957b15996eb7e31a4c5052be2b7/cf77e/cover.jpg","srcSet":"/static/0164f957b15996eb7e31a4c5052be2b7/8196e/cover.jpg 258w,\n/static/0164f957b15996eb7e31a4c5052be2b7/26313/cover.jpg 516w,\n/static/0164f957b15996eb7e31a4c5052be2b7/cf77e/cover.jpg 1032w,\n/static/0164f957b15996eb7e31a4c5052be2b7/7022b/cover.jpg 1548w,\n/static/0164f957b15996eb7e31a4c5052be2b7/14c55/cover.jpg 2064w,\n/static/0164f957b15996eb7e31a4c5052be2b7/21d0b/cover.jpg 5184w","sizes":"(max-width: 1032px) 100vw, 1032px"}}}},"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"How to position the Electron tray window on all platforms\",\n  \"author\": \"Patrick Passarella\",\n  \"date\": \"2020-11-07\",\n  \"subtitle\": \"Creating an Electron tray app and correctly positioning it on Windows, Linux, and Mac\",\n  \"cover\": \"./cover.jpg\",\n  \"coverCredit\": \"Aziz Acharki\",\n  \"coverWebsite\": \"Unsplash\",\n  \"published\": true\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"I've had a hard time trying to have the tray window appear in the correct spot for all platforms, since in Linux the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tray.getBounds()\"), \" doesn't work.\\nTo solve that, we can use the mouse position for Linux, and the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tray.getBounds()\"), \" for windows and mac.\"), mdx(\"p\", null, \"First, let's create an Electron app (I will not enter in details here, just a quick step-by-step list). You can also jump directly to the important part, \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"#creating-the-tray-icon\"\n  }), \"here\"), \".\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"$ yarn init\"), \" to create an empty project.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"main\"), \" property in the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"package.json\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"main.js\"), \".\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Create a \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"main.js\"), \" file in the root of your project.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"$ yarn add electron --dev\"), \".\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add a start script to your package.json, \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"\\\"start\\\": \\\"electron .\\\"\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Create an \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"index.html\"), \" in your project root.\")), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \" file, I will be using the Electron default code, found on their \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.electronjs.org/docs/tutorial/first-app\"\n  }), \"documentation\"), \". With just some adjustments.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const { app, BrowserWindow, Tray } = require('electron');\\nconst path = require('path');\\n\\n// Keep a global reference of the window object, if you don't, the window will\\n// be closed automatically when the JavaScript object is garbage collected.\\nlet tray = null;\\nlet window = null;\\n\\nfunction createWindow() {\\n  // Create the browser window.\\n  window = new BrowserWindow({\\n    width: 800,\\n    height: 600,\\n    webPreferences: {\\n      nodeIntegration: true,\\n    },\\n  });\\n\\n  // and load the index.html of the app.\\n  window.loadFile('index.html');\\n\\n  // Open the DevTools.\\n  window.webContents.openDevTools({ mode: 'detach' });\\n}\\n\\napp.on('ready', () => {\\n  createWindow();\\n});\\n\\napp.on('window-all-closed', () => {\\n  if (process.platform !== 'darwin') {\\n    app.quit();\\n  }\\n});\\n\\napp.on('activate', () => {\\n  // On macOS it's common to re-create a window in the app when the\\n  // dock icon is clicked and there are no other windows open.\\n  if (BrowserWindow.getAllWindows().length === 0) {\\n    createWindow();\\n  }\\n});\\n\")), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"index.html\"), \", I will also be using the same code as the Electron documentation (just a \\\"Hello World\\\").\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-html\"\n  }), \"<!DOCTYPE html>\\n<html>\\n  <head>\\n    <meta charset=\\\"UTF-8\\\" />\\n    <title>Hello World!</title>\\n    <meta\\n      http-equiv=\\\"Content-Security-Policy\\\"\\n      content=\\\"script-src 'self' 'unsafe-inline';\\\"\\n    />\\n  </head>\\n  <body>\\n    <h1>Hello World!</h1>\\n  </body>\\n</html>\\n\")), mdx(\"h2\", null, \"Creating the tray icon\"), mdx(\"p\", null, \"Now, let's create a tray icon. In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \" file, create a function \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"createTray\"), \". Remember to import \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Tray\"), \" from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"'electron'\"), \".\\nThis function will create the tray with the icon image provided in the assets folder, so you need to create that folder and put the image inside.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const createTray = () => {\\n  tray = new Tray(path.join(__dirname, 'assets', 'icon.png'));\\n};\\n\")), mdx(\"p\", null, \"Call this function only after the app is ready.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"app.on('ready', () => {\\n  createWindow();\\n  createTray();\\n});\\n\")), mdx(\"p\", null, \"Run the app again with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$ yarn start\"), \", you will see that the tray will appear with your icon.\"), mdx(\"h2\", null, \"Positioning the tray window\"), mdx(\"p\", null, \"Now for the main part, clicking the tray icon to open the window in the correct position.\", mdx(\"br\", {\n    parentName: \"p\"\n  }), \"\\n\", \"Change the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"show\"), \" option to false in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"BrowserWindow\"), \", to not show the window after created.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const window = new BrowserWindow({\\n  width: 800,\\n  height: 600,\\n  show: false, // <- here\\n  webPreferences: {\\n    nodeIntegration: true,\\n  },\\n});\\n\")), mdx(\"p\", null, \"Create a function to toggle the window. So whenever we click the tray icon, it will show the window if it's hidden, and hide if it's not.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const toggleWindow = () => {\\n  if (window.isVisible()) return window.hide();\\n  return showWindow();\\n};\\n\")), mdx(\"p\", null, \"That \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"showWindow\"), \" function, which is being used inside the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"toggleWindow\"), \", is the function that will correctly position the window before showing it. For that, we will be using the library \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/pixtron/electron-traywindow-positioner\"\n  }), \"electron-traywindow-positioner\"), \", which has everything built in to position the tray icon for all platforms, using the mouse position for Linux, and the tray bounds for windows and mac.\"), mdx(\"p\", null, \"Install the library \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$ yarn add electron-traywindow-positioner\"), \" and import it. After calling the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"positioner.position\"), \", it will change the position of the window, so you can just show it using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"window.show()\"), \".\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const positioner = require('electron-traywindow-positioner');\\n\\nconst showWindow = () => {\\n  positioner.position(window, tray.getBounds());\\n  window.show();\\n};\\n\")), mdx(\"p\", null, \"Then, add a click handler to the tray, which will call that function.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const createTray = () => {\\n  tray = new Tray(path.join(__dirname, 'assets', 'icon.png'));\\n  tray.on('click', () => {\\n    toggleWindow();\\n  });\\n};\\n\")), mdx(\"p\", null, \"Now, every time you click the tray icon, it will toggle the window, and with the correct position!\", mdx(\"br\", {\n    parentName: \"p\"\n  }), \"\\n\", \"Here is the full code in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"main.js\"), \" file.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const { app, BrowserWindow, Tray } = require('electron');\\nconst positioner = require('electron-traywindow-positioner');\\nconst path = require('path');\\n\\nlet window = null;\\nlet tray = null;\\n\\nconst showWindow = () => {\\n  positioner.position(window, tray.getBounds());\\n  window.show();\\n};\\n\\nconst toggleWindow = () => {\\n  if (window.isVisible()) return window.hide();\\n  return showWindow();\\n};\\n\\nconst createTray = () => {\\n  tray = new Tray(path.join(__dirname, 'assets', 'drop.png'));\\n  tray.on('click', () => {\\n    toggleWindow();\\n  });\\n};\\n\\nconst createWindow = () => {\\n  // Create the browser window.\\n  window = new BrowserWindow({\\n    width: 800,\\n    height: 600,\\n    show: false,\\n    webPreferences: {\\n      nodeIntegration: true,\\n    },\\n  });\\n\\n  // and load the index.html of the app.\\n  window.loadFile('index.html');\\n\\n  // Open the DevTools.\\n  window.webContents.openDevTools({ mode: 'detach' });\\n};\\n\\napp.on('ready', () => {\\n  createWindow();\\n  createTray();\\n});\\n\\napp.on('window-all-closed', () => {\\n  if (process.platform !== 'darwin') {\\n    app.quit();\\n  }\\n});\\n\\napp.on('activate', () => {\\n  // On macOS it's common to re-create a window in the app when the\\n  // dock icon is clicked and there are no other windows open.\\n  if (BrowserWindow.getAllWindows().length === 0) {\\n    createWindow();\\n  }\\n});\\n\")), mdx(\"p\", null, \"That's it, very simple, but it's something that took me some time to find a great and simple solution.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"I've had a hard time trying to have the tray window appear in the correct spot for all platforms, since in Linux the  tray.getBounds…","fields":{"slug":"how-to-position-electron-tray"}}},"pageContext":{"slug":"how-to-position-electron-tray","previous":{"fields":{"slug":"how-to-be-productive"}},"next":{"fields":{"slug":"creating-electron-react-app"}}}},"staticQueryHashes":["2337317332","3351591964"]}