import * as electron from "electron"; import * as fs from "fs-extra"; import * as path from "path"; /* We read/write to this file every time again because this file could be used by multiple processes */ const data_file: string = path.join((electron.app || electron.remote.app).getPath('userData'), "window-bounds.json"); import BrowserWindow = Electron.BrowserWindow; import Rectangle = Electron.Rectangle; let changedData: {[key: string]:Rectangle} = {}; let changedDataSaveTimeout: number; export async function save_changes() { clearTimeout(changedDataSaveTimeout); try { const data = (await fs.pathExists(data_file) ? await fs.readJson(data_file) : {}) || {}; Object.assign(data, changedData); await fs.ensureFile(data_file); await fs.writeJson(data_file, data); path_exists = true; changedData = {}; } catch(error) { console.warn("Failed to save window bounds: %o", error); } console.log("Window bounds have been successfully saved!"); } let path_exists = undefined; export async function get_last_bounds(key: string) : Promise { try { if(typeof(path_exists) === "undefined" ? !(path_exists = await fs.pathExists(data_file)) : !path_exists) throw "skip!"; const data = await fs.readJson(data_file) || {}; if(data[key]) return data[key]; } catch(error) { if(error !== "skip!") console.warn("Failed to load window bounds for %s: %o", key, error); } return { height: undefined, width: undefined, x: undefined, y: undefined } } export function startTrackWindowBounds(windowId: string, window: BrowserWindow) { const events = ['move', 'moved', 'resize']; const onWindowBoundsChanged = () => { changedData[windowId] = window.getBounds(); clearTimeout(changedDataSaveTimeout); changedDataSaveTimeout = setTimeout(save_changes, 1000); }; for(const event of events) window.on(event as any, onWindowBoundsChanged); window.on('closed', () => { for(const event of events) window.removeListener(event as any, onWindowBoundsChanged); }); } export async function loadWindowBounds(windowId: string, window: BrowserWindow, bounds?: Rectangle, options?: { applySize?: boolean; applyPosition?: boolean }) { const screen = electron.screen || electron.remote.screen; if(!bounds) { bounds = await get_last_bounds(windowId); } if(!options) { options = {}; } const original_bounds = window.getBounds(); if(typeof(options.applySize) !== "boolean" || options.applySize) { let height = bounds.height > 0 ? bounds.height : original_bounds.height; let width = bounds.width > 0 ? bounds.width : original_bounds.width; if(height != original_bounds.height || width != original_bounds.width) window.setSize(width, height, true); } if(typeof(options.applyPosition) !== "boolean" || options.applyPosition) { let x = typeof(bounds.x) === "number" ? bounds.x : original_bounds.x; let y = typeof(bounds.y) === "number" ? bounds.y : original_bounds.y; if(x != original_bounds.x || y != original_bounds.y) { const display = screen.getDisplayNearestPoint({ x: x, y: y }); if(display) { const bounds = display.workArea || display.bounds; let flag_invalid = false; flag_invalid = flag_invalid || bounds.x > x || (bounds.x + bounds.width) < x; flag_invalid = flag_invalid || bounds.y > x || (bounds.y + bounds.height) < y; if(!flag_invalid) { window.setPosition(x, y, true); console.log("Updating position for %s", windowId); } } } } }