mirror of
https://github.com/YuzuZensai/VRC-Circle.git
synced 2026-09-13 10:58:59 +00:00
✨ feat: Persist main window size, position, and maximization
This commit is contained in:
@@ -108,7 +108,9 @@ const handlers = {
|
|||||||
"debug:repoFlush": (name) => guard(async () => debug.repoFlush(name)),
|
"debug:repoFlush": (name) => guard(async () => debug.repoFlush(name)),
|
||||||
"debug:openWindow": () => guard(async () => openDebugWindow()),
|
"debug:openWindow": () => guard(async () => openDebugWindow()),
|
||||||
} satisfies {
|
} satisfies {
|
||||||
[C in IpcRequestChannel]: (...args: Parameters<IpcRequests[C]>) => Promise<ReturnType<IpcRequests[C]>>;
|
[C in IpcRequestChannel]: (
|
||||||
|
...args: Parameters<IpcRequests[C]>
|
||||||
|
) => Promise<ReturnType<IpcRequests[C]>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function registerIpcHandlers(): void {
|
export function registerIpcHandlers(): void {
|
||||||
@@ -121,7 +123,5 @@ function register<C extends IpcRequestChannel>(channel: C): void {
|
|||||||
const handler = handlers[channel] as (
|
const handler = handlers[channel] as (
|
||||||
...args: Parameters<IpcRequests[C]>
|
...args: Parameters<IpcRequests[C]>
|
||||||
) => Promise<ReturnType<IpcRequests[C]>>;
|
) => Promise<ReturnType<IpcRequests[C]>>;
|
||||||
ipcMain.handle(channel, (_event, ...args) =>
|
ipcMain.handle(channel, (_event, ...args) => handler(...(args as Parameters<IpcRequests[C]>)));
|
||||||
handler(...(args as Parameters<IpcRequests[C]>)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { app, screen, type BrowserWindow, type Rectangle } from "electron";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { writeFileAtomicSync } from "./lib/atomicFile";
|
||||||
|
|
||||||
|
type Bounds = Rectangle & { maximized: boolean };
|
||||||
|
|
||||||
|
const path = () => join(app.getPath("userData"), "window-state.json");
|
||||||
|
|
||||||
|
function read(): Partial<Bounds> | null {
|
||||||
|
try {
|
||||||
|
return JSON.parse(readFileSync(path(), "utf8")) as Partial<Bounds>;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScreen(b: Rectangle): boolean {
|
||||||
|
return screen.getAllDisplays().some((d) => {
|
||||||
|
const w = d.workArea;
|
||||||
|
return (
|
||||||
|
b.x < w.x + w.width && b.x + b.width > w.x && b.y < w.y + w.height && b.y + b.height > w.y
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreBounds(): { bounds?: Rectangle; maximized: boolean } {
|
||||||
|
const saved = read();
|
||||||
|
if (
|
||||||
|
saved &&
|
||||||
|
saved.width &&
|
||||||
|
saved.height &&
|
||||||
|
saved.x != null &&
|
||||||
|
saved.y != null &&
|
||||||
|
onScreen({ x: saved.x, y: saved.y, width: saved.width, height: saved.height })
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
bounds: { x: saved.x, y: saved.y, width: saved.width, height: saved.height },
|
||||||
|
maximized: saved.maximized ?? false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { maximized: saved?.maximized ?? false };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function trackBounds(win: BrowserWindow): void {
|
||||||
|
let normalBounds = win.getBounds();
|
||||||
|
|
||||||
|
const save = (): void => {
|
||||||
|
if (win.isDestroyed()) return;
|
||||||
|
writeFileAtomicSync(
|
||||||
|
path(),
|
||||||
|
JSON.stringify({ ...normalBounds, maximized: win.isMaximized() }, null, 2),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const remember = (): void => {
|
||||||
|
if (!win.isMaximized() && !win.isMinimized() && !win.isFullScreen()) {
|
||||||
|
normalBounds = win.getBounds();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
win.on("resize", remember);
|
||||||
|
win.on("move", remember);
|
||||||
|
win.on("close", save);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { join, dirname } from "node:path";
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import type { IpcEventChannel, IpcEvents } from "../shared/ipc";
|
import type { IpcEventChannel, IpcEvents } from "../shared/ipc";
|
||||||
import { TRAFFIC_LIGHT_INSET } from "../shared/window";
|
import { TRAFFIC_LIGHT_INSET } from "../shared/window";
|
||||||
|
import { restoreBounds, trackBounds } from "./windowState";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const isDev = !app.isPackaged;
|
const isDev = !app.isPackaged;
|
||||||
@@ -25,9 +26,11 @@ function load(win: BrowserWindow, hash = ""): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createMainWindow(): BrowserWindow {
|
export function createMainWindow(): BrowserWindow {
|
||||||
|
const restored = restoreBounds();
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 1180,
|
width: 1180,
|
||||||
height: 760,
|
height: 760,
|
||||||
|
...restored.bounds,
|
||||||
minWidth: 940,
|
minWidth: 940,
|
||||||
minHeight: 600,
|
minHeight: 600,
|
||||||
show: false,
|
show: false,
|
||||||
@@ -43,6 +46,9 @@ export function createMainWindow(): BrowserWindow {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (restored.maximized) mainWindow.maximize();
|
||||||
|
trackBounds(mainWindow);
|
||||||
|
|
||||||
mainWindow.once("ready-to-show", () => mainWindow?.show());
|
mainWindow.once("ready-to-show", () => mainWindow?.show());
|
||||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||||
void shell.openExternal(url);
|
void shell.openExternal(url);
|
||||||
|
|||||||
Reference in New Issue
Block a user