project initialized
This commit is contained in:
commit
cda91e40b4
2
.eslintignore
Normal file
2
.eslintignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
build/
|
||||||
|
dist/
|
3
.eslintrc.json
Normal file
3
.eslintrc.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/gts/"
|
||||||
|
}
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
pack/
|
4
.npmignore
Normal file
4
.npmignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
pack/
|
3
.prettierrc.js
Normal file
3
.prettierrc.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
...require('gts/.prettierrc.json'),
|
||||||
|
};
|
14
.vscode/settings.json
vendored
Normal file
14
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.insertSpaces": true,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnPaste": true,
|
||||||
|
"editor.autoClosingBrackets": "languageDefined",
|
||||||
|
"editor.trimAutoWhitespace": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
"files.trimFinalNewlines": true,
|
||||||
|
"files.watcherExclude": {
|
||||||
|
"**/dist": true
|
||||||
|
}
|
||||||
|
}
|
3638
package-lock.json
generated
Normal file
3638
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
Normal file
32
package.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"name": "multi-window",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"keywords": [],
|
||||||
|
"scripts": {
|
||||||
|
"lint": "gts lint",
|
||||||
|
"clean": "gts clean",
|
||||||
|
"fix": "gts fix",
|
||||||
|
"build:multi-window-electron": "ts-node ./scripts/build.ts multi-window-electron"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"lodash": "^4.17.20",
|
||||||
|
"rxjs": "^6.6.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/fs-extra": "^9.0.4",
|
||||||
|
"@types/lodash": "^4.14.165",
|
||||||
|
"@types/node": "^14.11.2",
|
||||||
|
"cross-env": "^7.0.2",
|
||||||
|
"electron": "^11.0.3",
|
||||||
|
"fs-extra": "^9.0.1",
|
||||||
|
"gts": "^3.0.2",
|
||||||
|
"husky": "^4.3.0",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"ts-node": "^9.0.0",
|
||||||
|
"typescript": "^4.0.3"
|
||||||
|
}
|
||||||
|
}
|
7
projects/multi-window-electron/package.json
Normal file
7
projects/multi-window-electron/package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "multi-window-electron",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"peerDependencies": {
|
||||||
|
"electron": "^7.0.0"
|
||||||
|
}
|
||||||
|
}
|
6
projects/multi-window-electron/project.json
Normal file
6
projects/multi-window-electron/project.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"dest": "../../dist/multi-window-electron",
|
||||||
|
"lib": {
|
||||||
|
"entryFile": "src/public-api.ts"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,553 @@
|
||||||
|
import * as url from 'url';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MultiWindowChannel,
|
||||||
|
MultiWindowWindowChannel,
|
||||||
|
} from '../types/channel.type';
|
||||||
|
import {head} from 'lodash';
|
||||||
|
|
||||||
|
export interface Template {
|
||||||
|
filePath?: string;
|
||||||
|
html?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MultiWindowPositionAlign {
|
||||||
|
vertical?: 'top' | 'middle' | 'bottom';
|
||||||
|
horizontal?: 'left' | 'center' | 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MultiWindowPosition {
|
||||||
|
displayTarget?: number;
|
||||||
|
size?: 'full' | number | {width?: 'full' | number; height?: 'full' | number};
|
||||||
|
point?: Electron.Point;
|
||||||
|
align?: MultiWindowPositionAlign;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DefaultOptions {
|
||||||
|
appIcon?: string;
|
||||||
|
optionsForBrowserWindow?: Electron.BrowserWindowConstructorOptions;
|
||||||
|
template: Template;
|
||||||
|
position?: MultiWindowPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultOptions: DefaultOptions = {
|
||||||
|
optionsForBrowserWindow: {
|
||||||
|
alwaysOnTop: true,
|
||||||
|
skipTaskbar: true,
|
||||||
|
resizable: false,
|
||||||
|
show: false,
|
||||||
|
frame: false,
|
||||||
|
transparent: true,
|
||||||
|
acceptFirstMouse: true,
|
||||||
|
},
|
||||||
|
template: {
|
||||||
|
html: `
|
||||||
|
<html>\n
|
||||||
|
<head></head>\n
|
||||||
|
<body style="overflow: hidden; -webkit-user-select: none;">\n
|
||||||
|
<div id="container">\n
|
||||||
|
<img src="" id="appIcon" />\n
|
||||||
|
<img src="" id="image" />\n
|
||||||
|
<div id="text">\n
|
||||||
|
<b id="title"></b>\n
|
||||||
|
<p id="message"></p>\n
|
||||||
|
</div>\n
|
||||||
|
<div id="close">X</div>\n
|
||||||
|
</div>\n
|
||||||
|
</body>\n
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OnShow4NotificationWindow = () => Promise<void>;
|
||||||
|
export type OnClose4NotificationWindow = () => Promise<void>;
|
||||||
|
export type OnMessage4NotificationWindow = (
|
||||||
|
channel: string,
|
||||||
|
...args: any[]
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
export interface MultiWindow {
|
||||||
|
id?: number;
|
||||||
|
optionsForBrowserWindow?: Electron.BrowserWindowConstructorOptions;
|
||||||
|
template: Template;
|
||||||
|
position?: MultiWindowPosition;
|
||||||
|
data?: any;
|
||||||
|
onShow?: OnShow4NotificationWindow;
|
||||||
|
onClose?: OnClose4NotificationWindow;
|
||||||
|
onMessage?: OnMessage4NotificationWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Activated = {
|
||||||
|
multiWindow: MultiWindow;
|
||||||
|
browserWindow: Electron.BrowserWindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class MultiWindowService {
|
||||||
|
private readonly __defaultOptions: DefaultOptions;
|
||||||
|
private __multiWindowId = 0;
|
||||||
|
private __activated: Activated[] = [];
|
||||||
|
|
||||||
|
constructor(options?: DefaultOptions) {
|
||||||
|
this.__defaultOptions = _.assignIn(defaultOptions, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(): void {
|
||||||
|
this.__initListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
this.__destroyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
open(multiWindow: MultiWindow): Promise<void> {
|
||||||
|
multiWindow.id = this.__nextMultiWindowId();
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
this.__show(multiWindow)
|
||||||
|
.then(() => {
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(reason => {
|
||||||
|
reject(reason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
close(id: number): void {
|
||||||
|
const activated = this.__findActivatedByMultiWindowId(id);
|
||||||
|
if (!activated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activated.browserWindow.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
message(id: number, channel: string, ...args: any[]): void {
|
||||||
|
const activated = this.__findActivatedByMultiWindowId(id);
|
||||||
|
if (!activated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activated.browserWindow.webContents.send(channel, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private __show(
|
||||||
|
multiWindow: MultiWindow
|
||||||
|
): Promise<Electron.BrowserWindow | undefined> {
|
||||||
|
const __this = this;
|
||||||
|
return new Promise<Electron.BrowserWindow | undefined>(
|
||||||
|
(resolve, reject) => {
|
||||||
|
const dimension = __this.__dimension(multiWindow);
|
||||||
|
__this
|
||||||
|
.__getWindow(multiWindow)
|
||||||
|
.then(browserWindow => {
|
||||||
|
browserWindow.setPosition(dimension.point.x, dimension.point.y);
|
||||||
|
__this.__activated.push({
|
||||||
|
multiWindow,
|
||||||
|
browserWindow,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!!multiWindow.onShow) {
|
||||||
|
multiWindow.onShow();
|
||||||
|
}
|
||||||
|
|
||||||
|
browserWindow.webContents.send(MultiWindowChannel.ready4Window);
|
||||||
|
|
||||||
|
browserWindow.showInactive();
|
||||||
|
return resolve(browserWindow);
|
||||||
|
})
|
||||||
|
.catch(reason => {
|
||||||
|
reject(reason);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private __getWindow(
|
||||||
|
multiWindow: MultiWindow
|
||||||
|
): Promise<Electron.BrowserWindow> {
|
||||||
|
const __this = this;
|
||||||
|
return new Promise<Electron.BrowserWindow>((resolve, reject) => {
|
||||||
|
const constructorOptions = _.assignIn(
|
||||||
|
this.__defaultOptions.optionsForBrowserWindow,
|
||||||
|
multiWindow.optionsForBrowserWindow
|
||||||
|
);
|
||||||
|
|
||||||
|
const browserWindow = new Electron.BrowserWindow(constructorOptions);
|
||||||
|
browserWindow.setVisibleOnAllWorkspaces(true);
|
||||||
|
browserWindow.loadURL(__this.__templateUrl(multiWindow));
|
||||||
|
browserWindow.webContents.once('did-finish-load', () => {
|
||||||
|
browserWindow.webContents.send(
|
||||||
|
MultiWindowChannel.init4Window,
|
||||||
|
multiWindow.data
|
||||||
|
);
|
||||||
|
resolve(browserWindow);
|
||||||
|
});
|
||||||
|
browserWindow.once('closed', () => {
|
||||||
|
__this.__onClosed4MultiWindow(browserWindow);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private __dimension(
|
||||||
|
multiWindow: MultiWindow
|
||||||
|
): {point: Electron.Point; size: Electron.Size} {
|
||||||
|
if (
|
||||||
|
(!this.__defaultOptions.optionsForBrowserWindow?.width ||
|
||||||
|
!this.__defaultOptions.optionsForBrowserWindow?.height) &&
|
||||||
|
(!multiWindow.optionsForBrowserWindow?.width ||
|
||||||
|
!multiWindow.optionsForBrowserWindow?.height)
|
||||||
|
) {
|
||||||
|
throw Error('There is not enough size information.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(!this.__defaultOptions.position && !multiWindow.position) ||
|
||||||
|
(!this.__defaultOptions.position?.point &&
|
||||||
|
!this.__defaultOptions.position?.align) ||
|
||||||
|
(!multiWindow.position?.point && !multiWindow.position?.align)
|
||||||
|
) {
|
||||||
|
throw Error('There is not enough location information.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayTarget = !!multiWindow.position?.displayTarget
|
||||||
|
? multiWindow.position?.displayTarget
|
||||||
|
: !!this.__defaultOptions.position?.displayTarget
|
||||||
|
? this.__defaultOptions.position.displayTarget
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const displays = Electron.screen.getAllDisplays();
|
||||||
|
|
||||||
|
let _displayTarget = displayTarget;
|
||||||
|
|
||||||
|
if (displays.length - 1 < _displayTarget) {
|
||||||
|
_displayTarget = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const display = displays[_displayTarget];
|
||||||
|
|
||||||
|
let size: Electron.Size = {width: 0, height: 0};
|
||||||
|
|
||||||
|
if (!!multiWindow.position.size) {
|
||||||
|
const percentage = (value: number, p: number): number => {
|
||||||
|
let _p = p;
|
||||||
|
if (0 > p) {
|
||||||
|
_p = 0;
|
||||||
|
}
|
||||||
|
if (100 < p) {
|
||||||
|
_p = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value * (p / 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ('full' === multiWindow.position.size) {
|
||||||
|
size = display.workAreaSize;
|
||||||
|
} else if (_.isNumber(multiWindow.position.size)) {
|
||||||
|
size = {
|
||||||
|
width: percentage(
|
||||||
|
display.workAreaSize.width,
|
||||||
|
multiWindow.position.size
|
||||||
|
),
|
||||||
|
height: percentage(
|
||||||
|
display.workAreaSize.height,
|
||||||
|
multiWindow.position.size
|
||||||
|
),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let width = 0;
|
||||||
|
let height = 0;
|
||||||
|
if ('full' === multiWindow.position.size.width) {
|
||||||
|
width = display.workAreaSize.width;
|
||||||
|
} else {
|
||||||
|
width = percentage(
|
||||||
|
display.workAreaSize.width,
|
||||||
|
multiWindow.position.size.width as number
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ('full' === multiWindow.position.size.height) {
|
||||||
|
height = display.workAreaSize.height;
|
||||||
|
} else {
|
||||||
|
height = percentage(
|
||||||
|
display.workAreaSize.height,
|
||||||
|
multiWindow.position.size.height as number
|
||||||
|
);
|
||||||
|
}
|
||||||
|
size = {width, height};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
!!multiWindow.optionsForBrowserWindow?.width &&
|
||||||
|
!!multiWindow.optionsForBrowserWindow?.height
|
||||||
|
) {
|
||||||
|
size = {
|
||||||
|
width: multiWindow.optionsForBrowserWindow?.width,
|
||||||
|
height: multiWindow.optionsForBrowserWindow?.height,
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
!!this.__defaultOptions.optionsForBrowserWindow?.width &&
|
||||||
|
!!this.__defaultOptions.optionsForBrowserWindow?.height
|
||||||
|
) {
|
||||||
|
size = {
|
||||||
|
width: this.__defaultOptions.optionsForBrowserWindow?.width,
|
||||||
|
height: this.__defaultOptions.optionsForBrowserWindow?.height,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
size = {width: 0, height: 0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const point: Electron.Point | undefined = !!multiWindow.position?.point
|
||||||
|
? multiWindow.position?.point
|
||||||
|
: !!this.__defaultOptions.position.point
|
||||||
|
? this.__defaultOptions.position.point
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!!point) {
|
||||||
|
return {
|
||||||
|
point,
|
||||||
|
size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const align: MultiWindowPositionAlign | undefined = !!multiWindow.position
|
||||||
|
?.align
|
||||||
|
? multiWindow.position?.align
|
||||||
|
: !!this.__defaultOptions.position.align
|
||||||
|
? this.__defaultOptions.position.align
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!!align) {
|
||||||
|
let x = 0;
|
||||||
|
let y = 0;
|
||||||
|
|
||||||
|
switch (align.horizontal) {
|
||||||
|
case 'center':
|
||||||
|
{
|
||||||
|
x =
|
||||||
|
display.workArea.x +
|
||||||
|
(display.workAreaSize.width - size.width) / 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
{
|
||||||
|
x = display.workArea.x + (display.workAreaSize.width - size.width);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
x = display.workArea.x;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (align.vertical) {
|
||||||
|
case 'middle':
|
||||||
|
{
|
||||||
|
y =
|
||||||
|
display.workArea.y +
|
||||||
|
(display.workAreaSize.height - size.height) / 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'bottom':
|
||||||
|
{
|
||||||
|
y =
|
||||||
|
display.workArea.y + (display.workAreaSize.height - size.height);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
y = display.workArea.y;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
point: {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
},
|
||||||
|
size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error('There is not enough position information.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private __nextMultiWindowId(): number {
|
||||||
|
this.__multiWindowId++;
|
||||||
|
|
||||||
|
if (Number.MAX_SAFE_INTEGER < this.__multiWindowId) {
|
||||||
|
this.__multiWindowId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.__multiWindowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private __findActivatedByMultiWindowId(id: number): Activated | undefined {
|
||||||
|
const i = this.__activated.findIndex(a => a.multiWindow.id === id);
|
||||||
|
if (-1 === i) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return this.__activated[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private __findActivatedByBrowserWindowId(id: number): Activated | undefined {
|
||||||
|
const i = this.__activated.findIndex(a => a.browserWindow.id === id);
|
||||||
|
if (-1 === i) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return this.__activated[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private __templateUrl(multiWindow: MultiWindow): string {
|
||||||
|
let u: string = '';
|
||||||
|
|
||||||
|
const filePath2Url = (filePath: string) => {
|
||||||
|
return url.format({
|
||||||
|
protocol: 'file:',
|
||||||
|
pathname: filePath,
|
||||||
|
slashes: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const html2Url = (html: string) => {
|
||||||
|
return 'data:text/html,' + encodeURIComponent(html);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!!multiWindow.template.filePath) {
|
||||||
|
return filePath2Url(multiWindow.template.filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!multiWindow.template.html) {
|
||||||
|
return html2Url(multiWindow.template.html);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!this.__defaultOptions.template.filePath) {
|
||||||
|
return filePath2Url(this.__defaultOptions.template.filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!this.__defaultOptions.template.html) {
|
||||||
|
return html2Url(this.__defaultOptions.template.html);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!defaultOptions.template.html ? defaultOptions.template.html : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private __rePositioning(): void {
|
||||||
|
this.__activated.forEach(a => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private __initListeners(): void {
|
||||||
|
Electron.ipcMain.on(
|
||||||
|
MultiWindowWindowChannel.onClose,
|
||||||
|
this.__onClose4NotificationWindow.bind(this)
|
||||||
|
);
|
||||||
|
Electron.ipcMain.on(
|
||||||
|
MultiWindowWindowChannel.onMessage,
|
||||||
|
this.__onMessage4NotificationWindow.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
Electron.screen.on(
|
||||||
|
'display-added',
|
||||||
|
this.__onDisplayAdded4ElectronScreen.bind(this)
|
||||||
|
);
|
||||||
|
Electron.screen.on(
|
||||||
|
'display-removed',
|
||||||
|
this.__onDisplayRemoved4ElectronScreen.bind(this)
|
||||||
|
);
|
||||||
|
Electron.screen.on(
|
||||||
|
'display-metrics-changed',
|
||||||
|
this.__onDisplayMetricsChanged4ElectronScreen.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private __destroyListeners(): void {
|
||||||
|
Electron.ipcMain.off(
|
||||||
|
MultiWindowWindowChannel.onClose,
|
||||||
|
this.__onClose4NotificationWindow.bind(this)
|
||||||
|
);
|
||||||
|
Electron.ipcMain.off(
|
||||||
|
MultiWindowWindowChannel.onMessage,
|
||||||
|
this.__onMessage4NotificationWindow.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
Electron.screen.off(
|
||||||
|
'display-added',
|
||||||
|
this.__onDisplayAdded4ElectronScreen.bind(this)
|
||||||
|
);
|
||||||
|
Electron.screen.off(
|
||||||
|
'display-removed',
|
||||||
|
this.__onDisplayRemoved4ElectronScreen.bind(this)
|
||||||
|
);
|
||||||
|
Electron.screen.off(
|
||||||
|
'display-metrics-changed',
|
||||||
|
this.__onDisplayMetricsChanged4ElectronScreen.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private __onClose4NotificationWindow(
|
||||||
|
event: Electron.IpcMainEvent,
|
||||||
|
notificationId: number
|
||||||
|
): void {
|
||||||
|
const activated = this.__findActivatedByMultiWindowId(notificationId);
|
||||||
|
if (!activated || !activated.multiWindow.onClose) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activated.multiWindow.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private __onMessage4NotificationWindow(
|
||||||
|
event: Electron.IpcMainEvent,
|
||||||
|
notificationId: number,
|
||||||
|
channel: string,
|
||||||
|
...args: any[]
|
||||||
|
): void {
|
||||||
|
const activated = this.__findActivatedByMultiWindowId(notificationId);
|
||||||
|
if (!activated || !activated.multiWindow.onMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activated.multiWindow.onMessage(channel, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private __onDisplayAdded4ElectronScreen(
|
||||||
|
event: Electron.Event,
|
||||||
|
newDisplay: Electron.Display
|
||||||
|
): void {
|
||||||
|
this.__rePositioning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private __onDisplayRemoved4ElectronScreen(
|
||||||
|
event: Electron.Event,
|
||||||
|
oldDisplay: Electron.Display
|
||||||
|
): void {
|
||||||
|
this.__rePositioning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private __onDisplayMetricsChanged4ElectronScreen(
|
||||||
|
event: Electron.Event,
|
||||||
|
display: Electron.Display,
|
||||||
|
changedMetrics: string[] // bounds, workArea, scaleFactor, rotation
|
||||||
|
): void {
|
||||||
|
this.__rePositioning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private __onClosed4MultiWindow(browserWindow: Electron.BrowserWindow): void {
|
||||||
|
const activated = this.__findActivatedByBrowserWindowId(browserWindow.id);
|
||||||
|
if (!activated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const i = this.__activated.findIndex(
|
||||||
|
a => a.browserWindow.id === browserWindow.id
|
||||||
|
);
|
||||||
|
if (-1 === i) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__activated.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
12
projects/multi-window-electron/src/lib/types/channel.type.ts
Normal file
12
projects/multi-window-electron/src/lib/types/channel.type.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export enum MultiWindowChannel {
|
||||||
|
init4Window = 'multi-window-electron::MultiWindowChannel::init4Window',
|
||||||
|
ready4Window = 'multi-window-electron::MultiWindowChannel::ready4Window',
|
||||||
|
reset4Window = 'multi-window-electron::MultiWindowChannel::reset4Window',
|
||||||
|
close4Window = 'multi-window-electron::MultiWindowChannel::close4Window',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MultiWindowWindowChannel {
|
||||||
|
onClose = 'multi-window-electron::MultiWindowWindowChannel::onClose',
|
||||||
|
onClick = 'multi-window-electron::MultiWindowWindowChannel::onClick',
|
||||||
|
onMessage = 'multi-window-electron::MultiWindowWindowChannel::onMessage',
|
||||||
|
}
|
3
projects/multi-window-electron/src/public-api.ts
Normal file
3
projects/multi-window-electron/src/public-api.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './lib/services/multi-window.service';
|
||||||
|
|
||||||
|
export * from './lib/types/channel.type';
|
9
projects/multi-window-electron/tsconfig.lib.json
Normal file
9
projects/multi-window-electron/tsconfig.lib.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/lib",
|
||||||
|
"target": "es2015",
|
||||||
|
"lib": ["dom"]
|
||||||
|
},
|
||||||
|
"exclude": ["src/test.ts", "**/*.spec.ts"]
|
||||||
|
}
|
3
projects/multi-window-electron/tsconfig.lib.prod.json
Normal file
3
projects/multi-window-electron/tsconfig.lib.prod.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.lib.json"
|
||||||
|
}
|
9
projects/multi-window-electron/tsconfig.spec.json
Normal file
9
projects/multi-window-electron/tsconfig.spec.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/spec",
|
||||||
|
"types": ["jasmine", "node"]
|
||||||
|
},
|
||||||
|
"files": ["src/test.ts"],
|
||||||
|
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
||||||
|
}
|
148
scripts/build.ts
Normal file
148
scripts/build.ts
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import * as path from 'path';
|
||||||
|
import {execSync} from 'child_process';
|
||||||
|
import * as fse from 'fs-extra';
|
||||||
|
|
||||||
|
async function build(args: string[]): Promise<any[]> {
|
||||||
|
const rootPath = path.join(__dirname, '..');
|
||||||
|
const packPath = path.join(rootPath, 'pack');
|
||||||
|
const projectName = args[0];
|
||||||
|
|
||||||
|
const projectPath = path.join(rootPath, 'projects', projectName);
|
||||||
|
const packageJson = require(path.join(projectPath, 'package.json'));
|
||||||
|
const projectJson = require(path.join(projectPath, 'project.json'));
|
||||||
|
|
||||||
|
const tsconfigJsonPath = path.join(projectPath, 'tsconfig.lib.prod.json');
|
||||||
|
const distPath = path.join(projectPath, projectJson.dest);
|
||||||
|
|
||||||
|
const clean = () =>
|
||||||
|
new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
console.log(`Cleaning is started.`);
|
||||||
|
execSync(`rimraf ${distPath}`, {stdio: 'inherit'});
|
||||||
|
console.log(`Cleaning is completed.`);
|
||||||
|
resolve();
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const declaration = () =>
|
||||||
|
new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const outDir = path.join(distPath, 'types');
|
||||||
|
console.log(`Generating typescript declaration files is started.`);
|
||||||
|
execSync(
|
||||||
|
`tsc --project ${tsconfigJsonPath} --module commonjs --target es5 --declaration true --emitDeclarationOnly --outDir ${outDir}`,
|
||||||
|
{stdio: 'inherit'}
|
||||||
|
);
|
||||||
|
console.log(`Generating typescript declaration files is completed.`);
|
||||||
|
resolve();
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const transpile = (
|
||||||
|
name: string,
|
||||||
|
dirName: string,
|
||||||
|
moduleName: string,
|
||||||
|
targetName: string
|
||||||
|
) =>
|
||||||
|
new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let out = '';
|
||||||
|
switch (moduleName.toLowerCase()) {
|
||||||
|
case 'amd':
|
||||||
|
case 'system':
|
||||||
|
out = `--outFile ${path.join(distPath, dirName, 'public-api.js')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
out = `--outDir ${path.join(distPath, dirName)}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const outDir = path.join(distPath, dirName);
|
||||||
|
console.log(`Generating typescript ${name} module is started.`);
|
||||||
|
execSync(
|
||||||
|
`tsc --project ${tsconfigJsonPath} --module ${moduleName} --target ${targetName} ${out}`,
|
||||||
|
{stdio: 'inherit'}
|
||||||
|
);
|
||||||
|
console.log(`Generating typescript ${name} module is completed.`);
|
||||||
|
resolve();
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const packagejson = () =>
|
||||||
|
new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
console.log(`Generating project package.json is started.`);
|
||||||
|
const projectPackageJson = {
|
||||||
|
...packageJson,
|
||||||
|
main: `cjs/public-api.js`,
|
||||||
|
module: `esm5/public-api.js`,
|
||||||
|
es2015: `es2015/public-api.js`,
|
||||||
|
types: `types/public-api.d.ts`,
|
||||||
|
sideEffects: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
fse.writeFileSync(
|
||||||
|
path.join(distPath, 'package.json'),
|
||||||
|
JSON.stringify(projectPackageJson, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Generating project package.json is completed.`);
|
||||||
|
resolve();
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const install2Local = () =>
|
||||||
|
new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
console.log(`Installing to local is started.`);
|
||||||
|
|
||||||
|
const distPackageJson = require(path.join(distPath, 'package.json'));
|
||||||
|
const packFileName = `${projectName}-${distPackageJson.version}.tgz`;
|
||||||
|
const distFilePath = path.join(distPath, packFileName);
|
||||||
|
const packFilePath = path.join(packPath, packFileName);
|
||||||
|
|
||||||
|
process.chdir(path.join(distPath));
|
||||||
|
execSync(`npm pack`, {stdio: 'inherit'});
|
||||||
|
|
||||||
|
fse.moveSync(distFilePath, packFilePath, {
|
||||||
|
overwrite: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
process.chdir(path.join(rootPath));
|
||||||
|
|
||||||
|
execSync(`npm install -D ${packFilePath}`, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Installing to local is completed.`);
|
||||||
|
resolve();
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
clean(),
|
||||||
|
declaration(),
|
||||||
|
transpile('commonjs', 'cjs', 'commonjs', 'es5'),
|
||||||
|
transpile('amd', 'amd', 'amd', 'es5'),
|
||||||
|
transpile('umd', 'umd', 'umd', 'es5'),
|
||||||
|
transpile('ESM5', 'esm5', 'es2015', 'es5'),
|
||||||
|
transpile('ES2015', 'es2015', 'es2015', 'es2015'),
|
||||||
|
packagejson(),
|
||||||
|
install2Local(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
build(process.argv.slice(2))
|
||||||
|
.then()
|
||||||
|
.catch(reason => {
|
||||||
|
console.error(reason);
|
||||||
|
});
|
8
tsconfig.json
Normal file
8
tsconfig.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/gts/tsconfig-google.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"declaration": false,
|
||||||
|
"moduleResolution": "node"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user