zaaack / node-systray

A cross-platform systray library for nodejs.
https://zaaack.github.io/node-systray/
MIT License
84 stars 26 forks source link

Update systray icon dynamically [feature request] #6

Closed Darkle closed 6 years ago

Darkle commented 6 years ago

Hi, I'm currently writing a systray app where the user can change the systray icon, it would be great if node-systray had a method where you could dynamically update the icon. At the moment I have to tell the user to restart the app for the new icon to show.

Thanks for the module btw, it's great.

zaaack commented 6 years ago

@Darkle I think you should be able to update the menu icon dynamically, the action type have three kind, update-item, update-menu, update-menu-and-item, see:
https://zaaack.github.io/node-systray/modules/_index_.html#action

Currently, you might need at least provide three fields of menu.

systray.onClick(action => {
    if (action.seq_id === 0) {
        systray.sendAction({
            type: 'update-menu',
            menu: {
              icon: '...',
              title: '...',
              tooltip: '...',
            },
            seq_id: action.seq_id,
        })
    }
})

Note: This cannot add/remove items, 'update-menu' would only update the menu fields as I list above, 'update-menu-and-item' can only update the menu fields and current item, if you want to change the items count dynamically, I guess currently you can kill the systray and recreate it. I'll try to make the API more flexible if I have time, compiling systray on three OS is a little tedious.

zaaack commented 6 years ago

Sorry for the unclear API, I actually mean the icon of systray, if the type of action update-menu then it will change the systray.

Darkle commented 6 years ago

Hey thanks that worked! 😀

I seemed to need to re-add the items though. If I just did:

        systray.sendAction({
            type: 'update-menu',
            menu: {
              icon: '...',
              title: '...',
              tooltip: '...',
            },
            seq_id: action.seq_id,
        })

It would give me an error of error: uncaughtException: Cannot read property 'map' of undefined

But re-adding the items seemed to work:

    systray.sendAction({
      type: 'update-menu',
      menu: {
        icon: base64IconData['blue'],
        title: "BlueLoss",
        tooltip: "BlueLoss",
        items: [
          {
            title: "Open BlueLoss Settings",
            tooltip: "Open BlueLoss Settings",
            enabled: true
          },
          {
            title: generateEnabledDisabledLabel(checkIfBlueLossEnabled()),
            tooltip: generateEnabledDisabledLabel(checkIfBlueLossEnabled()),
            enabled: true
          },

          {
            title: "Toggle Icon Color",
            tooltip: "Toggle Icon Color",
            enabled: true
          },
          {
            title: "Quit BlueLoss",
            tooltip: "Quit BlueLoss",
            enabled: true
          }
        ]
      },
      seq_id: action.seq_id
    })
zaaack commented 6 years ago

Feel free to reopen this or create a new one if you have other issues.

evertdespiegeleer commented 3 years ago

Possibly a bit of a more elegant way to handle this (in this case I'm changing the title dynamically, but the same thing should be possible for the icon):

I store the menu configuration in an object called actualMenu and use this object to initiate SysTray:

let actualMenu = {
  icon: 'data:image/png;base64,...',
  title: 'Test',
  tooltip: 'Tips',
  items: [{
    title: 'aa',
    tooltip: 'bb',
    checked: true,
    enabled: true
  }, {
    title: 'aa2',
    tooltip: 'bb',
    checked: false,
    enabled: true
  }]
}

const systray = new SysTray({
  menu: actualMenu,
  debug: false,
  copyDir: true
})

Then, I wrote a little function that changes the desired element inside actualMenu and makes SysTray load the new version of the menu:

const updateMenu = (changeObj) => {
  actualMenu = { ...actualMenu, ...changeObj }
  systray.sendAction({
    type: 'update-menu',
    menu: actualMenu
  })
}

updateMenu({title: 'TEST'})

I think this makes dynamically manipulating the menu much simpler.