Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Electron tray click events working reliably?

In my OSX Electron app I have a tray icon that I would like to toggle between opening and closing the Electron app window. Similar to how clicking on the OSX Dropbox tray icon will open and close the Dropbox tray menu, no matter how fast you click the tray icon.

Here is the code I'm using:

  tray.on('click', function(e){
    if (mainWindow.isVisible()) {
      mainWindow.hide()
    } else {
      mainWindow.show()
    }
  });

This works if you click slowly (wait a second between clicks) however if you click repeatedly, more than 1x in a second, the click fails and nothing happens. I couldn't find any type of delays in the docs. Any ideas on what's going on and how to make the click event work reliably?

like image 836
AnApprentice Avatar asked Jul 05 '16 00:07

AnApprentice


2 Answers

The problem you're describing is easy to reproduce. The result you're getting is not a bug or a wrong implementation on your side but it's the expected result regarding the current way Electron is handling these click events on a tray element.

The class Tray exposes 3 events relative to click: click, double-click and right-click.

If you use the right-click event, you're not going to have this issue, you can click as fast as you want, you'll get your callback called every times.

The Electron code for macOS for example to handle this event is the following:

- (void)rightMouseUp:(NSEvent*)event {
  trayIcon_->NotifyRightClicked(
    [self getBoundsFromEvent:event],
    ui::EventFlagsFromModifiers([event modifierFlags]));
}

For every right click, they're firing the right-click event and that's it.

Now if we take a look at how the left click are handled, the code is slightly different:

- (void)mouseUp:(NSEvent*)event {

  // ...
  // Truncated to only show the relevant part...
  // ...

  // Single click event.
  if (event.clickCount == 1)
    trayIcon_->NotifyClicked(
        [self getBoundsFromEvent:event],
        ui::EventFlagsFromModifiers([event modifierFlags]));

  // Double click event.
  if (event.clickCount == 2)
    trayIcon_->NotifyDoubleClicked(
        [self getBoundsFromEvent:event],
        ui::EventFlagsFromModifiers([event modifierFlags]));

  [self setNeedsDisplay:YES];
}

When the tray icon get clicked multiple times, the event.clickCount doesn't always return 1. Instead, it returns a value that counts the clicked times.

So when you're click the tray icon very fast, event.clickCount will have a value greater than 2 and they're only emitting an event when the value is 1 or 2 and if it's not the case, they don't have any fallback, they simply don't emit any event. That's the result you're seeing in your tests when clicking fast enough.

So without modifying the Electron implementation yourself, submitting an issue or a pull request, you can't at the moment avoid this behaviour.

like image 195
HiDeo Avatar answered Oct 09 '22 12:10

HiDeo


Electron 3.0 introduced an API that prevents waiting for double-click.

// Ignore double click events for the tray icon
tray.setIgnoreDoubleClickEvents(true)

"Sets the option to ignore double click events. Ignoring these events allows you to detect every individual click of the tray icon. This value is set to false by default."

Related Docs | Release Notes for Electron 3.0

like image 23
aguynamedben Avatar answered Oct 09 '22 11:10

aguynamedben