Rich HTML tray menu in a desktop web application
Asked Answered
D

1

5

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?

Demerit answered 8/11, 2016 at 10:56 Comment(0)
I
15

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.

Incarcerate answered 8/11, 2016 at 11:42 Comment(8)
Thanks! So this code is showing a borderless window next to the tray icon. That's neat!Demerit
If you want content (like headers/text etc) to be inside the window, you need to edit the index.html file, just like you would with a websiteIncarcerate
Would this work with different panel positions? (left, right, top, bottom)Demerit
Oh, I thought you can move it around.:)Demerit
You can move the window around, but not the actual tray icon. In the main.js code above where it says: // Center window horizontally below the tray icon you can edit it so that is moves horizontally either left or right. Same goes for the line below it, to move it verticallyIncarcerate
getBounds() doesn't work on Linux. Any idea how I get tray's coordinates?Demerit
I'm not too sure as I don't use Linux, although this seems to be a non fixed issue - bugs.chromium.org/p/chromium/issues/detail?id=309969Incarcerate
Why not just window.loadFile('index.html') ? Instead of window.loadURL('file://' + __dirname + '/index.html');Runkle

© 2022 - 2024 — McMap. All rights reserved.