From eab52bf8c135a753a79fced1df8ddc0f6c857c3e Mon Sep 17 00:00:00 2001 From: Patrick FIc Date: Thu, 15 May 2025 15:19:15 -0700 Subject: [PATCH] Ensure window stays on screen after external monitor removed. --- package-lock.json | 4 +- package.json | 2 +- src/main/index.ts | 8 +- src/main/util/ensureWindowOnScreen.ts | 109 ++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 src/main/util/ensureWindowOnScreen.ts diff --git a/package-lock.json b/package-lock.json index 9161377..3fa7118 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bodyshop-desktop", - "version": "0.0.1-alpha.10", + "version": "0.0.1-alpha.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bodyshop-desktop", - "version": "0.0.1-alpha.10", + "version": "0.0.1-alpha.11", "hasInstallScript": true, "dependencies": { "@apollo/client": "^3.13.6", diff --git a/package.json b/package.json index f359133..2d947c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bodyshop-desktop", - "version": "0.0.1-alpha.11", + "version": "0.0.1-alpha.13", "description": "Shop Management System Partner", "main": "./out/main/index.js", "author": "Convenient Brands, LLC", diff --git a/src/main/index.ts b/src/main/index.ts index 2245380..5e00ecb 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -33,6 +33,7 @@ import { isKeepAliveTaskInstalled, setupKeepAliveTask, } from "./setup-keep-alive-task"; +import ensureWindowOnScreen from "./util/ensureWindowOnScreen"; Sentry.init({ dsn: "https://ba41d22656999a8c1fd63bcb7df98650@o492140.ingest.us.sentry.io/4509074139447296", @@ -56,11 +57,14 @@ function createWindow(): void { y: number | undefined; }; + // Validate window position is on screen + const { validX, validY } = ensureWindowOnScreen(x, y, width, height); + const mainWindow = new BrowserWindow({ width, height, - x, - y, + x: validX, + y: validY, show: false, // Start hidden, show later if not keep-alive minWidth: 600, minHeight: 400, diff --git a/src/main/util/ensureWindowOnScreen.ts b/src/main/util/ensureWindowOnScreen.ts new file mode 100644 index 0000000..2cd1f07 --- /dev/null +++ b/src/main/util/ensureWindowOnScreen.ts @@ -0,0 +1,109 @@ +import { screen } from "electron"; + +function ensureWindowOnScreen( + x: number | undefined, + y: number | undefined, + windowWidth: number, + windowHeight: number, +): { validX: number | undefined; validY: number | undefined } { + // If no coordinates stored, let Electron position window automatically + if (x === undefined || y === undefined) { + return { validX: undefined, validY: undefined }; + } + + const displays = screen.getAllDisplays(); + + // Minimum visible pixels required on each edge to be considered "visible enough" + const MIN_VISIBLE = 50; // Ensure at least 50px from each edge is visible + + // Try to find a display where the window would be almost fully visible + for (const display of displays) { + const { bounds } = display; + + // Check if window is mostly within this display + if ( + x + MIN_VISIBLE >= bounds.x && + x + windowWidth - MIN_VISIBLE <= bounds.x + bounds.width && + y + MIN_VISIBLE >= bounds.y && + y + windowHeight - MIN_VISIBLE <= bounds.y + bounds.height + ) { + // Window is adequately visible on this display + return { validX: x, validY: y }; + } + } + + // If window isn't adequately visible on any display, try to adjust it to fit the closest display + const closestDisplay = findClosestDisplay(displays, x, y); + const { bounds } = closestDisplay; + + // Adjust position to ensure window is fully on screen + let adjustedX = x; + let adjustedY = y; + + // Adjust horizontal position if needed + if (x < bounds.x) { + adjustedX = bounds.x; + } else if (x + windowWidth > bounds.x + bounds.width) { + adjustedX = bounds.x + bounds.width - windowWidth; + } + + // Adjust vertical position if needed + if (y < bounds.y) { + adjustedY = bounds.y; + } else if (y + windowHeight > bounds.y + bounds.height) { + adjustedY = bounds.y + bounds.height - windowHeight; + } + + // If adjustments keep window on screen, use adjusted position + if ( + adjustedX >= bounds.x && + adjustedX + windowWidth <= bounds.x + bounds.width && + adjustedY >= bounds.y && + adjustedY + windowHeight <= bounds.y + bounds.height + ) { + return { validX: adjustedX, validY: adjustedY }; + } + + // If all else fails, center on primary display + const primaryDisplay = screen.getPrimaryDisplay(); + const primaryBounds = primaryDisplay.bounds; + + return { + validX: Math.floor( + primaryBounds.x + (primaryBounds.width - windowWidth) / 2, + ), + validY: Math.floor( + primaryBounds.y + (primaryBounds.height - windowHeight) / 2, + ), + }; +} + +// Helper function to find the closest display to a point +function findClosestDisplay( + displays: Electron.Display[], + x: number, + y: number, +): Electron.Display { + let closestDisplay = displays[0]; + let shortestDistance = Number.MAX_VALUE; + + for (const display of displays) { + const { bounds } = display; + // Calculate distance to center of display + const displayCenterX = bounds.x + bounds.width / 2; + const displayCenterY = bounds.y + bounds.height / 2; + + const distance = Math.sqrt( + Math.pow(x - displayCenterX, 2) + Math.pow(y - displayCenterY, 2), + ); + + if (distance < shortestDistance) { + shortestDistance = distance; + closestDisplay = display; + } + } + + return closestDisplay; +} + +export default ensureWindowOnScreen;