261 lines
6.9 KiB
TypeScript
Raw Normal View History

2019-11-11 15:53:39 +09:00
import * as path from 'path';
import * as url from 'url';
import { app, BrowserWindow, screen, ipcMain, IpcMainEvent } from 'electron';
import windowStateKeeper from 'electron-window-state';
import { EventEmitter } from 'events';
import { registerWindowStateChangedEvents } from '../lib/window-state';
import {
ElectronAppChannel,
ElectronBrowserWindowChannel,
ElectronWebContentsChannel
} from '@ucap-webmessenger/electron-core';
2020-02-06 18:20:45 +09:00
import { appStorage } from '../lib/storage';
import { now } from '../util/now';
2019-11-11 15:53:39 +09:00
export class AppWindow {
private window: BrowserWindow | null = null;
private eventEmitter = new EventEmitter();
// tslint:disable-next-line: variable-name
private _loadTime: number | null = null;
// tslint:disable-next-line: variable-name
private _rendererReadyTime: number | null = null;
2020-02-05 11:12:52 +09:00
private minWidth = 700;
private minHeight = 600;
private defaultWidth = 1160;
private defaultHeight = 800;
2019-11-11 15:53:39 +09:00
2019-11-18 15:02:24 +09:00
public constructor(private appIconPath: string) {
2019-11-11 15:53:39 +09:00
const savedWindowState = windowStateKeeper({
2020-02-05 11:12:52 +09:00
defaultWidth: this.defaultWidth,
defaultHeight: this.defaultHeight
2019-11-11 15:53:39 +09:00
});
const windowOptions: Electron.BrowserWindowConstructorOptions = {
x: savedWindowState.x,
y: savedWindowState.y,
width: savedWindowState.width,
height: savedWindowState.height,
minWidth: this.minWidth,
minHeight: this.minHeight,
center: true,
// This fixes subpixel aliasing on Windows
// See https://github.com/atom/atom/commit/683bef5b9d133cb194b476938c77cc07fd05b972
backgroundColor: '#fff',
webPreferences: {
// Disable auxclick event
// See https://developers.google.com/web/updates/2016/10/auxclick
disableBlinkFeatures: 'Auxclick',
// Enable, among other things, the ResizeObserver
experimentalFeatures: true,
nodeIntegration: true
},
acceptFirstMouse: true,
2020-02-06 18:20:45 +09:00
icon: this.appIconPath,
2020-02-06 18:24:02 +09:00
show: false
2019-11-11 15:53:39 +09:00
};
if (__DARWIN__) {
windowOptions.titleBarStyle = 'hidden';
} else if (__WIN32__) {
windowOptions.frame = false;
} else if (__LINUX__) {
2019-12-23 11:11:03 +09:00
windowOptions.frame = false;
2019-11-11 15:53:39 +09:00
}
this.window = new BrowserWindow(windowOptions);
savedWindowState.manage(this.window);
let quitting = true;
2019-11-11 15:53:39 +09:00
app.on(ElectronAppChannel.BeforeQuit, () => {
quitting = true;
});
ipcMain.on(ElectronAppChannel.WillQuit, (event: IpcMainEvent) => {
quitting = true;
event.returnValue = true;
});
// on macOS, when the user closes the window we really just hide it. This
// lets us activate quickly and keep all our interesting logic in the
// renderer.
if (__DARWIN__) {
this.window.on(ElectronBrowserWindowChannel.Close, e => {
if (!quitting) {
e.preventDefault();
}
});
2019-11-18 16:49:41 +09:00
} else if (__WIN32__) {
this.window.on(ElectronBrowserWindowChannel.Minimize, e => {
if (!quitting) {
e.preventDefault();
2019-12-04 18:10:22 +09:00
this.window.minimize();
2019-11-18 16:49:41 +09:00
}
});
this.window.on(ElectronBrowserWindowChannel.Close, e => {
// if (!quitting) {
e.preventDefault();
this.window.hide();
// }
});
this.window.on(ElectronBrowserWindowChannel.Focus, e => {
this.window.flashFrame(false);
2019-11-18 16:49:41 +09:00
});
2019-11-11 15:53:39 +09:00
}
if (__WIN32__) {
// workaround for known issue with fullscreen-ing the app and restoring
// is that some Chromium API reports the incorrect bounds, so that it
// will leave a small space at the top of the screen on every other
// maximize
//
// adapted from https://github.com/electron/electron/issues/12971#issuecomment-403956396
//
// can be tidied up once https://github.com/electron/electron/issues/12971
// has been confirmed as resolved
this.window.once(ElectronBrowserWindowChannel.ReadyToShow, () => {
2020-02-06 18:44:35 +09:00
if (appStorage.startupHideWindow) {
this.window.close();
} else {
2020-02-06 18:24:02 +09:00
this.window.show();
}
2019-11-11 15:53:39 +09:00
this.window.on(ElectronBrowserWindowChannel.Unmaximize, () => {
setTimeout(() => {
const bounds = this.window.getBounds();
bounds.width += 1;
this.window.setBounds(bounds);
bounds.width -= 1;
this.window.setBounds(bounds);
}, 5);
});
});
}
}
public load(): void {
let startLoad = 0;
this.window.webContents.once(
ElectronWebContentsChannel.DidStartLoading,
() => {
this._rendererReadyTime = null;
this._loadTime = null;
startLoad = now();
}
);
this.window.webContents.once(
ElectronWebContentsChannel.DidFinishLoad,
() => {
this.window.webContents.setVisualZoomLevelLimits(1, 1);
if (process.env.NODE_ENV === 'development') {
this.window.webContents.openDevTools();
}
this._loadTime = now() - startLoad;
}
);
this.window.webContents.on(ElectronWebContentsChannel.DidFailLoad, () => {
this.window.webContents.openDevTools();
this.window.show();
});
registerWindowStateChangedEvents(this.window);
if (__DEV__) {
this.window.loadURL('http://localhost:4200');
} else {
this.window.loadURL(
url.format({
2020-02-04 17:13:09 +09:00
pathname: path.join(
__dirname,
'..',
'ucap-webmessenger-app/index.html'
),
2019-11-11 15:53:39 +09:00
protocol: 'file:',
slashes: true
})
);
}
}
/** Is the page loaded and has the renderer signalled it's ready? */
private get rendererLoaded(): boolean {
return !!this.loadTime && !!this.rendererReadyTime;
}
public onClose(fn: () => void) {
this.window.on(ElectronBrowserWindowChannel.Closed, fn);
}
/**
* Register a function to call when the window is done loading. At that point
* the page has loaded and the renderer has signalled that it is ready.
*/
public onDidLoad(fn: () => void): EventEmitter {
return this.eventEmitter.on('did-load', fn);
}
public isMinimized() {
return this.window.isMinimized();
}
/** Is the window currently visible? */
public isVisible() {
return this.window.isVisible();
}
public restore() {
this.window.restore();
}
public focus() {
this.window.focus();
}
/** Show the window. */
public show() {
this.window.show();
}
2019-11-18 15:02:24 +09:00
public hide() {
this.window.hide();
}
2019-11-11 15:53:39 +09:00
/**
* Get the time (in milliseconds) spent loading the page.
*
* This will be `null` until `onDidLoad` is called.
*/
public get loadTime(): number | null {
return this._loadTime;
}
/**
* Get the time (in milliseconds) elapsed from the renderer being loaded to it
* signaling it was ready.
*
* This will be `null` until `onDidLoad` is called.
*/
public get rendererReadyTime(): number | null {
return this._rendererReadyTime;
}
public destroy() {
this.window.destroy();
}
public get browserWindow(): BrowserWindow | null {
return this.window;
}
}