Skip to content

Commit

Permalink
feat: Add browserWindow field in options, deprecate height, `widt…
Browse files Browse the repository at this point in the history
…h`, `x`, `y`, `alwaysOnTop` in favor of `browserWindow` (#18)

* Allow browserWindow in options

* Make cleanOptions work well

* Update readme

* Fix lint

* Update readme
  • Loading branch information
amaury1093 authored May 19, 2019
1 parent 755266c commit 0b2d897
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 81 deletions.
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ See the [`example/`](/example) folder for a working example.

The return value of `mb` is a `Menubar` class instance, which subclasses `EventEmitter` and has these additional properties:

```javascript
```
{
app: the electron require('app') instance,
window: the electron require('browser-window') instance,
Expand All @@ -70,19 +70,25 @@ You can pass an optional options object into the menubar constructor:

- `dir` (default `process.cwd()`) - the app source directory
- `index` (default `file:// + opts.dir + index.html`) - the html to load for the pop up window
- `browserWindow` - BrowserWindow options to be passed to the BrowserWindow constructor, see [Electron docs](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions). Some interesting fields to passed down are:
- `x` (default `undefined`) - the x position of the window
- `y` (default `undefined`) - the y position of the window
- `width` (default 400) - window width
- `height` (default 400) - window height
- `alwaysOnTop` (default false) - if true, the window will not hide on blur
- `icon` (default `opts.dir + IconTemplate.png`) - the png icon to use for the menubar. A good size to start with is 20x20. To support retina, supply a 2x sized image (e.g. 40x40) with `@2x` added to the end of the name, so `icon.png` and `[email protected]` and Electron will automatically use your `@2x` version on retina screens.
- `tooltip` (default empty) - menubar tray icon tooltip text
- `tray` (default created on-the-fly) - an electron `Tray` instance. if provided `opts.icon` will be ignored
- `preloadWindow` (default false) - Create [BrowserWindow](https://github.com/atom/electron/blob/master/docs/api/browser-window.md) instance before it is used -- increasing resource usage, but making the click on the menubar load faster.
- `width` (default 400) - window width
- `height` (default 400) - window height
- `x` (default null) - the x position of the window
- `y` (default null) - the y position of the window
- `alwaysOnTop` (default false) - if true, the window will not hide on blur
- `preloadWindow` (default false) - Create [BrowserWindow](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions) instance before it is used -- increasing resource usage, but making the click on the menubar load faster.
- `showOnAllWorkspaces` (default true) - Makes the window available on all OS X workspaces.
- `windowPosition` (default trayCenter and trayBottomCenter on Windows) - Sets the window position (x and y will still override this), check [positioner docs](https://github.com/jenslind/electron-positioner#docs) for valid values.
- `showDockIcon` (default false) - Configure the visibility of the application dock icon.
- `showOnRightClick` (default false) - Show the window on 'right-click' event instead of regular 'click'
- `width` _deprecated_ - Please use `options.browserWindow.width`, see [Electron docs](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions) for more info on this field.
- `height` _deprecated_ - Please use `options.browserWindow.height`, see [Electron docs](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions) for more info on this field.
- `x` _deprecated_ - Please use `options.browserWindow.x`, see [Electron docs](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions) for more info on this field.
- `y` _deprecated_ - Please use `options.browserWindow.y`, see [Electron docs](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions) for more info on this field.
- `alwaysOnTop` _deprecated_ - Please use `options.browserWindow.alwaysOnTop`, see [Electron docs](https://electronjs.org/docs/api/browser-window#new-browserwindowoptions) for more info on this field.

## Events

Expand All @@ -101,5 +107,5 @@ The return value of the menubar constructor is an event emitter:
## Tips

- Use `mb.on('after-create-window', callback)` to run things after your app has loaded. For example you could run `mb.window.openDevTools()` to open the developer tools for debugging, or load a different URL with `mb.window.loadUrl()`
- Use `mb.on('focus-lost')` if you would like to perform some operation when using the option `alwaysOnTop:true`
- Use `mb.on('focus-lost')` if you would like to perform some operation when using the option `browserWindow.alwaysOnTop: true`
- To restore focus of previous window after menubar hide, use `mb.on('after-hide', () => { mb.app.hide() } )` or similar
104 changes: 65 additions & 39 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ import { Options } from './types';
/**
* The main Menubar class. Menubar is an EventEmitter.
*/
class Menubar extends EventEmitter {
export class Menubar extends EventEmitter {
/**
* The Electron app instance.
* @see https://electronjs.org/docs/api/app
*/
public app: Electron.App;
/**
* The electron BrowserWindow instance.
* @see https://electronjs.org/docs/api/browser-window
*/
public browserWindow?: BrowserWindow;
private cachedBounds?: Electron.Rectangle; // cachedBounds are needed for double-clicked event
private options: Options;
/**
Expand All @@ -29,16 +34,11 @@ class Menubar extends EventEmitter {
* @see https://electronjs.org/docs/api/tray
*/
public tray?: Tray;
/**
* The electron BrowserWindow instance.
* @see https://electronjs.org/docs/api/browser-window
*/
public window?: BrowserWindow;

constructor (app: Electron.App, options: Options) {
constructor (app: Electron.App, options?: Partial<Options> | string) {
super();
this.app = app;
this.options = options;
this.options = cleanOptions(options);

if (app.isReady()) {
// See https://github.com/maxogden/menubar/pull/151
Expand Down Expand Up @@ -68,12 +68,12 @@ class Menubar extends EventEmitter {
if (this.supportsTrayHighlightState) {
this.tray!.setHighlightMode('never');
}
if (!this.window) {
if (!this.browserWindow) {
return;
}

this.emit('hide');
this.window.hide();
this.browserWindow.hide();
this.emit('after-hide');
}

Expand All @@ -100,10 +100,15 @@ class Menubar extends EventEmitter {
if (this.supportsTrayHighlightState) {
this.tray.setHighlightMode('always');
}
if (!this.window) {
if (!this.browserWindow) {
await this.createWindow();
}

// Use guard for TypeScript, to avoid ! everywhere
if (!this.browserWindow) {
throw new Error('Window has been initialized just above. qed.');
}

this.emit('show');

if (trayPos && trayPos.x !== 0) {
Expand All @@ -130,18 +135,20 @@ class Menubar extends EventEmitter {
const position = this.positioner.calculate(
noBoundsPosition || this.options.windowPosition,
trayPos
);

const x = this.options.x !== undefined ? this.options.x : position.x;
const y = this.options.y !== undefined ? this.options.y : position.y;

// Use guard for TypeScript
if (!this.window) {
throw new Error('Window has been initialized above');
}

this.window.setPosition(x, y);
this.window.show();
) as { x: number; y: number };

// Not using `||` because x and y can be zero.
const x =
this.options.browserWindow.x !== undefined
? this.options.browserWindow.x
: position.x;
const y =
this.options.browserWindow.y !== undefined
? this.options.browserWindow.y
: position.y;

this.browserWindow.setPosition(x, y);
this.browserWindow.show();
this.emit('after-show');
return;
}
Expand Down Expand Up @@ -194,7 +201,7 @@ class Menubar extends EventEmitter {
if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
return this.hideWindow();
}
if (this.window && this.window.isVisible()) {
if (this.browserWindow && this.browserWindow.isVisible()) {
return this.hideWindow();
}

Expand All @@ -204,35 +211,54 @@ class Menubar extends EventEmitter {

private async createWindow () {
this.emit('create-window');

// We add some default behavior for menubar's browserWindow
const defaults = {
show: false,
frame: false
show: false, // Don't show it at first
frame: false // Remove window frame
};

const winOpts = { ...defaults, ...this.options };
this.window = new BrowserWindow(winOpts);

this.positioner = new Positioner(this.window);

this.window.on('blur', () => {
this.options.alwaysOnTop ? this.emit('focus-lost') : this.hideWindow();
this.browserWindow =
this.options.browserWindow instanceof BrowserWindow
? this.options.browserWindow
: new BrowserWindow({
...defaults,
...this.options.browserWindow,
// For backward-compat, we keep allowing user doing e.g.:
// `new Menubar({ nodeIntegration: true })`
// and Menubar will pass down `nodeIntegration` to the BrowserWindow
// constructor. But we should remove this.
// https://github.com/amaurymartiny/menubar/issues/17
...this.options
});

this.positioner = new Positioner(this.browserWindow);

this.browserWindow.on('blur', () => {
if (!this.browserWindow) {
return;
}

this.browserWindow.isAlwaysOnTop()
? this.emit('focus-lost')
: this.hideWindow();
});

if (this.options.showOnAllWorkspaces !== false) {
this.window.setVisibleOnAllWorkspaces(true);
this.browserWindow.setVisibleOnAllWorkspaces(true);
}

this.window.on('close', this.windowClear.bind(this));
await this.window.loadURL(this.options.index);
this.browserWindow.on('close', this.windowClear.bind(this));
await this.browserWindow.loadURL(this.options.index);
this.emit('after-create-window');
}

private windowClear () {
this.window = undefined;
this.browserWindow = undefined;
this.emit('after-close');
}
}

export function menubar (options?: Options | string) {
return new Menubar(app, cleanOptions(options));
export function menubar (options?: Partial<Options> | string) {
return new Menubar(app, options);
}
58 changes: 42 additions & 16 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
import { Tray } from 'electron';
import { BrowserWindowConstructorOptions, Tray } from 'electron';

export interface Options {
/**
* If true, the window will not hide on blur.
* @deprecated Please pass this option inside `options.browserWindow`.
*/
alwaysOnTop?: boolean;
/**
* An Electron BrowserWindow instance, or an options object to be passed into
* the BrowserWindow constructor.
* @example
* ```javascript
* const options = { height: 640, width: 480 };
* const mb = new Menubar({
* browserWindow: new BrowserWindow(options)
* });
*
* // The above is equivalent to
*
* const options = { height: 640, width: 480 };
* const mb = new Menubar({
* browserWindow: options
* });
* ```
*/
browserWindow: BrowserWindowConstructorOptions;
/**
* The app source directory.
*/
dir: string;
/**
* Window height.
* @deprecated Please pass this option inside `options.browserWindow`.
*/
height: number;
height?: number;
/**
* The png icon to use for the menubar. A good size to start with is 20x20.
* To support retina, supply a 2x sized image (e.g. 40x40) with @2x added to
* the end of the name, so icon.png and [email protected] and Electron will
* automatically use your @2x version on retina screens.
*/
icon?: string | Electron.NativeImage;
icon: string | Electron.NativeImage;
/**
* The html to load for the pop up window.
*/
Expand All @@ -28,31 +49,29 @@ export interface Options {
* Create BrowserWindow instance before it is used -- increasing resource
* usage, but making the click on the menubar load faster.
*/
preloadWindow?: boolean;
preloadWindow: boolean;
/**
* Configure the visibility of the application dock icon.
* Configure the visibility of the application dock icon, macOS only. Calls
* [`app.dock.hide`](https://electronjs.org/docs/api/app#appdockhide-macos)
*/
showDockIcon: boolean;
/**
* Makes the window available on all OS X workspaces.
* Makes the window available on all OS X workspaces. Calls
* [`setVisibleOnAllWorkspaces`](https://electronjs.org/docs/api/browser-window#winsetvisibleonallworkspacesvisible-options)
*/
showOnAllWorkspaces?: boolean;
showOnAllWorkspaces: boolean;
/**
* Show the window on 'right-click' event instead of regular 'click'.
*/
showOnRightClick?: boolean;
showOnRightClick: boolean;
/**
* Menubar tray icon tooltip text.
* Menubar tray icon tooltip text. Calls [`tray.setTooltip`](https://electronjs.org/docs/api/tray#traysettooltiptooltip)
*/
tooltip: string;
/**
* An electron Tray instance. If provided, `options.icon` will be ignored.
*/
tray?: Tray;
/**
* the x position of the window.
*/
x?: number;
tray: Tray;
/**
* Sets the window position (x and y will still override this), check
* electron-positioner docs for valid values.
Expand All @@ -75,10 +94,17 @@ export interface Options {
| 'center';
/**
* Window width.
* @deprecated Please pass this option inside `options.browserWindow`.
*/
width?: number;
/**
* The x position of the window.
* @deprecated Please pass this option inside `options.browserWindow`.
*/
width: number;
x?: number;
/**
* the x position of the window.
* The x position of the window.
* @deprecated Please pass this option inside `options.browserWindow`.
*/
y?: number;
}
Loading

0 comments on commit 0b2d897

Please sign in to comment.