'use strict';
|
|
const path = require('path');
|
const electron = require('electron');
|
const jsonfile = require('jsonfile');
|
const mkdirp = require('mkdirp');
|
|
module.exports = function (options) {
|
const app = electron.app || electron.remote.app;
|
const screen = electron.screen || electron.remote.screen;
|
let state;
|
let winRef;
|
let stateChangeTimer;
|
const eventHandlingDelay = 100;
|
const config = Object.assign({
|
file: 'window-state.json',
|
path: app.getPath('userData'),
|
maximize: true,
|
fullScreen: true
|
}, options);
|
const fullStoreFileName = path.join(config.path, config.file);
|
|
function isNormal(win) {
|
return !win.isMaximized() && !win.isMinimized() && !win.isFullScreen();
|
}
|
|
function hasBounds() {
|
return state &&
|
Number.isInteger(state.x) &&
|
Number.isInteger(state.y) &&
|
Number.isInteger(state.width) && state.width > 0 &&
|
Number.isInteger(state.height) && state.height > 0;
|
}
|
|
function resetStateToDefault() {
|
const displayBounds = screen.getPrimaryDisplay().bounds;
|
|
// Reset state to default values on the primary display
|
state = {
|
width: config.defaultWidth || 800,
|
height: config.defaultHeight || 600,
|
x: 0,
|
y: 0,
|
displayBounds
|
};
|
}
|
|
function windowWithinBounds(bounds) {
|
return (
|
state.x >= bounds.x &&
|
state.y >= bounds.y &&
|
state.x + state.width <= bounds.x + bounds.width &&
|
state.y + state.height <= bounds.y + bounds.height
|
);
|
}
|
|
function ensureWindowVisibleOnSomeDisplay() {
|
const visible = screen.getAllDisplays().some(display => {
|
return windowWithinBounds(display.bounds);
|
});
|
|
if (!visible) {
|
// Window is partially or fully not visible now.
|
// Reset it to safe defaults.
|
return resetStateToDefault();
|
}
|
}
|
|
function validateState() {
|
const isValid = state && (hasBounds() || state.isMaximized || state.isFullScreen);
|
if (!isValid) {
|
state = null;
|
return;
|
}
|
|
if (hasBounds() && state.displayBounds) {
|
ensureWindowVisibleOnSomeDisplay();
|
}
|
}
|
|
function updateState(win) {
|
win = win || winRef;
|
if (!win) {
|
return;
|
}
|
// Don't throw an error when window was closed
|
try {
|
const winBounds = win.getBounds();
|
if (isNormal(win)) {
|
state.x = winBounds.x;
|
state.y = winBounds.y;
|
state.width = winBounds.width;
|
state.height = winBounds.height;
|
}
|
state.isMaximized = win.isMaximized();
|
state.isFullScreen = win.isFullScreen();
|
state.displayBounds = screen.getDisplayMatching(winBounds).bounds;
|
} catch (err) {}
|
}
|
|
function saveState(win) {
|
// Update window state only if it was provided
|
if (win) {
|
updateState(win);
|
}
|
|
// Save state
|
try {
|
mkdirp.sync(path.dirname(fullStoreFileName));
|
jsonfile.writeFileSync(fullStoreFileName, state);
|
} catch (err) {
|
// Don't care
|
}
|
}
|
|
function stateChangeHandler() {
|
// Handles both 'resize' and 'move'
|
clearTimeout(stateChangeTimer);
|
stateChangeTimer = setTimeout(updateState, eventHandlingDelay);
|
}
|
|
function closeHandler() {
|
updateState();
|
}
|
|
function closedHandler() {
|
// Unregister listeners and save state
|
unmanage();
|
saveState();
|
}
|
|
function manage(win) {
|
if (config.maximize && state.isMaximized) {
|
win.maximize();
|
}
|
if (config.fullScreen && state.isFullScreen) {
|
win.setFullScreen(true);
|
}
|
win.on('resize', stateChangeHandler);
|
win.on('move', stateChangeHandler);
|
win.on('close', closeHandler);
|
win.on('closed', closedHandler);
|
winRef = win;
|
}
|
|
function unmanage() {
|
if (winRef) {
|
winRef.removeListener('resize', stateChangeHandler);
|
winRef.removeListener('move', stateChangeHandler);
|
clearTimeout(stateChangeTimer);
|
winRef.removeListener('close', closeHandler);
|
winRef.removeListener('closed', closedHandler);
|
winRef = null;
|
}
|
}
|
|
// Load previous state
|
try {
|
state = jsonfile.readFileSync(fullStoreFileName);
|
} catch (err) {
|
// Don't care
|
}
|
|
// Check state validity
|
validateState();
|
|
// Set state fallback values
|
state = Object.assign({
|
width: config.defaultWidth || 800,
|
height: config.defaultHeight || 600
|
}, state);
|
|
return {
|
get x() { return state.x; },
|
get y() { return state.y; },
|
get width() { return state.width; },
|
get height() { return state.height; },
|
get displayBounds() { return state.displayBounds; },
|
get isMaximized() { return state.isMaximized; },
|
get isFullScreen() { return state.isFullScreen; },
|
saveState,
|
unmanage,
|
manage,
|
resetStateToDefault
|
};
|
};
|