1
1
import { is } from '@electron-toolkit/utils' ;
2
2
import { app , BrowserWindow , globalShortcut , ipcMain , screen , session , shell } from 'electron' ;
3
3
import Store from 'electron-store' ;
4
+ import windowStateKeeper from 'electron-window-state' ;
4
5
import { join } from 'path' ;
5
6
6
7
const store = new Store ( ) ;
7
8
8
- // 保存主窗口的大小和位置
9
- let mainWindowState = {
10
- width : 1200 ,
11
- height : 780 ,
9
+ // 默认窗口尺寸
10
+ const DEFAULT_MAIN_WIDTH = 1200 ;
11
+ const DEFAULT_MAIN_HEIGHT = 780 ;
12
+ const DEFAULT_MINI_WIDTH = 340 ;
13
+ const DEFAULT_MINI_HEIGHT = 64 ;
14
+ const DEFAULT_MINI_EXPANDED_HEIGHT = 400 ;
15
+
16
+ // 保存主窗口引用,以便在 activate 事件中使用
17
+ let mainWindowInstance : BrowserWindow | null = null ;
18
+
19
+ // 保存迷你模式前的窗口状态
20
+ let preMiniModeState = {
21
+ width : DEFAULT_MAIN_WIDTH ,
22
+ height : DEFAULT_MAIN_HEIGHT ,
12
23
x : undefined as number | undefined ,
13
24
y : undefined as number | undefined ,
14
25
isMaximized : false
15
26
} ;
16
27
17
- // 保存主窗口引用,以便在 activate 事件中使用
18
- let mainWindowInstance : BrowserWindow | null = null ;
28
+ /**
29
+ * 计算适合当前缩放比的缩放因子
30
+ * @returns 适合当前系统的页面缩放因子
31
+ */
32
+ function calculateContentZoomFactor ( ) : number {
33
+ // 获取系统的缩放因子
34
+ const { scaleFactor } = screen . getPrimaryDisplay ( ) ;
35
+
36
+ // 缩放因子默认为1
37
+ let zoomFactor = 1 ;
38
+
39
+ // 只在高DPI情况下调整
40
+ if ( scaleFactor > 1 ) {
41
+ // 自定义逻辑来根据不同的缩放比例进行调整
42
+ if ( scaleFactor >= 2.5 ) {
43
+ // 极高缩放比,例如4K屏幕用200%+缩放
44
+ zoomFactor = 0.7 ;
45
+ } else if ( scaleFactor >= 2 ) {
46
+ // 高缩放比,例如200%
47
+ zoomFactor = 0.8 ;
48
+ } else if ( scaleFactor >= 1.5 ) {
49
+ // 中等缩放比,例如150%
50
+ zoomFactor = 0.85 ;
51
+ } else if ( scaleFactor > 1.25 ) {
52
+ // 略高缩放比,例如125%-149%
53
+ zoomFactor = 0.9 ;
54
+ } else {
55
+ // 低缩放比,不做调整
56
+ zoomFactor = 1 ;
57
+ }
58
+ }
59
+
60
+ // 获取用户的自定义缩放设置(如果有)
61
+ const userZoomFactor = store . get ( 'set.contentZoomFactor' ) as number | undefined ;
62
+ if ( userZoomFactor ) {
63
+ zoomFactor = userZoomFactor ;
64
+ }
65
+
66
+ return zoomFactor ;
67
+ }
68
+
69
+ /**
70
+ * 应用页面内容缩放
71
+ * @param window 目标窗口
72
+ */
73
+ function applyContentZoom ( window : BrowserWindow ) : void {
74
+ const zoomFactor = calculateContentZoomFactor ( ) ;
75
+ window . webContents . setZoomFactor ( zoomFactor ) ;
76
+ console . log ( `应用页面缩放因子: ${ zoomFactor } , 系统缩放比: ${ screen . getPrimaryDisplay ( ) . scaleFactor } ` ) ;
77
+ }
19
78
20
79
/**
21
80
* 初始化代理设置
@@ -86,26 +145,28 @@ export function initializeWindowManager() {
86
145
ipcMain . on ( 'mini-window' , ( event ) => {
87
146
const win = BrowserWindow . fromWebContents ( event . sender ) ;
88
147
if ( win ) {
89
- // 保存当前窗口状态
148
+ // 保存当前窗口状态,以便之后恢复
90
149
const [ width , height ] = win . getSize ( ) ;
91
150
const [ x , y ] = win . getPosition ( ) ;
92
- mainWindowState = {
151
+ preMiniModeState = {
93
152
width,
94
153
height,
95
154
x,
96
155
y,
97
156
isMaximized : win . isMaximized ( )
98
157
} ;
99
158
100
- // 获取屏幕尺寸
101
- const { width : screenWidth } = screen . getPrimaryDisplay ( ) . workAreaSize ;
102
-
159
+ // 获取屏幕工作区尺寸
160
+ const display = screen . getDisplayMatching ( win . getBounds ( ) ) ;
161
+ const { width : screenWidth , x : screenX } = display . workArea ;
162
+
103
163
// 设置迷你窗口的大小和位置
104
164
win . unmaximize ( ) ;
105
- win . setMinimumSize ( 340 , 64 ) ;
106
- win . setMaximumSize ( 340 , 64 ) ;
107
- win . setSize ( 340 , 64 ) ;
108
- win . setPosition ( screenWidth - 340 , 20 ) ;
165
+ win . setMinimumSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_HEIGHT ) ;
166
+ win . setMaximumSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_HEIGHT ) ;
167
+ win . setSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_HEIGHT ) ;
168
+ // 将迷你窗口放在工作区的右上角,留出一些边距
169
+ win . setPosition ( screenX + screenWidth - DEFAULT_MINI_WIDTH - 20 , display . workArea . y + 20 ) ;
109
170
win . setAlwaysOnTop ( true ) ;
110
171
win . setSkipTaskbar ( false ) ;
111
172
win . setResizable ( false ) ;
@@ -115,6 +176,9 @@ export function initializeWindowManager() {
115
176
116
177
// 发送事件到渲染进程,通知切换到迷你模式
117
178
win . webContents . send ( 'mini-mode' , true ) ;
179
+
180
+ // 迷你窗口使用默认的缩放比
181
+ win . webContents . setZoomFactor ( 1 ) ;
118
182
}
119
183
} ) ;
120
184
@@ -124,18 +188,57 @@ export function initializeWindowManager() {
124
188
if ( win ) {
125
189
// 恢复窗口的大小调整功能
126
190
win . setResizable ( true ) ;
127
- win . setMaximumSize ( 0 , 0 ) ;
128
-
129
- // 恢复窗口的最小尺寸限制
130
- win . setMinimumSize ( 1200 , 780 ) ;
191
+ win . setMaximumSize ( 0 , 0 ) ; // 取消最大尺寸限制
192
+
193
+ // 根据屏幕尺寸计算合适的最小尺寸
194
+ const workArea = screen . getDisplayMatching ( win . getBounds ( ) ) . workArea ;
195
+ const minWidth = Math . min ( DEFAULT_MAIN_WIDTH , workArea . width * 0.6 ) ;
196
+ const minHeight = Math . min ( DEFAULT_MAIN_HEIGHT , workArea . height * 0.6 ) ;
197
+
198
+ win . setMinimumSize ( Math . round ( minWidth ) , Math . round ( minHeight ) ) ;
131
199
132
200
// 恢复窗口状态
133
- if ( mainWindowState . isMaximized ) {
201
+ if ( preMiniModeState . isMaximized ) {
134
202
win . maximize ( ) ;
135
203
} else {
136
- win . setSize ( mainWindowState . width , mainWindowState . height ) ;
137
- if ( mainWindowState . x !== undefined && mainWindowState . y !== undefined ) {
138
- win . setPosition ( mainWindowState . x , mainWindowState . y ) ;
204
+ // 确保窗口尺寸不超过当前屏幕
205
+ const { width, height } = screen . getDisplayMatching ( {
206
+ x : preMiniModeState . x || 0 ,
207
+ y : preMiniModeState . y || 0 ,
208
+ width : 1 ,
209
+ height : 1
210
+ } ) . workArea ;
211
+
212
+ win . setSize (
213
+ Math . min ( preMiniModeState . width , Math . round ( width * 0.9 ) ) ,
214
+ Math . min ( preMiniModeState . height , Math . round ( height * 0.9 ) )
215
+ ) ;
216
+
217
+ if ( preMiniModeState . x !== undefined && preMiniModeState . y !== undefined ) {
218
+ // 确保窗口位于屏幕内
219
+ const displays = screen . getAllDisplays ( ) ;
220
+ let isVisible = false ;
221
+
222
+ for ( const display of displays ) {
223
+ const { x, y, width, height } = display . workArea ;
224
+ if (
225
+ preMiniModeState . x >= x &&
226
+ preMiniModeState . x < x + width &&
227
+ preMiniModeState . y >= y &&
228
+ preMiniModeState . y < y + height
229
+ ) {
230
+ isVisible = true ;
231
+ break ;
232
+ }
233
+ }
234
+
235
+ if ( isVisible ) {
236
+ win . setPosition ( preMiniModeState . x , preMiniModeState . y ) ;
237
+ } else {
238
+ win . center ( ) ; // 如果位置不可见,则居中
239
+ }
240
+ } else {
241
+ win . center ( ) ;
139
242
}
140
243
}
141
244
@@ -147,13 +250,23 @@ export function initializeWindowManager() {
147
250
148
251
// 发送事件到渲染进程,通知退出迷你模式
149
252
win . webContents . send ( 'mini-mode' , false ) ;
253
+
254
+ // 应用页面内容缩放
255
+ applyContentZoom ( win ) ;
150
256
}
151
257
} ) ;
152
258
153
259
// 监听代理设置变化
154
260
store . onDidChange ( 'set.proxyConfig' , ( ) => {
155
261
initializeProxy ( ) ;
156
262
} ) ;
263
+
264
+ // 监听自定义内容缩放设置变化
265
+ store . onDidChange ( 'set.contentZoomFactor' , ( ) => {
266
+ if ( mainWindowInstance && ! mainWindowInstance . isDestroyed ( ) ) {
267
+ applyContentZoom ( mainWindowInstance ) ;
268
+ }
269
+ } ) ;
157
270
158
271
// 监听窗口大小调整事件
159
272
ipcMain . on ( 'resize-window' , ( event , width , height ) => {
@@ -170,19 +283,61 @@ export function initializeWindowManager() {
170
283
const win = BrowserWindow . fromWebContents ( event . sender ) ;
171
284
if ( win ) {
172
285
if ( showPlaylist ) {
173
- console . log ( ' 主进程: 扩大迷你窗口至 340 x 400' ) ;
286
+ console . log ( ` 主进程: 扩大迷你窗口至 ${ DEFAULT_MINI_WIDTH } x ${ DEFAULT_MINI_EXPANDED_HEIGHT } ` ) ;
174
287
// 调整最大尺寸限制,允许窗口变大
175
- win . setMinimumSize ( 340 , 64 ) ;
176
- win . setMaximumSize ( 340 , 400 ) ;
288
+ win . setMinimumSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_HEIGHT ) ;
289
+ win . setMaximumSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_EXPANDED_HEIGHT ) ;
177
290
// 调整窗口尺寸
178
- win . setSize ( 340 , 400 ) ;
291
+ win . setSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_EXPANDED_HEIGHT ) ;
179
292
} else {
180
- console . log ( ' 主进程: 缩小迷你窗口至 340 x 64' ) ;
293
+ console . log ( ` 主进程: 缩小迷你窗口至 ${ DEFAULT_MINI_WIDTH } x ${ DEFAULT_MINI_HEIGHT } ` ) ;
181
294
// 强制重置尺寸限制,确保窗口可以缩小
182
- win . setMaximumSize ( 340 , 64 ) ;
183
- win . setMinimumSize ( 340 , 64 ) ;
295
+ win . setMaximumSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_HEIGHT ) ;
296
+ win . setMinimumSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_HEIGHT ) ;
184
297
// 调整窗口尺寸
185
- win . setSize ( 340 , 64 ) ;
298
+ win . setSize ( DEFAULT_MINI_WIDTH , DEFAULT_MINI_HEIGHT ) ;
299
+ }
300
+ }
301
+ } ) ;
302
+
303
+ // 允许用户通过IPC调整页面缩放比例
304
+ ipcMain . on ( 'set-content-zoom' , ( event , zoomFactor ) => {
305
+ const win = BrowserWindow . fromWebContents ( event . sender ) ;
306
+ if ( win ) {
307
+ win . webContents . setZoomFactor ( zoomFactor ) ;
308
+ store . set ( 'set.contentZoomFactor' , zoomFactor ) ;
309
+ }
310
+ } ) ;
311
+
312
+ // 获取当前缩放比例
313
+ ipcMain . handle ( 'get-content-zoom' , ( event ) => {
314
+ const win = BrowserWindow . fromWebContents ( event . sender ) ;
315
+ if ( win ) {
316
+ return win . webContents . getZoomFactor ( ) ;
317
+ }
318
+ return 1 ; // 默认缩放比例
319
+ } ) ;
320
+
321
+ // 获取系统缩放因子
322
+ ipcMain . handle ( 'get-system-scale-factor' , ( ) => {
323
+ return screen . getPrimaryDisplay ( ) . scaleFactor ;
324
+ } ) ;
325
+
326
+ // 重置页面缩放到基于系统缩放比的默认值
327
+ ipcMain . on ( 'reset-content-zoom' , ( event ) => {
328
+ const win = BrowserWindow . fromWebContents ( event . sender ) ;
329
+ if ( win ) {
330
+ store . delete ( 'set.contentZoomFactor' ) ;
331
+ applyContentZoom ( win ) ;
332
+ }
333
+ } ) ;
334
+
335
+ // 监听显示器变化事件
336
+ screen . on ( 'display-metrics-changed' , ( _event , _display , changedMetrics ) => {
337
+ if ( mainWindowInstance && ! mainWindowInstance . isDestroyed ( ) ) {
338
+ // 当缩放因子变化时,重新应用页面缩放
339
+ if ( changedMetrics . includes ( 'scaleFactor' ) ) {
340
+ applyContentZoom ( mainWindowInstance ) ;
186
341
}
187
342
}
188
343
} ) ;
@@ -203,9 +358,27 @@ export function initializeWindowManager() {
203
358
* 创建主窗口
204
359
*/
205
360
export function createMainWindow ( icon : Electron . NativeImage ) : BrowserWindow {
361
+ // 使用 electron-window-state 管理窗口状态
362
+ const mainWindowState = windowStateKeeper ( {
363
+ defaultWidth : DEFAULT_MAIN_WIDTH ,
364
+ defaultHeight : DEFAULT_MAIN_HEIGHT
365
+ } ) ;
366
+
367
+ // 计算适当的最小尺寸(基于工作区大小)
368
+ const primaryDisplay = screen . getPrimaryDisplay ( ) ;
369
+ const { width : workAreaWidth , height : workAreaHeight } = primaryDisplay . workArea ;
370
+
371
+ // 根据缩放因子和工作区大小调整最小尺寸
372
+ const minWidth = Math . min ( Math . round ( DEFAULT_MAIN_WIDTH * 0.6 ) , Math . round ( workAreaWidth * 0.5 ) ) ;
373
+ const minHeight = Math . min ( Math . round ( DEFAULT_MAIN_HEIGHT * 0.6 ) , Math . round ( workAreaHeight * 0.5 ) ) ;
374
+
206
375
const mainWindow = new BrowserWindow ( {
207
- width : 1200 ,
208
- height : 780 ,
376
+ x : mainWindowState . x ,
377
+ y : mainWindowState . y ,
378
+ width : mainWindowState . width ,
379
+ height : mainWindowState . height ,
380
+ minWidth,
381
+ minHeight,
209
382
show : false ,
210
383
frame : false ,
211
384
autoHideMenuBar : true ,
@@ -218,11 +391,27 @@ export function createMainWindow(icon: Electron.NativeImage): BrowserWindow {
218
391
}
219
392
} ) ;
220
393
221
- mainWindow . setMinimumSize ( 1200 , 780 ) ;
394
+ // 确保最小尺寸设置正确
395
+ mainWindow . setMinimumSize ( minWidth , minHeight ) ;
222
396
mainWindow . removeMenu ( ) ;
397
+
398
+ // 让 windowStateKeeper 管理窗口状态
399
+ mainWindowState . manage ( mainWindow ) ;
400
+
401
+ // 初始化时保存到 preMiniModeState,以便从迷你模式恢复
402
+ preMiniModeState = {
403
+ width : mainWindowState . width ,
404
+ height : mainWindowState . height ,
405
+ x : mainWindowState . x ,
406
+ y : mainWindowState . y ,
407
+ isMaximized : mainWindowState . isMaximized
408
+ } ;
223
409
224
410
mainWindow . on ( 'ready-to-show' , ( ) => {
225
411
mainWindow . show ( ) ;
412
+
413
+ // 应用页面内容缩放
414
+ applyContentZoom ( mainWindow ) ;
226
415
} ) ;
227
416
228
417
mainWindow . webContents . setWindowOpenHandler ( ( details ) => {
@@ -247,7 +436,5 @@ export function createMainWindow(icon: Electron.NativeImage): BrowserWindow {
247
436
// 保存主窗口引用
248
437
mainWindowInstance = mainWindow ;
249
438
250
- mainWindow . on ( 'blur' , ( ) => mainWindow && mainWindow . setMaximizable ( false ) )
251
-
252
439
return mainWindow ;
253
440
}
0 commit comments