mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
Typescript - Optimize window actions
This commit is contained in:
parent
1054884417
commit
81f0a1460e
@ -6,8 +6,8 @@ import { logger } from '../common/logger';
|
|||||||
import { autoLaunchInstance as autoLaunch } from './auto-launch-controller';
|
import { autoLaunchInstance as autoLaunch } from './auto-launch-controller';
|
||||||
import { config, IConfig } from './config-handler';
|
import { config, IConfig } from './config-handler';
|
||||||
import { exportCrashDumps, exportLogs } from './reports';
|
import { exportCrashDumps, exportLogs } from './reports';
|
||||||
|
import { updateAlwaysOnTop } from './window-actions';
|
||||||
import { windowHandler } from './window-handler';
|
import { windowHandler } from './window-handler';
|
||||||
import { updateAlwaysOnTop } from './window-utils';
|
|
||||||
|
|
||||||
export const menuSections = {
|
export const menuSections = {
|
||||||
about: 'about',
|
about: 'about',
|
||||||
|
@ -4,7 +4,10 @@ import { apiCmds, apiName, IApiArgs } from '../common/api-interface';
|
|||||||
import { LocaleType } from '../common/i18n';
|
import { LocaleType } from '../common/i18n';
|
||||||
import { logger } from '../common/logger';
|
import { logger } from '../common/logger';
|
||||||
import { activityDetection } from './activity-detection';
|
import { activityDetection } from './activity-detection';
|
||||||
|
import { config } from './config-handler';
|
||||||
|
import { checkProtocolAction, setProtocolWindow } from './protocol-handler';
|
||||||
import { screenSnippet } from './screen-snippet';
|
import { screenSnippet } from './screen-snippet';
|
||||||
|
import { activate, handleKeyPress } from './window-actions';
|
||||||
import { windowHandler } from './window-handler';
|
import { windowHandler } from './window-handler';
|
||||||
import {
|
import {
|
||||||
isValidWindow,
|
isValidWindow,
|
||||||
@ -29,33 +32,30 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (arg.cmd) {
|
switch (arg.cmd) {
|
||||||
/*case ApiCmds.isOnline:
|
case apiCmds.isOnline:
|
||||||
if (typeof arg.isOnline === 'boolean') {
|
if (typeof arg.isOnline === 'boolean') {
|
||||||
windowMgr.setIsOnline(arg.isOnline);
|
windowHandler.isOnline = arg.isOnline;
|
||||||
}
|
}
|
||||||
break;*/
|
break;
|
||||||
case apiCmds.setBadgeCount:
|
case apiCmds.setBadgeCount:
|
||||||
if (typeof arg.count === 'number') {
|
if (typeof arg.count === 'number') {
|
||||||
showBadgeCount(arg.count);
|
showBadgeCount(arg.count);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/*case ApiCmds.registerProtocolHandler:
|
case apiCmds.registerProtocolHandler:
|
||||||
protocolHandler.setProtocolWindow(event.sender);
|
setProtocolWindow(event.sender);
|
||||||
protocolHandler.checkProtocolAction();
|
checkProtocolAction();
|
||||||
break;*/
|
break;
|
||||||
case apiCmds.badgeDataUrl:
|
case apiCmds.badgeDataUrl:
|
||||||
if (typeof arg.dataUrl === 'string' && typeof arg.count === 'number') {
|
if (typeof arg.dataUrl === 'string' && typeof arg.count === 'number') {
|
||||||
setDataUrl(arg.dataUrl, arg.count);
|
setDataUrl(arg.dataUrl, arg.count);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/*case ApiCmds.activate:
|
case apiCmds.activate:
|
||||||
if (typeof arg.windowName === 'string') {
|
if (typeof arg.windowName === 'string') {
|
||||||
windowMgr.activate(arg.windowName);
|
activate(arg.windowName);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ApiCmds.registerBoundsChange:
|
|
||||||
windowMgr.setBoundsChangeWindow(event.sender);
|
|
||||||
break;*/
|
|
||||||
case apiCmds.registerLogger:
|
case apiCmds.registerLogger:
|
||||||
// renderer window that has a registered logger from JS.
|
// renderer window that has a registered logger from JS.
|
||||||
logger.setLoggerWindow(event.sender);
|
logger.setLoggerWindow(event.sender);
|
||||||
@ -76,12 +76,13 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
|
|||||||
sanitize(arg.windowName);
|
sanitize(arg.windowName);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/*case ApiCmds.bringToFront:
|
case apiCmds.bringToFront:
|
||||||
// validates the user bring to front config and activates the wrapper
|
// validates the user bring to front config and activates the wrapper
|
||||||
if (typeof arg.reason === 'string' && arg.reason === 'notification') {
|
if (typeof arg.reason === 'string' && arg.reason === 'notification') {
|
||||||
bringToFront(arg.windowName, arg.reason);
|
const shouldBringToFront = config.getConfigFields([ 'bringToFront' ]);
|
||||||
|
if (shouldBringToFront) activate(arg.windowName, false);
|
||||||
}
|
}
|
||||||
break;*/
|
break;
|
||||||
case apiCmds.openScreenPickerWindow:
|
case apiCmds.openScreenPickerWindow:
|
||||||
if (Array.isArray(arg.sources) && typeof arg.id === 'number') {
|
if (Array.isArray(arg.sources) && typeof arg.id === 'number') {
|
||||||
windowHandler.createScreenPickerWindow(event.sender, arg.sources, arg.id);
|
windowHandler.createScreenPickerWindow(event.sender, arg.sources, arg.id);
|
||||||
@ -114,11 +115,11 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
|
|||||||
updateLocale(arg.locale as LocaleType);
|
updateLocale(arg.locale as LocaleType);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/*case ApiCmds.keyPress:
|
case apiCmds.keyPress:
|
||||||
if (typeof arg.keyCode === 'number') {
|
if (typeof arg.keyCode === 'number') {
|
||||||
windowMgr.handleKeyPress(arg.keyCode);
|
handleKeyPress(arg.keyCode);
|
||||||
}
|
}
|
||||||
break;*/
|
break;
|
||||||
case apiCmds.openScreenSnippet:
|
case apiCmds.openScreenSnippet:
|
||||||
screenSnippet.capture(event.sender);
|
screenSnippet.capture(event.sender);
|
||||||
break;
|
break;
|
||||||
|
@ -21,7 +21,7 @@ const setProtocolUrl = (uri: string): void => {
|
|||||||
* Processes a protocol uri
|
* Processes a protocol uri
|
||||||
* @param {String} uri - the uri opened in the format 'symphony://abc?def=ghi'
|
* @param {String} uri - the uri opened in the format 'symphony://abc?def=ghi'
|
||||||
*/
|
*/
|
||||||
const processProtocolUri = (uri: string): void => {
|
export const processProtocolUri = (uri: string): void => {
|
||||||
|
|
||||||
logger.info(`Processing protocol action, uri ${uri}`);
|
logger.info(`Processing protocol action, uri ${uri}`);
|
||||||
if (!protocolWindow) {
|
if (!protocolWindow) {
|
||||||
@ -41,7 +41,7 @@ const processProtocolUri = (uri: string): void => {
|
|||||||
* @param uri
|
* @param uri
|
||||||
* @param isAppAlreadyOpen {Boolean} whether the app is already open
|
* @param isAppAlreadyOpen {Boolean} whether the app is already open
|
||||||
*/
|
*/
|
||||||
const handleProtocolAction = (uri: string, isAppAlreadyOpen: boolean): void => {
|
export const handleProtocolAction = (uri: string, isAppAlreadyOpen: boolean): void => {
|
||||||
|
|
||||||
if (!isAppAlreadyOpen) {
|
if (!isAppAlreadyOpen) {
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ const handleProtocolAction = (uri: string, isAppAlreadyOpen: boolean): void => {
|
|||||||
* @param argv {Array} an array of command line arguments
|
* @param argv {Array} an array of command line arguments
|
||||||
* @param isAppAlreadyOpen {Boolean} whether the app is already open
|
* @param isAppAlreadyOpen {Boolean} whether the app is already open
|
||||||
*/
|
*/
|
||||||
const processProtocolArgv = (argv: string[], isAppAlreadyOpen: boolean): void => {
|
export const processProtocolArgv = (argv: string[], isAppAlreadyOpen: boolean): void => {
|
||||||
|
|
||||||
// In case of windows, we need to handle protocol handler
|
// In case of windows, we need to handle protocol handler
|
||||||
// manually because electron doesn't emit
|
// manually because electron doesn't emit
|
||||||
@ -99,7 +99,7 @@ const processProtocolArgv = (argv: string[], isAppAlreadyOpen: boolean): void =>
|
|||||||
* Sets the protocol window
|
* Sets the protocol window
|
||||||
* @param {Object} win - the renderer window
|
* @param {Object} win - the renderer window
|
||||||
*/
|
*/
|
||||||
const setProtocolWindow = (win: Electron.WebContents): void => {
|
export const setProtocolWindow = (win: Electron.WebContents): void => {
|
||||||
logger.info(`Setting protocol window ${win}`);
|
logger.info(`Setting protocol window ${win}`);
|
||||||
protocolWindow = win;
|
protocolWindow = win;
|
||||||
};
|
};
|
||||||
@ -107,7 +107,7 @@ const setProtocolWindow = (win: Electron.WebContents): void => {
|
|||||||
/**
|
/**
|
||||||
* Checks to see if the app was opened by a uri
|
* Checks to see if the app was opened by a uri
|
||||||
*/
|
*/
|
||||||
const checkProtocolAction = (): void => {
|
export const checkProtocolAction = (): void => {
|
||||||
logger.info('Checking if we have a cached protocol url');
|
logger.info('Checking if we have a cached protocol url');
|
||||||
if (protocolUrl) {
|
if (protocolUrl) {
|
||||||
logger.info(`Found a cached protocol url (${protocolUrl}), processing it`);
|
logger.info(`Found a cached protocol url (${protocolUrl}), processing it`);
|
||||||
@ -121,9 +121,7 @@ const checkProtocolAction = (): void => {
|
|||||||
* Gets the protocol url set against an instance
|
* Gets the protocol url set against an instance
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
const getProtocolUrl = (): string | undefined => {
|
export const getProtocolUrl = (): string | undefined => {
|
||||||
logger.info(`Getting the property protocol url ${protocolUrl}`);
|
logger.info(`Getting the property protocol url ${protocolUrl}`);
|
||||||
return protocolUrl;
|
return protocolUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
export {processProtocolUri, processProtocolArgv, setProtocolWindow, checkProtocolAction, getProtocolUrl};
|
|
@ -9,8 +9,8 @@ import { IScreenSnippet } from '../common/api-interface';
|
|||||||
import { isDevEnv, isMac } from '../common/env';
|
import { isDevEnv, isMac } from '../common/env';
|
||||||
import { i18n } from '../common/i18n';
|
import { i18n } from '../common/i18n';
|
||||||
import { logger } from '../common/logger';
|
import { logger } from '../common/logger';
|
||||||
|
import { updateAlwaysOnTop } from './window-actions';
|
||||||
import { windowHandler } from './window-handler';
|
import { windowHandler } from './window-handler';
|
||||||
import { updateAlwaysOnTop } from './window-utils';
|
|
||||||
|
|
||||||
const readFile = util.promisify(fs.readFile);
|
const readFile = util.promisify(fs.readFile);
|
||||||
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import { IBoundsChange, KeyCodes } from '../common/api-interface';
|
||||||
|
import { isWindowsOS } from '../common/env';
|
||||||
import { throttle } from '../common/utils';
|
import { throttle } from '../common/utils';
|
||||||
import { config } from './config-handler';
|
import { config } from './config-handler';
|
||||||
import { ICustomBrowserWindow } from './window-handler';
|
import { ICustomBrowserWindow, windowHandler } from './window-handler';
|
||||||
|
import { showPopupMenu } from './window-utils';
|
||||||
|
|
||||||
export const saveWindowSettings = (): void => {
|
export const saveWindowSettings = (): void => {
|
||||||
const browserWindow = BrowserWindow.getFocusedWindow() as ICustomBrowserWindow;
|
const browserWindow = BrowserWindow.getFocusedWindow() as ICustomBrowserWindow;
|
||||||
@ -11,7 +14,7 @@ export const saveWindowSettings = (): void => {
|
|||||||
const [ x, y ] = browserWindow.getPosition();
|
const [ x, y ] = browserWindow.getPosition();
|
||||||
const [ width, height ] = browserWindow.getSize();
|
const [ width, height ] = browserWindow.getSize();
|
||||||
if (x && y && width && height) {
|
if (x && y && width && height) {
|
||||||
browserWindow.webContents.send('boundChanges', { x, y, width, height });
|
browserWindow.webContents.send('boundChanges', { x, y, width, height, windowName: browserWindow.winName } as IBoundsChange);
|
||||||
|
|
||||||
if (browserWindow.winName === 'main') {
|
if (browserWindow.winName === 'main') {
|
||||||
const isMaximized = browserWindow.isMaximized();
|
const isMaximized = browserWindow.isMaximized();
|
||||||
@ -37,4 +40,86 @@ export const leaveFullScreen = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const throttledWindowChanges = throttle(saveWindowSettings, 1000);
|
export const throttledWindowChanges = throttle(saveWindowSettings, 1000);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries finding a window we have created with given name. If found, then
|
||||||
|
* brings to front and gives focus.
|
||||||
|
*
|
||||||
|
* @param {string} windowName Name of target window. Note: main window has
|
||||||
|
* name 'main'.
|
||||||
|
* @param {Boolean} shouldFocus whether to get window to focus or just show
|
||||||
|
* without giving focus
|
||||||
|
*/
|
||||||
|
export const activate = (windowName: string, shouldFocus: boolean = true): void => {
|
||||||
|
|
||||||
|
// Electron-136: don't activate when the app is reloaded programmatically
|
||||||
|
if (windowHandler.isAutoReload) return;
|
||||||
|
|
||||||
|
const windows = windowHandler.getAllWindows();
|
||||||
|
for (const key in windows) {
|
||||||
|
if (windows.hasOwnProperty(key)) {
|
||||||
|
const window = windows[ key ];
|
||||||
|
if (window && !window.isDestroyed() && window.winName === windowName) {
|
||||||
|
|
||||||
|
// Bring the window to the top without focusing
|
||||||
|
// Flash task bar icon in Windows for windows
|
||||||
|
if (!shouldFocus) {
|
||||||
|
window.moveTop();
|
||||||
|
return isWindowsOS ? window.flashFrame(true) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.isMinimized() ? window.restore() : window.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets always on top property based on isAlwaysOnTop
|
||||||
|
*
|
||||||
|
* @param shouldSetAlwaysOnTop
|
||||||
|
* @param shouldActivateMainWindow
|
||||||
|
*/
|
||||||
|
export const updateAlwaysOnTop = (shouldSetAlwaysOnTop: boolean, shouldActivateMainWindow: boolean = true): void => {
|
||||||
|
const browserWins: ICustomBrowserWindow[] = BrowserWindow.getAllWindows() as ICustomBrowserWindow[];
|
||||||
|
if (browserWins.length > 0) {
|
||||||
|
browserWins
|
||||||
|
.filter((browser) => typeof browser.notificationObj !== 'object')
|
||||||
|
.forEach((browser) => browser.setAlwaysOnTop(shouldSetAlwaysOnTop));
|
||||||
|
|
||||||
|
// An issue where changing the alwaysOnTop property
|
||||||
|
// focus the pop-out window
|
||||||
|
// Issue - Electron-209/470
|
||||||
|
const mainWindow = windowHandler.getMainWindow();
|
||||||
|
if (mainWindow && mainWindow.winName && shouldActivateMainWindow) {
|
||||||
|
activate(mainWindow.winName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that handles key press
|
||||||
|
*
|
||||||
|
* @param key {number}
|
||||||
|
*/
|
||||||
|
export const handleKeyPress = (key: number): void => {
|
||||||
|
switch (key) {
|
||||||
|
case KeyCodes.Esc: {
|
||||||
|
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||||
|
|
||||||
|
if (focusedWindow && !focusedWindow.isDestroyed() && focusedWindow.isFullScreen()) {
|
||||||
|
focusedWindow.setFullScreen(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KeyCodes.Alt:
|
||||||
|
const browserWin = BrowserWindow.getFocusedWindow();
|
||||||
|
if (browserWin && !browserWin.isDestroyed()) {
|
||||||
|
showPopupMenu({ window: browserWin });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -86,6 +86,7 @@ export class WindowHandler {
|
|||||||
|
|
||||||
public appMenu: AppMenu | null;
|
public appMenu: AppMenu | null;
|
||||||
public isAutoReload: boolean;
|
public isAutoReload: boolean;
|
||||||
|
public isOnline: boolean;
|
||||||
|
|
||||||
private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
|
private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
|
||||||
private readonly globalConfig: IConfig;
|
private readonly globalConfig: IConfig;
|
||||||
@ -108,6 +109,7 @@ export class WindowHandler {
|
|||||||
this.windows = {};
|
this.windows = {};
|
||||||
this.windowOpts = { ...this.getMainWindowOpts(), ...opts };
|
this.windowOpts = { ...this.getMainWindowOpts(), ...opts };
|
||||||
this.isAutoReload = false;
|
this.isAutoReload = false;
|
||||||
|
this.isOnline = true;
|
||||||
this.isCustomTitleBarAndWindowOS = isWindowsOS && this.config.isCustomTitleBar;
|
this.isCustomTitleBarAndWindowOS = isWindowsOS && this.config.isCustomTitleBar;
|
||||||
|
|
||||||
this.appMenu = null;
|
this.appMenu = null;
|
||||||
|
@ -2,11 +2,11 @@ import { app, BrowserWindow, nativeImage } from 'electron';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
|
|
||||||
import { isMac, isWindowsOS } from '../common/env';
|
import { isMac } from '../common/env';
|
||||||
import { i18n, LocaleType } from '../common/i18n';
|
import { i18n, LocaleType } from '../common/i18n';
|
||||||
import { logger } from '../common/logger';
|
import { logger } from '../common/logger';
|
||||||
import { screenSnippet } from './screen-snippet';
|
import { screenSnippet } from './screen-snippet';
|
||||||
import { ICustomBrowserWindow, windowHandler } from './window-handler';
|
import { windowHandler } from './window-handler';
|
||||||
|
|
||||||
const checkValidWindow = true;
|
const checkValidWindow = true;
|
||||||
|
|
||||||
@ -116,62 +116,6 @@ export const setDataUrl = (dataUrl: string, count: number): void => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries finding a window we have created with given name. If found, then
|
|
||||||
* brings to front and gives focus.
|
|
||||||
*
|
|
||||||
* @param {string} windowName Name of target window. Note: main window has
|
|
||||||
* name 'main'.
|
|
||||||
* @param {Boolean} shouldFocus whether to get window to focus or just show
|
|
||||||
* without giving focus
|
|
||||||
*/
|
|
||||||
export const activate = (windowName: string, shouldFocus: boolean = true): void => {
|
|
||||||
|
|
||||||
// Electron-136: don't activate when the app is reloaded programmatically
|
|
||||||
if (windowHandler.isAutoReload) return;
|
|
||||||
|
|
||||||
const windows = windowHandler.getAllWindows();
|
|
||||||
for (const key in windows) {
|
|
||||||
if (windows.hasOwnProperty(key)) {
|
|
||||||
const window = windows[ key ];
|
|
||||||
if (window && !window.isDestroyed() && window.winName === windowName) {
|
|
||||||
|
|
||||||
// Bring the window to the top without focusing
|
|
||||||
// Flash task bar icon in Windows for windows
|
|
||||||
if (!shouldFocus) {
|
|
||||||
window.moveTop();
|
|
||||||
return isWindowsOS ? window.flashFrame(true) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return window.isMinimized() ? window.restore() : window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets always on top property based on isAlwaysOnTop
|
|
||||||
*
|
|
||||||
* @param shouldSetAlwaysOnTop
|
|
||||||
* @param shouldActivateMainWindow
|
|
||||||
*/
|
|
||||||
export const updateAlwaysOnTop = (shouldSetAlwaysOnTop: boolean, shouldActivateMainWindow: boolean = true): void => {
|
|
||||||
const browserWins: ICustomBrowserWindow[] = BrowserWindow.getAllWindows() as ICustomBrowserWindow[];
|
|
||||||
if (browserWins.length > 0) {
|
|
||||||
browserWins
|
|
||||||
.filter((browser) => typeof browser.notificationObj !== 'object')
|
|
||||||
.forEach((browser) => browser.setAlwaysOnTop(shouldSetAlwaysOnTop));
|
|
||||||
|
|
||||||
// An issue where changing the alwaysOnTop property
|
|
||||||
// focus the pop-out window
|
|
||||||
// Issue - Electron-209/470
|
|
||||||
const mainWindow = windowHandler.getMainWindow();
|
|
||||||
if (mainWindow && mainWindow.winName && shouldActivateMainWindow) {
|
|
||||||
activate(mainWindow.winName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure events comes from a window that we have created.
|
* Ensure events comes from a window that we have created.
|
||||||
*
|
*
|
||||||
|
@ -63,4 +63,13 @@ export interface IScreenSnippet {
|
|||||||
data?: string;
|
data?: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
type: ScreenSnippetDataType;
|
type: ScreenSnippetDataType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBoundsChange extends Electron.Rectangle {
|
||||||
|
windowName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum KeyCodes {
|
||||||
|
Esc = 27,
|
||||||
|
Alt = 18,
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ import * as React from 'react';
|
|||||||
|
|
||||||
import { apiCmds, apiName } from '../../common/api-interface';
|
import { apiCmds, apiName } from '../../common/api-interface';
|
||||||
import { i18n } from '../../common/i18n';
|
import { i18n } from '../../common/i18n';
|
||||||
import { throttle } from '../../common/utils';
|
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
isMaximized: boolean;
|
isMaximized: boolean;
|
||||||
@ -11,11 +10,6 @@ interface IState {
|
|||||||
titleBarHeight: string;
|
titleBarHeight: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum KeyCodes {
|
|
||||||
Esc = 27,
|
|
||||||
Alt = 18,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class WindowsTitleBar extends React.Component<{}, IState> {
|
export default class WindowsTitleBar extends React.Component<{}, IState> {
|
||||||
private readonly window: Electron.BrowserWindow;
|
private readonly window: Electron.BrowserWindow;
|
||||||
private readonly eventHandlers = {
|
private readonly eventHandlers = {
|
||||||
@ -25,14 +19,10 @@ export default class WindowsTitleBar extends React.Component<{}, IState> {
|
|||||||
onShowMenu: () => this.showMenu(),
|
onShowMenu: () => this.showMenu(),
|
||||||
onUnmaximize: () => this.unmaximize(),
|
onUnmaximize: () => this.unmaximize(),
|
||||||
};
|
};
|
||||||
private isAltKey: boolean;
|
|
||||||
private isMenuOpen: boolean;
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.window = remote.getCurrentWindow();
|
this.window = remote.getCurrentWindow();
|
||||||
this.isAltKey = false;
|
|
||||||
this.isMenuOpen = false;
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isFullScreen: this.window.isFullScreen(),
|
isFullScreen: this.window.isFullScreen(),
|
||||||
isMaximized: this.window.isMaximized(),
|
isMaximized: this.window.isMaximized(),
|
||||||
@ -47,32 +37,6 @@ export default class WindowsTitleBar extends React.Component<{}, IState> {
|
|||||||
this.window.on('unmaximize', () => this.updateState({ isMaximized: false }));
|
this.window.on('unmaximize', () => this.updateState({ isMaximized: false }));
|
||||||
this.window.on('enter-full-screen', () => this.updateState({ isFullScreen: true }));
|
this.window.on('enter-full-screen', () => this.updateState({ isFullScreen: true }));
|
||||||
this.window.on('leave-full-screen', () => this.updateState({ isFullScreen: false }));
|
this.window.on('leave-full-screen', () => this.updateState({ isFullScreen: false }));
|
||||||
|
|
||||||
// Handle key down events
|
|
||||||
const throttledKeyDown = throttle( (event) => {
|
|
||||||
this.isAltKey = event.keyCode === KeyCodes.Alt;
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
// Handle key up events
|
|
||||||
const throttledKeyUp = throttle( (event) => {
|
|
||||||
if (this.isAltKey && (event.keyCode === KeyCodes.Alt || KeyCodes.Esc)) {
|
|
||||||
this.isMenuOpen = !this.isMenuOpen;
|
|
||||||
}
|
|
||||||
if (this.isAltKey && this.isMenuOpen && event.keyCode === KeyCodes.Alt) {
|
|
||||||
this.showMenu();
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
// Handle mouse down event
|
|
||||||
const throttleMouseDown = throttle(() => {
|
|
||||||
if (this.isAltKey && this.isMenuOpen) {
|
|
||||||
this.isMenuOpen = !this.isMenuOpen;
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
window.addEventListener('keyup', throttledKeyUp, true);
|
|
||||||
window.addEventListener('keydown', throttledKeyDown, true);
|
|
||||||
window.addEventListener('mousedown', throttleMouseDown, { capture: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
@ -6,16 +6,21 @@ import {
|
|||||||
apiName,
|
apiName,
|
||||||
IActivityDetection,
|
IActivityDetection,
|
||||||
IBadgeCount,
|
IBadgeCount,
|
||||||
IScreenSnippet,
|
IBoundsChange,
|
||||||
|
IScreenSnippet, KeyCodes,
|
||||||
} from '../common/api-interface';
|
} from '../common/api-interface';
|
||||||
import { i18n, LocaleType } from '../common/i18n';
|
import { i18n, LocaleType } from '../common/i18n';
|
||||||
import { throttle } from '../common/utils';
|
import { throttle } from '../common/utils';
|
||||||
import { getSource } from './desktop-capturer';
|
import { getSource } from './desktop-capturer';
|
||||||
|
|
||||||
|
let isAltKey = false;
|
||||||
|
let isMenuOpen = false;
|
||||||
|
|
||||||
interface ILocalObject {
|
interface ILocalObject {
|
||||||
ipcRenderer;
|
ipcRenderer;
|
||||||
activityDetection?: (arg: IActivityDetection) => void;
|
activityDetectionCallback?: (arg: IActivityDetection) => void;
|
||||||
screenSnippet?: (arg: IScreenSnippet) => void;
|
screenSnippetCallback?: (arg: IScreenSnippet) => void;
|
||||||
|
boundsChangeCallback?: (arg: IBoundsChange) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const local: ILocalObject = {
|
const local: ILocalObject = {
|
||||||
@ -68,12 +73,12 @@ export class SSFApi {
|
|||||||
* Allows JS to register a activity detector that can be used by electron main process.
|
* Allows JS to register a activity detector that can be used by electron main process.
|
||||||
*
|
*
|
||||||
* @param {Object} period - minimum user idle time in millisecond
|
* @param {Object} period - minimum user idle time in millisecond
|
||||||
* @param {Object} activityDetection - function that can be called accepting
|
* @param {Object} activityDetectionCallback - function that can be called accepting
|
||||||
* @example registerActivityDetection(40000, func)
|
* @example registerActivityDetection(40000, func)
|
||||||
*/
|
*/
|
||||||
public registerActivityDetection(period: number, activityDetection: Partial<ILocalObject>): void {
|
public registerActivityDetection(period: number, activityDetectionCallback: Partial<ILocalObject>): void {
|
||||||
if (typeof activityDetection === 'function') {
|
if (typeof activityDetectionCallback === 'function') {
|
||||||
local.activityDetection = activityDetection;
|
local.activityDetectionCallback = activityDetectionCallback;
|
||||||
|
|
||||||
// only main window can register
|
// only main window can register
|
||||||
local.ipcRenderer.send(apiName.symphonyApi, {
|
local.ipcRenderer.send(apiName.symphonyApi, {
|
||||||
@ -83,14 +88,27 @@ export class SSFApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows JS to register a callback to be invoked when size/positions
|
||||||
|
* changes for any pop-out window (i.e., window.open). The main
|
||||||
|
* process will emit IPC event 'boundsChange' (see below). Currently
|
||||||
|
* only one window can register for bounds change.
|
||||||
|
* @param {Function} callback Function invoked when bounds changes.
|
||||||
|
*/
|
||||||
|
public registerBoundsChange(callback: () => void): void {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
local.boundsChangeCallback = callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow user to capture portion of screen
|
* Allow user to capture portion of screen
|
||||||
*
|
*
|
||||||
* @param screenSnippet {function}
|
* @param screenSnippetCallback {function}
|
||||||
*/
|
*/
|
||||||
public openScreenSnippet(screenSnippet: Partial<IScreenSnippet>): void {
|
public openScreenSnippet(screenSnippetCallback: Partial<IScreenSnippet>): void {
|
||||||
if (typeof screenSnippet === 'function') {
|
if (typeof screenSnippetCallback === 'function') {
|
||||||
local.screenSnippet = screenSnippet;
|
local.screenSnippetCallback = screenSnippetCallback;
|
||||||
|
|
||||||
local.ipcRenderer.send(apiName.symphonyApi, {
|
local.ipcRenderer.send(apiName.symphonyApi, {
|
||||||
cmd: apiCmds.openScreenSnippet,
|
cmd: apiCmds.openScreenSnippet,
|
||||||
@ -130,7 +148,7 @@ export class SSFApi {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Creates a data url
|
// Creates a data url
|
||||||
ipcRenderer.on('create-badge-data-url', (_event: Event, arg: IBadgeCount) => {
|
local.ipcRenderer.on('create-badge-data-url', (_event: Event, arg: IBadgeCount) => {
|
||||||
const count = arg && arg.count || 0;
|
const count = arg && arg.count || 0;
|
||||||
|
|
||||||
// create 32 x 32 img
|
// create 32 x 32 img
|
||||||
@ -169,28 +187,80 @@ ipcRenderer.on('create-badge-data-url', (_event: Event, arg: IBadgeCount) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('screen-snippet-data', (_event: Event, arg: IScreenSnippet) => {
|
local.ipcRenderer.on('screen-snippet-data', (_event: Event, arg: IScreenSnippet) => {
|
||||||
if (typeof arg === 'object' && typeof local.screenSnippet === 'function') {
|
if (typeof arg === 'object' && typeof local.screenSnippetCallback === 'function') {
|
||||||
local.screenSnippet(arg);
|
local.screenSnippetCallback(arg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('activity', (_event: Event, arg: IActivityDetection) => {
|
local.ipcRenderer.on('activity', (_event: Event, arg: IActivityDetection) => {
|
||||||
if (typeof arg === 'object' && typeof local.activityDetection === 'function') {
|
if (typeof arg === 'object' && typeof local.activityDetectionCallback === 'function') {
|
||||||
local.activityDetection(arg);
|
local.activityDetectionCallback(arg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// listen for notifications that some window size/position has changed
|
||||||
|
local.ipcRenderer.on('boundsChange', (_event, arg: IBoundsChange): void => {
|
||||||
|
const { x, y, height, width, windowName } = arg;
|
||||||
|
if (x && y && height && width && windowName && typeof local.boundsChangeCallback === 'function') {
|
||||||
|
local.boundsChangeCallback({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
windowName,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Invoked whenever the app is reloaded/navigated
|
// Invoked whenever the app is reloaded/navigated
|
||||||
const sanitize = (): void => {
|
const sanitize = (): void => {
|
||||||
local.ipcRenderer.send(apiName, {
|
local.ipcRenderer.send(apiName.symphonyApi, {
|
||||||
cmd: apiCmds.sanitize,
|
cmd: apiCmds.sanitize,
|
||||||
windowName: window.name || 'main',
|
windowName: window.name || 'main',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// listens for the online/offline events and updates the main process
|
||||||
|
const updateOnlineStatus = (): void => {
|
||||||
|
local.ipcRenderer.send(apiName.symphonyApi, {
|
||||||
|
cmd: apiCmds.isOnline,
|
||||||
|
isOnline: window.navigator.onLine,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle key down events
|
||||||
|
const throttledKeyDown = throttle( (event) => {
|
||||||
|
isAltKey = event.keyCode === KeyCodes.Alt;
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Handle key up events
|
||||||
|
const throttledKeyUp = throttle( (event) => {
|
||||||
|
if (isAltKey && (event.keyCode === KeyCodes.Alt || KeyCodes.Esc)) {
|
||||||
|
isMenuOpen = !isMenuOpen;
|
||||||
|
}
|
||||||
|
if (isAltKey && isMenuOpen && event.keyCode === KeyCodes.Alt) {
|
||||||
|
local.ipcRenderer.send(apiName.symphonyApi, {
|
||||||
|
cmd: apiCmds.keyPress,
|
||||||
|
keyCode: event.keyCode,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Handle mouse down event
|
||||||
|
const throttleMouseDown = throttle(() => {
|
||||||
|
if (isAltKey && isMenuOpen) {
|
||||||
|
isMenuOpen = !isMenuOpen;
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Window Events
|
* Window Events
|
||||||
*/
|
*/
|
||||||
|
|
||||||
window.addEventListener('beforeunload', sanitize, false);
|
window.addEventListener('beforeunload', sanitize, false);
|
||||||
|
window.addEventListener('offline', updateOnlineStatus, false);
|
||||||
|
window.addEventListener('online', updateOnlineStatus, false);
|
||||||
|
window.addEventListener('keyup', throttledKeyUp, true);
|
||||||
|
window.addEventListener('keydown', throttledKeyDown, true);
|
||||||
|
window.addEventListener('mousedown', throttleMouseDown, { capture: true });
|
Loading…
Reference in New Issue
Block a user