Skip to content

Commit 9bd3fa2

Browse files
authored
Launch child windows in the center of the parent window (#1067)
* Launch child windows in the center of the parent window * Address copilot review comments
1 parent 2ad4083 commit 9bd3fa2

8 files changed

Lines changed: 164 additions & 32 deletions

File tree

gcs/electron/modules/aboutWindow.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { BrowserWindow, ipcMain, shell } from "electron"
22
import path from "path"
3+
import { getCenteredWindowPosition } from "../utils/windowUtils"
34

45
let aboutPopoutWin: BrowserWindow | null = null
56

67
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]
78

8-
export function openAboutPopout() {
9+
export function openAboutPopout(parentWindow?: BrowserWindow) {
910
if (aboutPopoutWin === null) {
10-
aboutPopoutWin = new BrowserWindow({
11+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
1112
width: 400,
1213
height: 300,
1314
frame: true,
@@ -20,7 +21,20 @@ export function openAboutPopout() {
2021
},
2122
fullscreen: false,
2223
fullscreenable: false,
23-
})
24+
}
25+
26+
// Position window in the center of the parent window
27+
const centeredPosition = getCenteredWindowPosition(
28+
parentWindow,
29+
windowOptions.width!,
30+
windowOptions.height!,
31+
)
32+
if (centeredPosition) {
33+
windowOptions.x = centeredPosition.x
34+
windowOptions.y = centeredPosition.y
35+
}
36+
37+
aboutPopoutWin = new BrowserWindow(windowOptions)
2438
}
2539

2640
if (VITE_DEV_SERVER_URL) {
@@ -57,8 +71,9 @@ export default function registerAboutIPC() {
5771
ipcMain.removeHandler("app:open-about-window")
5872
ipcMain.removeHandler("app:close-about-window")
5973

60-
ipcMain.handle("app:open-about-window", () => {
61-
openAboutPopout()
74+
ipcMain.handle("app:open-about-window", (event) => {
75+
const parentWindow = BrowserWindow.fromWebContents(event.sender)
76+
openAboutPopout(parentWindow || undefined)
6277
})
6378
ipcMain.handle("app:close-about-window", () => closeAboutPopout())
6479
}

gcs/electron/modules/ekfStatusWindow.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { BrowserWindow, ipcMain } from "electron"
22
import path from "path"
3+
import { getCenteredWindowPosition } from "../utils/windowUtils"
34

45
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]
56

67
let ekfStatusWin: BrowserWindow | null = null
78

8-
export function openEkfStatusWindow() {
9+
export function openEkfStatusWindow(parentWindow?: BrowserWindow) {
910
if (ekfStatusWin === null) {
10-
ekfStatusWin = new BrowserWindow({
11+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
1112
width: 700,
1213
height: 400,
1314
frame: true,
@@ -21,7 +22,20 @@ export function openEkfStatusWindow() {
2122
fullscreen: false,
2223
fullscreenable: false,
2324
alwaysOnTop: true,
24-
})
25+
}
26+
27+
// Position window in the center of the parent window
28+
const centeredPosition = getCenteredWindowPosition(
29+
parentWindow,
30+
windowOptions.width!,
31+
windowOptions.height!,
32+
)
33+
if (centeredPosition) {
34+
windowOptions.x = centeredPosition.x
35+
windowOptions.y = centeredPosition.y
36+
}
37+
38+
ekfStatusWin = new BrowserWindow(windowOptions)
2539
}
2640

2741
if (VITE_DEV_SERVER_URL) {
@@ -51,8 +65,9 @@ export default function registerEkfStatusIPC() {
5165
ipcMain.removeHandler("app:close-ekf-status-window")
5266
ipcMain.removeHandler("app:update-ekf-status")
5367

54-
ipcMain.handle("app:open-ekf-status-window", () => {
55-
openEkfStatusWindow()
68+
ipcMain.handle("app:open-ekf-status-window", (event) => {
69+
const parentWindow = BrowserWindow.fromWebContents(event.sender)
70+
openEkfStatusWindow(parentWindow || undefined)
5671
})
5772
ipcMain.handle("app:close-ekf-status-window", () => closeEkfStatusWindow())
5873
ipcMain.handle("app:update-ekf-status", (_, ekfStatusData) => {

gcs/electron/modules/flaParamsWindow.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BrowserWindow, ipcMain } from "electron"
22
import path from "path"
33
import { ParamObject } from "../types/flaTypes"
4+
import { getCenteredWindowPosition } from "../utils/windowUtils"
45

56
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]
67

@@ -9,12 +10,13 @@ let flaParamsWin: BrowserWindow | null = null
910
export function openFlaParamsWindow(
1011
paramsData: ParamObject[] | null,
1112
fileName: string | null,
13+
parentWindow?: BrowserWindow,
1214
) {
1315
if (flaParamsWin !== null) {
1416
destroyFlaParamsWindow()
1517
}
1618

17-
flaParamsWin = new BrowserWindow({
19+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
1820
width: 800,
1921
height: 1200,
2022
frame: true,
@@ -27,7 +29,20 @@ export function openFlaParamsWindow(
2729
},
2830
fullscreen: false,
2931
fullscreenable: false,
30-
})
32+
}
33+
34+
// Position window in the center of the parent window
35+
const centeredPosition = getCenteredWindowPosition(
36+
parentWindow,
37+
windowOptions.width!,
38+
windowOptions.height!,
39+
)
40+
if (centeredPosition) {
41+
windowOptions.x = centeredPosition.x
42+
windowOptions.y = centeredPosition.y
43+
}
44+
45+
flaParamsWin = new BrowserWindow(windowOptions)
3146

3247
if (VITE_DEV_SERVER_URL) {
3348
flaParamsWin?.loadURL(VITE_DEV_SERVER_URL + "flaParams.html")
@@ -64,8 +79,9 @@ export default function registerFlaParamsIPC() {
6479
ipcMain.removeHandler("app:open-fla-params-window")
6580
ipcMain.removeHandler("app:close-fla-params-window")
6681

67-
ipcMain.handle("app:open-fla-params-window", (_, data) => {
68-
openFlaParamsWindow(data.params, data.fileName)
82+
ipcMain.handle("app:open-fla-params-window", (event, data) => {
83+
const parentWindow = BrowserWindow.fromWebContents(event.sender)
84+
openFlaParamsWindow(data.params, data.fileName, parentWindow || undefined)
6985
})
7086
ipcMain.handle("app:close-fla-params-window", () => closeFlaParamsWindow())
7187
}

gcs/electron/modules/graphWindow.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BrowserWindow, ipcMain } from "electron"
22
import path from "path"
3+
import { getCenteredWindowPosition } from "../utils/windowUtils"
34

45
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]
56

@@ -74,15 +75,18 @@ function sendInit(graphKey: GraphKey) {
7475
}
7576
}
7677

77-
export function openGraphWindow({ graphKey, meta }: OpenArgs) {
78+
export function openGraphWindow(
79+
{ graphKey, meta }: OpenArgs,
80+
parentWindow?: BrowserWindow,
81+
) {
7882
// Always cache latest meta for this slot (used by ready-handshake)
7983
lastMeta[graphKey] = meta
8084

8185
// Reuse existing window if it's alive
8286
let win = getGraphWin(graphKey)
8387

8488
if (!win) {
85-
win = new BrowserWindow({
89+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
8690
width: 700,
8791
height: 350,
8892
frame: true,
@@ -96,7 +100,20 @@ export function openGraphWindow({ graphKey, meta }: OpenArgs) {
96100
fullscreen: false,
97101
fullscreenable: false,
98102
alwaysOnTop: true,
99-
})
103+
}
104+
105+
// Position window in the center of the parent window
106+
const centeredPosition = getCenteredWindowPosition(
107+
parentWindow,
108+
windowOptions.width!,
109+
windowOptions.height!,
110+
)
111+
if (centeredPosition) {
112+
windowOptions.x = centeredPosition.x
113+
windowOptions.y = centeredPosition.y
114+
}
115+
116+
win = new BrowserWindow(windowOptions)
100117

101118
graphWins[graphKey] = win
102119
win.setMenuBarVisibility(false)
@@ -130,7 +147,7 @@ export function openGraphWindow({ graphKey, meta }: OpenArgs) {
130147
} catch (e) {
131148
// If it threw, treat it as dead and try again once
132149
graphWins[graphKey] = undefined
133-
return openGraphWindow({ graphKey, meta })
150+
return openGraphWindow({ graphKey, meta }, parentWindow)
134151
}
135152
}
136153

@@ -180,8 +197,9 @@ export default function registerGraphWindowIPC(appWin?: BrowserWindow) {
180197
sendInit(graphKey)
181198
})
182199

183-
ipcMain.handle("app:open-graph-window", (_event, args: OpenArgs) => {
184-
openGraphWindow(args)
200+
ipcMain.handle("app:open-graph-window", (event, args: OpenArgs) => {
201+
const parentWindow = BrowserWindow.fromWebContents(event.sender)
202+
openGraphWindow(args, parentWindow || undefined)
185203
})
186204

187205
ipcMain.handle("app:close-graph-window", (_event, args: CloseArgs) => {

gcs/electron/modules/linkStatsWindow.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { BrowserWindow, ipcMain } from "electron"
22
import path from "path"
3+
import { getCenteredWindowPosition } from "../utils/windowUtils"
34

45
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]
56

67
let linkStatsWin: BrowserWindow | null = null
78

8-
export function openLinkStatsWindow() {
9+
export function openLinkStatsWindow(parentWindow?: BrowserWindow) {
910
if (linkStatsWin === null) {
10-
linkStatsWin = new BrowserWindow({
11+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
1112
width: 500,
1213
height: 300,
1314
frame: true,
@@ -20,7 +21,20 @@ export function openLinkStatsWindow() {
2021
},
2122
fullscreen: false,
2223
fullscreenable: false,
23-
})
24+
}
25+
26+
// Position window in the center of the parent window
27+
const centeredPosition = getCenteredWindowPosition(
28+
parentWindow,
29+
windowOptions.width!,
30+
windowOptions.height!,
31+
)
32+
if (centeredPosition) {
33+
windowOptions.x = centeredPosition.x
34+
windowOptions.y = centeredPosition.y
35+
}
36+
37+
linkStatsWin = new BrowserWindow(windowOptions)
2438
}
2539

2640
if (VITE_DEV_SERVER_URL) {
@@ -50,8 +64,9 @@ export default function registerLinkStatsIPC() {
5064
ipcMain.removeHandler("app:close-link-stats-window")
5165
ipcMain.removeHandler("app:update-link-stats")
5266

53-
ipcMain.handle("app:open-link-stats-window", () => {
54-
openLinkStatsWindow()
67+
ipcMain.handle("app:open-link-stats-window", (event) => {
68+
const parentWindow = BrowserWindow.fromWebContents(event.sender)
69+
openLinkStatsWindow(parentWindow || undefined)
5570
})
5671
ipcMain.handle("app:close-link-stats-window", () => closeLinkStatsWindow())
5772
ipcMain.handle("app:update-link-stats", (_, linkStats) => {

gcs/electron/modules/vibeStatusWindow.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { BrowserWindow, ipcMain } from "electron"
22
import path from "path"
3+
import { getCenteredWindowPosition } from "../utils/windowUtils"
34

45
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]
56

67
let vibeStatusWin: BrowserWindow | null = null
78

8-
export function openVibeStatusWindow() {
9+
export function openVibeStatusWindow(parentWindow?: BrowserWindow) {
910
if (vibeStatusWin === null) {
10-
vibeStatusWin = new BrowserWindow({
11+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
1112
width: 600,
1213
height: 250,
1314
frame: true,
@@ -21,7 +22,20 @@ export function openVibeStatusWindow() {
2122
fullscreen: false,
2223
fullscreenable: false,
2324
alwaysOnTop: true,
24-
})
25+
}
26+
27+
// Position window in the center of the parent window
28+
const centeredPosition = getCenteredWindowPosition(
29+
parentWindow,
30+
windowOptions.width!,
31+
windowOptions.height!,
32+
)
33+
if (centeredPosition) {
34+
windowOptions.x = centeredPosition.x
35+
windowOptions.y = centeredPosition.y
36+
}
37+
38+
vibeStatusWin = new BrowserWindow(windowOptions)
2539
}
2640

2741
if (VITE_DEV_SERVER_URL) {
@@ -51,8 +65,9 @@ export default function registerVibeStatusIPC() {
5165
ipcMain.removeHandler("app:close-vibe-status-window")
5266
ipcMain.removeHandler("app:update-vibe-status")
5367

54-
ipcMain.handle("app:open-vibe-status-window", () => {
55-
openVibeStatusWindow()
68+
ipcMain.handle("app:open-vibe-status-window", (event) => {
69+
const parentWindow = BrowserWindow.fromWebContents(event.sender)
70+
openVibeStatusWindow(parentWindow || undefined)
5671
})
5772
ipcMain.handle("app:close-vibe-status-window", () => closeVibeStatusWindow())
5873
ipcMain.handle("app:update-vibe-status", (_, vibeStatusData) => {

gcs/electron/modules/videoWindow.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BrowserWindow, ipcMain } from "electron"
22
import path from "path"
3+
import { getCenteredWindowPosition } from "../utils/windowUtils"
34

45
let videoPopoutWin: BrowserWindow | null = null
56

@@ -42,7 +43,7 @@ export function openVideoPopout(
4243
mainWindow: BrowserWindow | null = null,
4344
) {
4445
if (videoPopoutWin === null) {
45-
videoPopoutWin = new BrowserWindow({
46+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
4647
width: 400,
4748
height: 300,
4849
frame: false,
@@ -57,7 +58,20 @@ export function openVideoPopout(
5758
fullscreen: false,
5859
fullscreenable: false,
5960
alwaysOnTop: true,
60-
})
61+
}
62+
63+
// Position window in the center of the parent window
64+
const centeredPosition = getCenteredWindowPosition(
65+
mainWindow || undefined,
66+
windowOptions.width!,
67+
windowOptions.height!,
68+
)
69+
if (centeredPosition) {
70+
windowOptions.x = centeredPosition.x
71+
windowOptions.y = centeredPosition.y
72+
}
73+
74+
videoPopoutWin = new BrowserWindow(windowOptions)
6175
}
6276

6377
loadVideo(type, videoStreamId, name, streamUrl)

gcs/electron/utils/windowUtils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { BrowserWindow } from "electron"
2+
3+
// Get the position for a new window centered on the parent window
4+
export function getCenteredWindowPosition(
5+
parentWindow: BrowserWindow | undefined,
6+
windowWidth: number,
7+
windowHeight: number,
8+
): { x: number; y: number } | undefined {
9+
if (!parentWindow) {
10+
const focusedWindow = BrowserWindow.getFocusedWindow()
11+
if (focusedWindow) {
12+
parentWindow = focusedWindow
13+
} else {
14+
return undefined
15+
}
16+
}
17+
18+
const parentBounds = parentWindow.getBounds()
19+
20+
return {
21+
x: parentBounds.x + Math.floor((parentBounds.width - windowWidth) / 2),
22+
y: parentBounds.y + Math.floor((parentBounds.height - windowHeight) / 2),
23+
}
24+
}

0 commit comments

Comments
 (0)