返回 DeepSeek-Reasonix
windowGeometry.native.ts
根目录 / desktop / electron / src / main / windowGeometry.native.ts
1 // Runs in real Electron via scripts/window-geometry-smoke.mjs.
2 import assert from "node:assert/strict";
3 import type { EventEmitter } from "node:events";
4 import { join } from "node:path";
5 import { app, screen, type BrowserWindow } from "electron";
6 import { MainWindow } from "./window.js";
7 import { AppZoomStore } from "./zoomStore.js";
8 import { restoreWindowRect, type WindowRect } from "./windowBounds.js";
9 import type { HelloWindow } from "./handshake.js";
10
11 function near(actual: WindowRect, expected: WindowRect): void {
12 for (const key of ["x", "y", "width", "height"] as const) {
13 assert.ok(Math.abs(actual[key] - expected[key]) <= 1, `${key}: ${actual[key]} != ${expected[key]}`);
14 }
15 }
16
17 async function transition(win: BrowserWindow, event: "maximize" | "minimize" | "restore" | "unmaximize", action: () => void): Promise<void> {
18 await new Promise<void>((resolve, reject) => {
19 const timer = setTimeout(() => reject(new Error(`no ${event} event: ${JSON.stringify({ bounds: win.getBounds(), maximized: win.isMaximized(), minimized: win.isMinimized(), visible: win.isVisible() })}`)), 10_000);
20 (win as EventEmitter).once(event, () => { clearTimeout(timer); resolve(); });
21 action();
22 });
23 }
24
25 async function run(): Promise<void> {
26 await app.whenReady();
27 const workArea = screen.getPrimaryDisplay().workArea;
28 const geometry: HelloWindow = {
29 width: Math.min(1000, workArea.width - 100), height: Math.min(650, workArea.height - 100),
30 minWidth: 400, minHeight: 300, frameless: process.platform === "win32", zoomFactor: 1,
31 position: { x: workArea.x + 40, y: workArea.y + 40 },
32 };
33 function create(input: HelloWindow): MainWindow {
34 const main = new MainWindow({
35 preloadPath: join(app.getPath("userData"), "unused-preload.cjs"),
36 appURL: "https://example.invalid", platform: process.platform,
37 log: console, zoomStore: new AppZoomStore(join(app.getPath("userData"), "zoom.json")),
38 onAppDomReady() {}, onCloseRequested: async () => {}, onShellAction() {},
39 });
40 main.create(input);
41 return main;
42 }
43 const main = create(geometry);
44 const win = main.browserWindow!;
45 const expected = restoreWindowRect(geometry, geometry.position, workArea);
46 near(win.getBounds(), expected);
47 assert.equal(win.isVisible(), false, "restore geometry must be applied before show");
48 await win.loadURL("about:blank");
49 win.show();
50 await transition(win, "maximize", () => main.maximise());
51 // Do not poll bounds between maximise and minimise: native event capture
52 // must retain the restore geometry even before the renderer's first save.
53 await transition(win, "minimize", () => main.minimise());
54 near(main.bounds(), expected);
55 assert.equal(main.bounds().maximised, true, "minimise must preserve maximised intent");
56 await transition(win, "restore", () => main.unminimise());
57 await transition(win, "unmaximize", () => main.unmaximise());
58 near(win.getBounds(), expected);
59 assert.equal(main.bounds().maximised, false);
60 await transition(win, "maximize", () => main.maximise());
61 const saved = main.bounds();
62 win.destroy();
63 const relaunched = create({ ...geometry, width: saved.width, height: saved.height, position: { x: saved.x, y: saved.y } });
64 near(relaunched.bounds(), expected);
65 relaunched.browserWindow!.destroy();
66 const legacy = create({ ...geometry, width: workArea.width + 16, height: workArea.height + 16,
67 position: { x: workArea.x - 8, y: workArea.y - 8 } });
68 near(legacy.browserWindow!.getBounds(), workArea);
69 legacy.browserWindow!.destroy();
70 console.log("PASS native geometry: custom restore, maximise/minimise, relaunch, legacy oversized frame");
71 }
72
73 // Avoid quitting between the individual window cases.
74 app.on("window-all-closed", () => {});
75 void run().then(() => app.exit(0), (error: unknown) => { console.error(error); app.exit(1); });
76
76 lines TYPESCRIPT