Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how to make a custom title bar on macOS respect native double-click behavior #16385

Open
sindresorhus opened this issue Jan 14, 2019 · 9 comments

Comments

@sindresorhus
Copy link
Contributor

@sindresorhus sindresorhus commented Jan 14, 2019

  • Output of node_modules/.bin/electron --version: v4.0.1
  • Operating System (Platform and Version): macOS 10.14.2

Expected Behavior

When using a custom title bar on macOS with the BrowserWindow option {titleBarStyle: 'hiddenInset'} and the CSS -webkit-app-region: drag; to mark the title bar, I expected double-clicking the title bar to zoom the window (or minimize depending on the Dock setting).

Actual behavior

Nothing happens when I double-click the custom title bar.

To Reproduce

$ git clone https://github.com/sindresorhus/electron-quick-start --branch hidden-inset-double-click
$ npm install
$ npm start

Additional Information

Electron gets a lot of bad reputation for not being native. It should strive to fit as much into the native environment as possible. Small things like this matters.

This can not even be correctly worked around as there's no way to know whether the user has chosen to zoom or minimize the window on double-click.


Related issue: sindresorhus/caprine#687

@MarshallOfSound
Copy link
Member

@MarshallOfSound MarshallOfSound commented Jan 14, 2019

Solution

Good news, this is actually quite straight forward to implement (if you know the magic NSUserDefaults key). I actually implemented this in Electron Fiddle just last week --> electron/fiddle#156

The relevant code has been copied below

const handleDoubleClick = () => {
  const doubleClickAction = remote.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
  const win = remote.getCurrentWindow();
  if (doubleClickAction === 'Minimize') {
    win.minimize();
  } else if (doubleClickAction === 'Maximize') {
    if (!win.isMaximized()) {
      win.maximize();
    } else {
      win.unmaximize();
    }
  }
}

You can attach that as a doubleclick event handler to any DOM element including the element that is currently styled with -webkit-app-region: drag

IMO

IMO this shouldn't be the default behavior, the -webkit-app-region modifier is a webkit standard (trying to become a browser standard) and currently afaik does not dictate that it will intercept or use double click events.

Changing this behavior will mess with apps that don't expect this to occur. For example "floating" chat apps will not expect a double-click on that chat head to maximize the chat head 🤔 We'd have to introduce a bunch of stuff to "prevent" maximization and minimizations which is a lot of infra and complexity.

This could maybe be added to the docs / guides around frameless windows to help people implement this behavior 👍 I'm going to retag / retitle this issue as a documentation thing 👍

@MarshallOfSound MarshallOfSound changed the title Custom title bar on macOS does not respect native double-click behavior Document how to make a custom title bar on macOS respect native double-click behavior Jan 14, 2019
@sindresorhus
Copy link
Contributor Author

@sindresorhus sindresorhus commented Jan 14, 2019

That's a lot of boilerplate code nobody is going to implement. Which means many Electron apps will continue to be second-class apps.

I do agree, the solution should not affect existing apps. How about introducing an Electron-only CSS property called -electron-app-title-bar?

Alternatively, expose a method in the renderer that accepts a CSS selector or element to make the inset title bar. This method would make the element draggable (ala -webkit-app-region: drag) and also double-clickable.

@MarshallOfSound
Copy link
Member

@MarshallOfSound MarshallOfSound commented Jan 14, 2019

How about introducing an Electron-only CSS property called -electron-app-title-bar?

I'm not sure that's possible and would probably involve us patching blink, would have to look into it more though to see if that's feasible.

This method would make the element draggable (ala -webkit-app-region: drag) and also double-clickable.

That would introduce this weird amount of time between your app loading and that method running where the title bar wouldn't be usable (I know because one of my apps used to do this and it got issues raised from people saying the couldn't move the app in the first 100-200ms or so)

As an immediate action we can document this, secondarily we can look into making it a one-liner / work-out-of-the-box with a special CSS property.

@sindresorhus
Copy link
Contributor Author

@sindresorhus sindresorhus commented Jan 14, 2019

That would introduce this weird amount of time between your app loading and that method running where the title bar wouldn't be usable (I know because one of my apps used to do this and it got issues raised from people saying the couldn't move the app in the first 100-200ms or so)

I don't see how the boilerplate you posted above would do this any faster. You need the element to be available. This imaginary method wouldn't need to wait for DOM ready. All it needs is the element matching the selector to be available.

As an immediate action we can document this, secondarily we can look into making it a one-liner / work-out-of-the-box with a special CSS property.

Seems like the easiest immediate action would be to take your above boilerplate and expose it as just a simple method. For example: app.handleTitleBarDoubleClick();. Users could then just call it however we want, like you've done here: https://github.com/electron/fiddle/pull/156/files#diff-a80bb2946759571affc7054eb927d3e5R32

@Aura-zx
Copy link

@Aura-zx Aura-zx commented Apr 22, 2019

@MarshallOfSound Does it works in windows 10?

@fabiospampinato
Copy link
Contributor

@fabiospampinato fabiospampinato commented Jul 5, 2020

Good news, this is actually quite straight forward to implement (if you know the magic NSUserDefaults key).

There are a bunch of interesting things that can be done with Electron if only ones knows these "magic" strings. It might be a good idea to document them somewhere in the docs.

@fabiospampinato
Copy link
Contributor

@fabiospampinato fabiospampinato commented Jul 5, 2020

For future readers: the function mentioned in this thread that is supposed to handle this correctly is not handling the case where the double click action on titlebars is disabled entirely in system preferences, the proper function for handling this should look more like this:

const handleDoubleClick = () => {
  const win = remote.getCurrentWindow();
  if ( !win ) return; // No window, nothing to do, although this check doesn't make a whole lot of sense from the renderer process, but you shouldn't handle this from the renderer process
  if ( process.platform === 'darwin' ) { // `getUserDefault` is only available under macOS
    const action = remote.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
    if ( action === 'None' ) return; // Action disabled entirely, nothing to do
    if ( action === 'Minimize') return win.minimize(); // The user prefers to minimize the window, weird
  }
  // Toggling maximization otherwise
  // Under macOS this should actually trigger the "zoom" action, but I believe that's identical to toggling maximization for Electron apps, so we'll just do that for simplicity here
  // In case you want to trigger the zoom action for some reason: Menu.sendActionToFirstResponder ( 'zoom:' );
  if (win.isMaximized()) return win.unmaximize();
  return win.maximize ();
}

Errata corrige: actually it does technically handle the "None" case correctly, my bad 🤦‍♂️ One just has to be careful when generalizing the function for all plaforms.

@marcello3d
Copy link

@marcello3d marcello3d commented Dec 2, 2020

In case it's useful, we noticed that on Big Sur, remote.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string') seems to return the empty string ("") by default. It only returns a value if you change the option in system preferences.

@softmarshmallow
Copy link

@softmarshmallow softmarshmallow commented Jun 17, 2021

Cannot read property 'systemPreferences' of undefined I get this when double click.

EDIT

adding this solved

    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true,
    },

https://stackoverflow.com/questions/37884130/electron-remote-is-undefined

Solution

Good news, this is actually quite straight forward to implement (if you know the magic NSUserDefaults key). I actually implemented this in Electron Fiddle just last week --> electron/fiddle#156

The relevant code has been copied below

const handleDoubleClick = () => {
  const doubleClickAction = remote.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
  const win = remote.getCurrentWindow();
  if (doubleClickAction === 'Minimize') {
    win.minimize();
  } else if (doubleClickAction === 'Maximize') {
    if (!win.isMaximized()) {
      win.maximize();
    } else {
      win.unmaximize();
    }
  }
}

You can attach that as a doubleclick event handler to any DOM element including the element that is currently styled with -webkit-app-region: drag

IMO

IMO this shouldn't be the default behavior, the -webkit-app-region modifier is a webkit standard (trying to become a browser standard) and currently afaik does not dictate that it will intercept or use double click events.

Changing this behavior will mess with apps that don't expect this to occur. For example "floating" chat apps will not expect a double-click on that chat head to maximize the chat head 🤔 We'd have to introduce a bunch of stuff to "prevent" maximization and minimizations which is a lot of infra and complexity.

This could maybe be added to the docs / guides around frameless windows to help people implement this behavior 👍 I'm going to retag / retitle this issue as a documentation thing 👍

saenzramiro added a commit to ramboxapp/custom-electron-titlebar that referenced this issue Jan 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants