diff --git a/docs/stack.md b/docs/stack.md index 5243833..0064105 100644 --- a/docs/stack.md +++ b/docs/stack.md @@ -118,6 +118,10 @@ A few quality-of-life utilities for working in Electron. Persist JSON to the file system. +> **electron-window-state-manager** + +Persist window coordinates & dimensions to disk to survive restarts. + ## Bundler diff --git a/package-lock.json b/package-lock.json index d61244b..a3c03f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1103,8 +1103,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base64-img": { "version": "1.0.3", @@ -1192,7 +1191,6 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -1553,8 +1551,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.0", @@ -2594,6 +2591,22 @@ } } }, + "electron-window-state-manager": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/electron-window-state-manager/-/electron-window-state-manager-0.3.2.tgz", + "integrity": "sha1-pVLqNUcCl8mZx0TYGrsqjIpRdw4=", + "requires": { + "app-root-path": "1.4.0", + "fs-jetpack": "0.9.2" + }, + "dependencies": { + "app-root-path": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-1.4.0.tgz", + "integrity": "sha1-YzXYZclkDQ+tmQBOWnkjIjjpLfo=" + } + } + }, "elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", @@ -3748,11 +3761,21 @@ "fs-extra": "4.0.1" } }, + "fs-jetpack": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-0.9.2.tgz", + "integrity": "sha1-k7QU29B4AYY9ZMgadq3APYUMm1Y=", + "requires": { + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "q": "1.5.0", + "rimraf": "2.6.1" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.1.2", @@ -4800,7 +4823,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -5116,7 +5138,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "1.4.0", "wrappy": "1.0.2" @@ -5125,8 +5146,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.4", @@ -6492,7 +6512,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "1.1.8" } @@ -6500,14 +6519,12 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" } @@ -6867,7 +6884,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1.0.2" } @@ -7246,8 +7262,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -7588,6 +7603,11 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, + "q": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", + "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=" + }, "qs": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", @@ -8075,7 +8095,6 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "dev": true, "requires": { "glob": "7.1.2" } @@ -9409,8 +9428,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "0.2.1", diff --git a/package.json b/package.json index 5622639..71228e2 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "electron-log": "2.2.7", "electron-store": "1.2.0", "electron-updater": "2.8.2", + "electron-window-state-manager": "0.3.2", "glamor": "2.20.39", "mobx": "3.2.2", "mobx-react": "4.2.2", diff --git a/src/main/main-window/main-window.ts b/src/main/main-window/main-window.ts index eb6df50..a333856 100644 --- a/src/main/main-window/main-window.ts +++ b/src/main/main-window/main-window.ts @@ -1,6 +1,7 @@ import { app, BrowserWindow } from 'electron' import { join } from 'path' import { format } from 'url' +import WindowStateManager = require('electron-window-state-manager') // default dimensions const DIMENSIONS = { width: 600, height: 500, minWidth: 450, minHeight: 450 } @@ -12,8 +13,20 @@ const DIMENSIONS = { width: 600, height: 500, minWidth: 450, minHeight: 450 } * @return The main BrowserWindow. */ export function createMainWindow(appPath: string) { + // persistent window state manager + const windowState = new WindowStateManager('main', { + defaultWidth: DIMENSIONS.width, + defaultHeight: DIMENSIONS.height, + }) + + // create our main window const window = new BrowserWindow({ - ...DIMENSIONS, + minWidth: DIMENSIONS.minWidth, + minHeight: DIMENSIONS.minHeight, + width: windowState.width, + height: windowState.height, + x: windowState.x, + y: windowState.y, show: false, useContentSize: true, titleBarStyle: 'hidden', @@ -22,6 +35,16 @@ export function createMainWindow(appPath: string) { title: app.getName(), }) + // maximize if we did before + if (windowState.maximized) { + window.maximize() + } + + // trap movement events + window.on('close', () => windowState.saveState(window)) + window.on('move', () => windowState.saveState(window)) + window.on('resize', () => windowState.saveState(window)) + // load entry html page in the renderer. window.loadURL( format({ @@ -33,8 +56,10 @@ export function createMainWindow(appPath: string) { // only appear once we've loaded window.webContents.on('did-finish-load', () => { - window.show() - window.focus() + setTimeout(() => { + window.show() + window.focus() + }, 100) }) return window diff --git a/types/electron-window-state-manager.d.ts b/types/electron-window-state-manager.d.ts new file mode 100644 index 0000000..39f1f92 --- /dev/null +++ b/types/electron-window-state-manager.d.ts @@ -0,0 +1,35 @@ +declare module 'electron-window-state-manager' { + interface ElectronStateManagerOptions { + /** The default width for this window. */ + defaultWidth?: number + /** The default height for this window. */ + defaultHeight?: number + } + + /** + * Manages an Electron.BrowserWindow's dimensions and stores it to disk. + */ + class ElectronStateManager { + /** + * Creates a manager that loads/saves a window's coordinates & dimensions. + * + * @param name The name of the window. + * @param options Some default options. + */ + constructor(name: string, options?: ElectronStateManagerOptions) + /** Save this window's dimensions */ + saveState(browserWindow: Electron.BrowserWindow): void + /** The window width. */ + width: number + /** The window height. */ + height: number + /** the window left position. */ + x: number + /** The window right position. */ + y: number + /** Is this maximized? */ + maximized: boolean + } + + export = ElectronStateManager +}