147 lines
3.8 KiB
TypeScript
147 lines
3.8 KiB
TypeScript
import { BrowserWindow, Menu, screen } from "electron";
|
|
import path from "node:path";
|
|
|
|
export interface AppWindowManagerOptions {
|
|
preloadPath: string;
|
|
rendererUrl?: string;
|
|
rendererFilePath: string;
|
|
onMainReadyToShow?: () => void | Promise<void>;
|
|
}
|
|
|
|
export interface AppWindowManager {
|
|
createMainWindow: () => void;
|
|
focusMainWindow: () => { ok: boolean };
|
|
hasMainWindow: () => boolean;
|
|
getMainWindow: () => BrowserWindow | null;
|
|
sendScannerCommand: (command: unknown) => void;
|
|
createOverlayWindow: () => void;
|
|
hideOverlayWindow: () => { ok: boolean };
|
|
}
|
|
|
|
export function createAppWindowManager({
|
|
preloadPath,
|
|
rendererUrl,
|
|
rendererFilePath,
|
|
onMainReadyToShow,
|
|
}: AppWindowManagerOptions): AppWindowManager {
|
|
let mainWindow: BrowserWindow | null = null;
|
|
let overlayWindow: BrowserWindow | null = null;
|
|
|
|
function createMainWindow() {
|
|
Menu.setApplicationMenu(null);
|
|
|
|
mainWindow = new BrowserWindow({
|
|
width: 1320,
|
|
height: 860,
|
|
minWidth: 1120,
|
|
minHeight: 720,
|
|
backgroundColor: "#090711",
|
|
title: "Genshin Artifact Assistant",
|
|
show: false,
|
|
autoHideMenuBar: true,
|
|
webPreferences: {
|
|
preload: preloadPath,
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
},
|
|
});
|
|
|
|
mainWindow.setMenuBarVisibility(false);
|
|
mainWindow.on("closed", () => {
|
|
mainWindow = null;
|
|
});
|
|
mainWindow.once("ready-to-show", () => {
|
|
void onMainReadyToShow?.();
|
|
focusMainWindow();
|
|
});
|
|
mainWindow.webContents.once("did-finish-load", () => {
|
|
setTimeout(() => focusMainWindow(), 350);
|
|
});
|
|
|
|
if (rendererUrl) {
|
|
mainWindow.loadURL(rendererUrl);
|
|
} else {
|
|
mainWindow.loadFile(rendererFilePath);
|
|
}
|
|
}
|
|
|
|
function focusMainWindow() {
|
|
if (!mainWindow || mainWindow.isDestroyed()) return { ok: false };
|
|
|
|
if (mainWindow.isMinimized()) mainWindow.restore();
|
|
mainWindow.show();
|
|
// Genshin often keeps foreground focus after a scan click. Toggling
|
|
// always-on-top for one tick nudges Windows to surface the dashboard again
|
|
// without leaving it pinned above other apps.
|
|
mainWindow.setAlwaysOnTop(true, "screen-saver");
|
|
mainWindow.focus();
|
|
setTimeout(() => {
|
|
if (!mainWindow || mainWindow.isDestroyed()) return;
|
|
mainWindow.setAlwaysOnTop(false);
|
|
mainWindow.focus();
|
|
}, 250);
|
|
return { ok: true };
|
|
}
|
|
|
|
function sendScannerCommand(command: unknown) {
|
|
if (!mainWindow || mainWindow.isDestroyed()) return;
|
|
mainWindow.webContents.send("scanner:command", command);
|
|
}
|
|
|
|
function createOverlayWindow() {
|
|
if (overlayWindow) {
|
|
overlayWindow.show();
|
|
return;
|
|
}
|
|
|
|
const display = screen.getPrimaryDisplay();
|
|
overlayWindow = new BrowserWindow({
|
|
x: display.workArea.x,
|
|
y: display.workArea.y,
|
|
width: display.workArea.width,
|
|
height: display.workArea.height,
|
|
transparent: true,
|
|
frame: false,
|
|
alwaysOnTop: true,
|
|
skipTaskbar: true,
|
|
resizable: false,
|
|
focusable: false,
|
|
webPreferences: {
|
|
preload: preloadPath,
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
},
|
|
});
|
|
|
|
overlayWindow.setIgnoreMouseEvents(true, { forward: true });
|
|
|
|
if (rendererUrl) {
|
|
overlayWindow.loadURL(`${rendererUrl}?overlay=1`);
|
|
} else {
|
|
overlayWindow.loadFile(rendererFilePath, {
|
|
query: { overlay: "1" },
|
|
});
|
|
}
|
|
|
|
overlayWindow.on("closed", () => {
|
|
overlayWindow = null;
|
|
});
|
|
}
|
|
|
|
function hideOverlayWindow() {
|
|
if (!overlayWindow || overlayWindow.isDestroyed()) return { ok: false };
|
|
overlayWindow.close();
|
|
return { ok: true };
|
|
}
|
|
|
|
return {
|
|
createMainWindow,
|
|
focusMainWindow,
|
|
hasMainWindow: () => Boolean(mainWindow && !mainWindow.isDestroyed()),
|
|
getMainWindow: () => mainWindow,
|
|
sendScannerCommand,
|
|
createOverlayWindow,
|
|
hideOverlayWindow,
|
|
};
|
|
}
|