Skip to content

Commit 6170047

Browse files
committed
feat: 添加主窗口自适应大小功能,页面缩放功能,支持缩放因子的调整和重置,并在搜索栏中提供缩放控制
1 parent bf4bcfc commit 6170047

File tree

7 files changed

+379
-40
lines changed

7 files changed

+379
-40
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"cors": "^2.8.5",
2929
"electron-store": "^8.1.0",
3030
"electron-updater": "^6.6.2",
31+
"electron-window-state": "^5.0.3",
3132
"express": "^4.18.2",
3233
"font-list": "^1.5.1",
3334
"husky": "^9.1.7",

src/i18n/lang/en-US/comp.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ export default {
9090
restart: 'Restart',
9191
refresh: 'Refresh',
9292
currentVersion: 'Current Version',
93-
searchPlaceholder: 'Search for something...'
93+
searchPlaceholder: 'Search for something...',
94+
zoom: 'Zoom',
95+
zoom100: 'Zoom 100%',
96+
resetZoom: 'Reset Zoom',
97+
zoomDefault: 'Default Zoom'
9498
},
9599
titleBar: {
96100
closeTitle: 'Choose how to close',

src/i18n/lang/zh-CN/comp.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ export default {
8888
restart: '重启',
8989
refresh: '刷新',
9090
currentVersion: '当前版本',
91-
searchPlaceholder: '搜索点什么吧...'
91+
searchPlaceholder: '搜索点什么吧...',
92+
zoom: '页面缩放',
93+
zoom100: '标准缩放100%',
94+
resetZoom: '点击重置缩放',
95+
zoomDefault: '标准缩放'
9296
},
9397
titleBar: {
9498
closeTitle: '请选择关闭方式',

src/main/modules/window.ts

Lines changed: 223 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,80 @@
11
import { is } from '@electron-toolkit/utils';
22
import { app, BrowserWindow, globalShortcut, ipcMain, screen, session, shell } from 'electron';
33
import Store from 'electron-store';
4+
import windowStateKeeper from 'electron-window-state';
45
import { join } from 'path';
56

67
const store = new Store();
78

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,
1223
x: undefined as number | undefined,
1324
y: undefined as number | undefined,
1425
isMaximized: false
1526
};
1627

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+
}
1978

2079
/**
2180
* 初始化代理设置
@@ -86,26 +145,28 @@ export function initializeWindowManager() {
86145
ipcMain.on('mini-window', (event) => {
87146
const win = BrowserWindow.fromWebContents(event.sender);
88147
if (win) {
89-
// 保存当前窗口状态
148+
// 保存当前窗口状态,以便之后恢复
90149
const [width, height] = win.getSize();
91150
const [x, y] = win.getPosition();
92-
mainWindowState = {
151+
preMiniModeState = {
93152
width,
94153
height,
95154
x,
96155
y,
97156
isMaximized: win.isMaximized()
98157
};
99158

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+
103163
// 设置迷你窗口的大小和位置
104164
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);
109170
win.setAlwaysOnTop(true);
110171
win.setSkipTaskbar(false);
111172
win.setResizable(false);
@@ -115,6 +176,9 @@ export function initializeWindowManager() {
115176

116177
// 发送事件到渲染进程,通知切换到迷你模式
117178
win.webContents.send('mini-mode', true);
179+
180+
// 迷你窗口使用默认的缩放比
181+
win.webContents.setZoomFactor(1);
118182
}
119183
});
120184

@@ -124,18 +188,57 @@ export function initializeWindowManager() {
124188
if (win) {
125189
// 恢复窗口的大小调整功能
126190
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));
131199

132200
// 恢复窗口状态
133-
if (mainWindowState.isMaximized) {
201+
if (preMiniModeState.isMaximized) {
134202
win.maximize();
135203
} 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();
139242
}
140243
}
141244

@@ -147,13 +250,23 @@ export function initializeWindowManager() {
147250

148251
// 发送事件到渲染进程,通知退出迷你模式
149252
win.webContents.send('mini-mode', false);
253+
254+
// 应用页面内容缩放
255+
applyContentZoom(win);
150256
}
151257
});
152258

153259
// 监听代理设置变化
154260
store.onDidChange('set.proxyConfig', () => {
155261
initializeProxy();
156262
});
263+
264+
// 监听自定义内容缩放设置变化
265+
store.onDidChange('set.contentZoomFactor', () => {
266+
if (mainWindowInstance && !mainWindowInstance.isDestroyed()) {
267+
applyContentZoom(mainWindowInstance);
268+
}
269+
});
157270

158271
// 监听窗口大小调整事件
159272
ipcMain.on('resize-window', (event, width, height) => {
@@ -170,19 +283,61 @@ export function initializeWindowManager() {
170283
const win = BrowserWindow.fromWebContents(event.sender);
171284
if (win) {
172285
if (showPlaylist) {
173-
console.log('主进程: 扩大迷你窗口至 340 x 400');
286+
console.log(`主进程: 扩大迷你窗口至 ${DEFAULT_MINI_WIDTH} x ${DEFAULT_MINI_EXPANDED_HEIGHT}`);
174287
// 调整最大尺寸限制,允许窗口变大
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);
177290
// 调整窗口尺寸
178-
win.setSize(340, 400);
291+
win.setSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_EXPANDED_HEIGHT);
179292
} else {
180-
console.log('主进程: 缩小迷你窗口至 340 x 64');
293+
console.log(`主进程: 缩小迷你窗口至 ${DEFAULT_MINI_WIDTH} x ${DEFAULT_MINI_HEIGHT}`);
181294
// 强制重置尺寸限制,确保窗口可以缩小
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);
184297
// 调整窗口尺寸
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);
186341
}
187342
}
188343
});
@@ -203,9 +358,27 @@ export function initializeWindowManager() {
203358
* 创建主窗口
204359
*/
205360
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+
206375
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,
209382
show: false,
210383
frame: false,
211384
autoHideMenuBar: true,
@@ -218,11 +391,27 @@ export function createMainWindow(icon: Electron.NativeImage): BrowserWindow {
218391
}
219392
});
220393

221-
mainWindow.setMinimumSize(1200, 780);
394+
// 确保最小尺寸设置正确
395+
mainWindow.setMinimumSize(minWidth, minHeight);
222396
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+
};
223409

224410
mainWindow.on('ready-to-show', () => {
225411
mainWindow.show();
412+
413+
// 应用页面内容缩放
414+
applyContentZoom(mainWindow);
226415
});
227416

228417
mainWindow.webContents.setWindowOpenHandler((details) => {
@@ -247,7 +436,5 @@ export function createMainWindow(icon: Electron.NativeImage): BrowserWindow {
247436
// 保存主窗口引用
248437
mainWindowInstance = mainWindow;
249438

250-
mainWindow.on('blur', () => mainWindow && mainWindow.setMaximizable(false))
251-
252439
return mainWindow;
253440
}

0 commit comments

Comments
 (0)