Search code examples
desktop-applicationelectronnw.jsphonegap-desktop-app

Rich HTML tray menu in a desktop web application


I want to create a tray menu app with custom buttons, sliders, maybe some nice transition effect, a header and footer like this:

menu

The application needs to work on Linux, Windows and Mac. I guessed it should be possible with a desktop web app + some HTML, but I can't find any useful example for any framework. Every example uses the OS' menu that just doesn't have the elements I need.

Can anyone direct me how to more or less implement this in any of the web app frameworks?


Solution

  • This can be done in electron quite easily, I've actually created a few tray apps myself in the below images:

    Tray app Trap app 2

    The rudimentary files you need are:

    • index.html
    • main.js
    • package.json

    In the index.html you would design your app the way you wanted it to look. In my example above I just used a couple of input boxes and styled them with CSS.

    The main.js file is where you would put your main code to power the app.

    In the package.json file is where you put the details about your app, dev dependencies etc.

    The main file you should be concerned with is the main.js file. Below is an example of the main.js file for the app above. I've added comments to help you understand:

    // Sets variables (const)
    const {app, BrowserWindow, ipcMain, Tray} = require('electron')
    const path = require('path')
    
    const assetsDirectory = path.join(__dirname, 'img')
    
    let tray = undefined
    let window = undefined
    
    // Don't show the app in the doc
    app.dock.hide()
    
    // Creates tray & window
    app.on('ready', () => {
      createTray()
      createWindow()
    })
    
    // Quit the app when the window is closed
    app.on('window-all-closed', () => {
      app.quit()
    })
    
    // Creates tray image & toggles window on click
    const createTray = () => {
      tray = new Tray(path.join(assetsDirectory, 'icon.png'))
      tray.on('click', function (event) {
        toggleWindow()
      })
    }
    
      const getWindowPosition = () => {
      const windowBounds = window.getBounds()
      const trayBounds = tray.getBounds()
    
      // Center window horizontally below the tray icon
      const x = Math.round(trayBounds.x + (trayBounds.width / 2) - (windowBounds.width / 2))
    
      // Position window 4 pixels vertically below the tray icon
      const y = Math.round(trayBounds.y + trayBounds.height + 3)
    
      return {x: x, y: y}
    }
    
    // Creates window & specifies its values
    const createWindow = () => {
      window = new BrowserWindow({
            width: 250,
            height: 310,
            show: false,
            frame: false,
            fullscreenable: false,
            resizable: false,
            transparent: true,
            'node-integration': false
        })
        // This is where the index.html file is loaded into the window
        window.loadURL('file://' + __dirname + '/index.html');
    
      // Hide the window when it loses focus
      window.on('blur', () => {
        if (!window.webContents.isDevToolsOpened()) {
          window.hide()
        }
      })
    }
    
    const toggleWindow = () => {
      if (window.isVisible()) {
        window.hide()
      } else {
        showWindow()
      }
    }
    
    const showWindow = () => {
      const position = getWindowPosition()
      window.setPosition(position.x, position.y, false)
      window.show()
      window.focus()
    }
    
    ipcMain.on('show-window', () => {
      showWindow()
    })
    

    Below is an example of the package.json file:

    {
      "name": "NAMEOFAPP",
      "description": "DESCRIPTION OF APP",
      "version": "0.1.0",
      "main": "main.js",
      "license": "MIT",
      "author": "NAME OF AUTHOR",
      "scripts": {
        "start": "electron ."
      },
      "devDependencies": {
        "electron-packager": "^8.2.0"
      }
    }
    

    So, If you create a simple index.html file saying "Hello World", place the above codes into the main.js file and package.json file respectively and run the app it will run from the tray.

    If you have no idea how to use Electron, you need to figure that out first (Electron docs). It will then become clear where to place which file and how to run the app.