feat: 优化窗口大小管理功能,优化窗口状态保存与恢复逻辑

- 引入窗口大小管理器,初始化窗口大小管理
- 优化窗口状态保存与恢复,确保在迷你模式下正确应用窗口大小
- 移除不必要的代码,简化窗口管理逻辑
- 更新窗口创建逻辑,确保窗口大小和位置的正确性
This commit is contained in:
alger
2025-06-06 23:37:06 +08:00
parent b203077cad
commit e46df8a04e
5 changed files with 825 additions and 257 deletions

View File

@@ -15,6 +15,7 @@ import { initializeTray, updateCurrentSong, updatePlayState, updateTrayMenu } fr
import { setupUpdateHandlers } from './modules/update';
import { createMainWindow, initializeWindowManager } from './modules/window';
import { startMusicApi } from './server';
import { initWindowSizeManager } from './modules/window-size';
// 导入所有图标
const iconPath = join(__dirname, '../../resources');
@@ -99,6 +100,9 @@ if (!isSingleInstance) {
optimizer.watchWindowShortcuts(window);
});
// 初始化窗口大小管理器
initWindowSizeManager();
// 初始化应用
initialize();

View File

@@ -8,8 +8,6 @@ import * as NodeID3 from 'node-id3';
import * as path from 'path';
import * as os from 'os';
import * as mm from 'music-metadata';
// 导入文件类型库这里使用CommonJS兼容方式导入
// 对于file-type v21.0.0需要这样导入
import { fileTypeFromFile } from 'file-type';
import { getStore } from './config';

View File

@@ -0,0 +1,700 @@
import { app, BrowserWindow, ipcMain, screen } from 'electron';
import Store from 'electron-store';
const store = new Store();
// 默认窗口尺寸
export const DEFAULT_MAIN_WIDTH = 1200;
export const DEFAULT_MAIN_HEIGHT = 780;
export const DEFAULT_MINI_WIDTH = 340;
export const DEFAULT_MINI_HEIGHT = 64;
export const DEFAULT_MINI_EXPANDED_HEIGHT = 400;
// 用于存储窗口状态的键名
export const WINDOW_STATE_KEY = 'windowState';
// 最小窗口尺寸
let MIN_WIDTH = Math.round(DEFAULT_MAIN_WIDTH * 0.5);
let MIN_HEIGHT = Math.round(DEFAULT_MAIN_HEIGHT * 0.5);
// 标记IPC处理程序是否已注册
let ipcHandlersRegistered = false;
/**
* 窗口状态类型定义
*/
export interface WindowState {
width: number;
height: number;
x?: number;
y?: number;
isMaximized: boolean;
}
/**
* 窗口大小管理器
* 负责保存、恢复和维护窗口大小状态
*/
class WindowSizeManager {
private store: Store;
private mainWindow: BrowserWindow | null = null;
private savedState: WindowState | null = null;
private isInitialized: boolean = false;
constructor() {
this.store = store;
// 初始化时不做与screen相关的操作等app ready后再初始化
}
/**
* 初始化窗口大小管理器
* 必须在app ready后调用
*/
initialize(): void {
if (!app.isReady()) {
console.warn('WindowSizeManager.initialize() 必须在 app ready 之后调用!');
return;
}
if (this.isInitialized) {
return;
}
this.initMinimumWindowSize();
this.setupIPCHandlers();
this.isInitialized = true;
console.log('窗口大小管理器初始化完成');
}
/**
* 设置主窗口引用
*/
setMainWindow(win: BrowserWindow): void {
if (!this.isInitialized) {
this.initialize();
}
this.mainWindow = win;
// 读取保存的状态
this.savedState = this.getWindowState();
// 监听重要事件
this.setupEventListeners(win);
// 立即保存初始状态
this.saveWindowState(win);
}
/**
* 初始化最小窗口尺寸
*/
private initMinimumWindowSize(): void {
if (!app.isReady()) {
console.warn('不能在 app ready 之前访问 screen 模块');
return;
}
try {
const { width: workAreaWidth, height: workAreaHeight } = screen.getPrimaryDisplay().workArea;
// 根据工作区大小设置合理的最小尺寸
MIN_WIDTH = Math.min(Math.round(DEFAULT_MAIN_WIDTH * 0.5), Math.round(workAreaWidth * 0.3));
MIN_HEIGHT = Math.min(Math.round(DEFAULT_MAIN_HEIGHT * 0.5), Math.round(workAreaHeight * 0.3));
console.log(`设置最小窗口尺寸: ${MIN_WIDTH}x${MIN_HEIGHT}`);
} catch (error) {
console.error('初始化最小窗口尺寸失败:', error);
// 使用默认值
MIN_WIDTH = Math.round(DEFAULT_MAIN_WIDTH * 0.5);
MIN_HEIGHT = Math.round(DEFAULT_MAIN_HEIGHT * 0.5);
}
}
/**
* 设置事件监听器
*/
private setupEventListeners(win: BrowserWindow): void {
// 监听窗口大小调整事件
win.on('resize', () => {
if (!win.isDestroyed() && !win.isMinimized()) {
this.saveWindowState(win);
}
});
// 监听窗口移动事件
win.on('move', () => {
if (!win.isDestroyed() && !win.isMinimized()) {
this.saveWindowState(win);
}
});
// 监听窗口最大化事件
win.on('maximize', () => {
if (!win.isDestroyed()) {
this.saveWindowState(win);
}
});
// 监听窗口从最大化恢复事件
win.on('unmaximize', () => {
if (!win.isDestroyed()) {
this.saveWindowState(win);
}
});
// 监听窗口关闭事件,确保保存最终状态
win.on('close', () => {
if (!win.isDestroyed()) {
this.saveWindowState(win);
}
});
// 在页面加载完成后确保窗口大小正确
win.webContents.on('did-finish-load', () => {
this.enforceCorrectSize(win);
});
// 在窗口准备好显示时确保尺寸正确
win.on('ready-to-show', () => {
this.enforceCorrectSize(win);
});
}
/**
* 强制应用正确的窗口大小
*/
private enforceCorrectSize(win: BrowserWindow): void {
if (!this.savedState || win.isMaximized() || win.isMinimized() || win.isDestroyed()) {
return;
}
const [currentWidth, currentHeight] = win.getSize();
if (Math.abs(currentWidth - this.savedState.width) > 2 ||
Math.abs(currentHeight - this.savedState.height) > 2) {
console.log(`强制调整窗口大小: 当前=${currentWidth}x${currentHeight}, 目标=${this.savedState.width}x${this.savedState.height}`);
// 临时禁用minimum size限制
const [minWidth, minHeight] = win.getMinimumSize();
win.setMinimumSize(1, 1);
// 强制设置正确大小
win.setSize(this.savedState.width, this.savedState.height, false);
// 恢复原始minimum size
win.setMinimumSize(minWidth, minHeight);
// 验证尺寸设置是否成功
const [newWidth, newHeight] = win.getSize();
console.log(`调整后窗口大小: ${newWidth}x${newHeight}`);
// 如果调整后的大小仍然与目标不一致,尝试再次调整
if (Math.abs(newWidth - this.savedState.width) > 1 ||
Math.abs(newHeight - this.savedState.height) > 1) {
console.log(`窗口大小调整后仍不一致,将再次尝试调整`);
setTimeout(() => {
if (!win.isDestroyed() && !win.isMaximized() && !win.isMinimized()) {
win.setSize(this.savedState!.width, this.savedState!.height, false);
}
}, 50);
}
// // 开始尺寸强制执行
// this.startSizeEnforcement(win);
}
}
/**
* 开启尺寸强制执行定时器
*/
// private startSizeEnforcement(win: BrowserWindow): void {
// // 清除之前的定时器
// if (this.enforceTimer) {
// clearInterval(this.enforceTimer);
// this.enforceTimer = null;
// }
// this.enforceCount = 0;
// // 创建新的定时器每50ms检查一次窗口大小
// this.enforceTimer = setInterval(() => {
// if (this.enforceCount >= this.MAX_ENFORCE_COUNT ||
// !this.savedState ||
// win.isDestroyed() ||
// win.isMaximized() ||
// win.isMinimized()) {
// // 达到最大检查次数或不需要检查,清除定时器
// if (this.enforceTimer) {
// clearInterval(this.enforceTimer);
// this.enforceTimer = null;
// }
// return;
// }
// const [currentWidth, currentHeight] = win.getSize();
// if (Math.abs(currentWidth - this.savedState.width) > 2 ||
// Math.abs(currentHeight - this.savedState.height) > 2) {
// console.log(`[定时检查] 强制调整窗口大小: 当前=${currentWidth}x${currentHeight}, 目标=${this.savedState.width}x${this.savedState.height}`);
// // 临时禁用minimum size限制
// const [minWidth, minHeight] = win.getMinimumSize();
// win.setMinimumSize(1, 1);
// // 强制设置正确大小
// win.setSize(this.savedState.width, this.savedState.height, false);
// // 恢复原始minimum size
// win.setMinimumSize(minWidth, minHeight);
// // 验证尺寸设置是否成功
// const [newWidth, newHeight] = win.getSize();
// if (Math.abs(newWidth - this.savedState.width) <= 1 &&
// Math.abs(newHeight - this.savedState.height) <= 1) {
// console.log(`窗口大小已成功调整为目标尺寸: ${newWidth}x${newHeight}`);
// }
// }
// this.enforceCount++;
// }, 50);
// }
/**
* 获取窗口创建选项
*/
getWindowOptions(): Electron.BrowserWindowConstructorOptions {
// 确保初始化
if (!this.isInitialized && app.isReady()) {
this.initialize();
}
// 读取保存的状态
const savedState = this.getWindowState();
// 准备选项
const options: Electron.BrowserWindowConstructorOptions = {
width: savedState?.width || DEFAULT_MAIN_WIDTH,
height: savedState?.height || DEFAULT_MAIN_HEIGHT,
minWidth: MIN_WIDTH,
minHeight: MIN_HEIGHT,
show: false,
frame: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
};
// 如果有保存的位置,且位置有效,则使用该位置
if (savedState?.x !== undefined && savedState?.y !== undefined && app.isReady()) {
if (this.isPositionVisible(savedState.x, savedState.y)) {
options.x = savedState.x;
options.y = savedState.y;
}
}
console.log(`窗口创建选项: 大小=${options.width}x${options.height}, 位置=(${options.x}, ${options.y})`);
return options;
}
/**
* 应用窗口初始状态
* 在窗口创建后调用
*/
applyInitialState(win: BrowserWindow): void {
const savedState = this.getWindowState();
if (!savedState) {
win.center();
return;
}
// 如果需要最大化,直接最大化
if (savedState.isMaximized) {
console.log('应用已保存的最大化状态');
win.maximize();
}
// 如果位置无效,则居中显示
else if (!app.isReady() || savedState.x === undefined || savedState.y === undefined ||
!this.isPositionVisible(savedState.x, savedState.y)) {
console.log('保存的位置无效,窗口居中显示');
win.center();
}
}
/**
* 保存窗口状态
*/
saveWindowState(win: BrowserWindow): WindowState {
// 如果窗口已销毁,则返回之前的状态或默认状态
console.log('win.isDestroyed()',win.isDestroyed())
if (win.isDestroyed()) {
return this.savedState || {
width: DEFAULT_MAIN_WIDTH,
height: DEFAULT_MAIN_HEIGHT,
isMaximized: false
};
}
const isMaximized = win.isMaximized();
let state: WindowState;
if (isMaximized) {
// 如果窗口处于最大化状态,保存最大化标志
// 由于 Electron 的限制,最大化状态下 getBounds() 可能不准确
// 所以我们尽量保留之前保存的非最大化时的大小
const currentBounds = win.getBounds();
const previousSize = this.savedState && !this.savedState.isMaximized
? { width: this.savedState.width, height: this.savedState.height }
: { width: currentBounds.width, height: currentBounds.height };
state = {
width: previousSize.width,
height: previousSize.height,
x: currentBounds.x,
y: currentBounds.y,
isMaximized: true
};
console.log('state IsMaximized',state)
}
else if (win.isMinimized()) {
// 最小化状态下不保存窗口大小,因为可能不准确
console.log('state IsMinimized',this.savedState)
return this.savedState || {
width: DEFAULT_MAIN_WIDTH,
height: DEFAULT_MAIN_HEIGHT,
isMaximized: false
};
}
else {
// 正常状态下保存当前大小和位置
const [width, height] = win.getSize();
const [x, y] = win.getPosition();
state = {
width,
height,
x,
y,
isMaximized: false
};
console.log('state IsNormal',state)
}
// 保存状态到存储
this.store.set(WINDOW_STATE_KEY, state);
console.log(`已保存窗口状态: ${JSON.stringify(state)}`);
// 更新内部状态
this.savedState = state;
console.log('state',state)
return state;
}
/**
* 获取保存的窗口状态
*/
getWindowState(): WindowState | null {
const state = this.store.get(WINDOW_STATE_KEY) as WindowState | undefined;
if (!state) {
console.log('未找到保存的窗口状态,将使用默认值');
return null;
}
// 验证尺寸,确保不小于最小值
const validatedState: WindowState = {
width: Math.max(MIN_WIDTH, state.width || DEFAULT_MAIN_WIDTH),
height: Math.max(MIN_HEIGHT, state.height || DEFAULT_MAIN_HEIGHT),
x: state.x,
y: state.y,
isMaximized: !!state.isMaximized
};
console.log(`读取保存的窗口状态: ${JSON.stringify(validatedState)}`);
return validatedState;
}
/**
* 检查位置是否在可见屏幕范围内
*/
isPositionVisible(x: number, y: number): boolean {
if (!app.isReady()) {
return false;
}
try {
const displays = screen.getAllDisplays();
for (const display of displays) {
const { x: screenX, y: screenY, width, height } = display.workArea;
if (
x >= screenX &&
x < screenX + width &&
y >= screenY &&
y < screenY + height
) {
return true;
}
}
} catch (error) {
console.error('检查位置可见性失败:', error);
return false;
}
return false;
}
/**
* 计算适合当前缩放比的缩放因子
*/
calculateContentZoomFactor(): number {
// 只有在 app 准备好后才能使用screen
if (!app.isReady()) {
return 1;
}
try {
// 获取系统的缩放因子
const { scaleFactor } = screen.getPrimaryDisplay();
// 缩放因子默认为1
let zoomFactor = 1;
// 只在高DPI情况下调整
if (scaleFactor > 1) {
// 自定义逻辑来根据不同的缩放比例进行调整
if (scaleFactor >= 2.5) {
// 极高缩放比例如4K屏幕用200%+缩放
zoomFactor = 0.7;
} else if (scaleFactor >= 2) {
// 高缩放比例如200%
zoomFactor = 0.8;
} else if (scaleFactor >= 1.5) {
// 中等缩放比例如150%
zoomFactor = 0.85;
} else if (scaleFactor > 1.25) {
// 略高缩放比例如125%-149%
zoomFactor = 0.9;
} else {
// 低缩放比,不做调整
zoomFactor = 1;
}
}
// 获取用户的自定义缩放设置(如果有)
const userZoomFactor = this.store.get('set.contentZoomFactor') as number | undefined;
if (userZoomFactor) {
zoomFactor = userZoomFactor;
}
return zoomFactor;
} catch (error) {
console.error('计算内容缩放因子失败:', error);
return 1;
}
}
/**
* 应用页面内容缩放
*/
applyContentZoom(win: BrowserWindow): void {
const zoomFactor = this.calculateContentZoomFactor();
win.webContents.setZoomFactor(zoomFactor);
if (app.isReady()) {
try {
console.log(`应用页面缩放因子: ${zoomFactor}, 系统缩放比: ${screen.getPrimaryDisplay().scaleFactor}`);
} catch (error) {
console.log(`应用页面缩放因子: ${zoomFactor}`);
}
} else {
console.log(`应用页面缩放因子: ${zoomFactor}`);
}
}
/**
* 初始化IPC消息处理程序
*/
setupIPCHandlers(): void {
// 防止重复注册IPC处理程序
if (ipcHandlersRegistered) {
console.log('IPC处理程序已注册跳过重复注册');
return;
}
console.log('注册窗口大小相关的IPC处理程序');
// 标记为已注册
ipcHandlersRegistered = true;
// 安全地移除已存在的处理程序(如果有)
const removeHandlerSafely = (channel: string) => {
try {
ipcMain.removeHandler(channel);
} catch (error) {
// 忽略错误,处理程序可能不存在
}
};
// 为需要使用handle方法的通道先移除已有处理程序
removeHandlerSafely('get-content-zoom');
removeHandlerSafely('get-system-scale-factor');
// 注册新的处理程序
ipcMain.on('set-content-zoom', (event, zoomFactor) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win && !win.isDestroyed()) {
win.webContents.setZoomFactor(zoomFactor);
this.store.set('set.contentZoomFactor', zoomFactor);
}
});
ipcMain.handle('get-content-zoom', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win && !win.isDestroyed()) {
return win.webContents.getZoomFactor();
}
return 1;
});
ipcMain.handle('get-system-scale-factor', () => {
if (!app.isReady()) {
return 1;
}
try {
return screen.getPrimaryDisplay().scaleFactor;
} catch (error) {
console.error('获取系统缩放因子失败:', error);
return 1;
}
});
ipcMain.on('reset-content-zoom', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win && !win.isDestroyed()) {
this.store.delete('set.contentZoomFactor');
this.applyContentZoom(win);
}
});
ipcMain.on('resize-window', (event, width, height) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win && !win.isDestroyed()) {
console.log(`接收到调整窗口大小请求: ${width}x${height}`);
// 确保尺寸不小于最小值
const adjustedWidth = Math.max(width, MIN_WIDTH);
const adjustedHeight = Math.max(height, MIN_HEIGHT);
// 设置窗口的大小
win.setSize(adjustedWidth, adjustedHeight);
console.log(`窗口大小已调整为: ${adjustedWidth}x${adjustedHeight}`);
// 保存窗口状态
this.saveWindowState(win);
}
});
ipcMain.on('resize-mini-window', (event, showPlaylist) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win && !win.isDestroyed()) {
if (showPlaylist) {
console.log(`扩大迷你窗口至 ${DEFAULT_MINI_WIDTH} x ${DEFAULT_MINI_EXPANDED_HEIGHT}`);
win.setMinimumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
win.setMaximumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_EXPANDED_HEIGHT);
win.setSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_EXPANDED_HEIGHT, false);
} else {
console.log(`缩小迷你窗口至 ${DEFAULT_MINI_WIDTH} x ${DEFAULT_MINI_HEIGHT}`);
win.setMaximumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
win.setMinimumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
win.setSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT, false);
}
}
});
// 只在app ready后设置显示器变化监听
if (app.isReady()) {
// 监听显示器变化事件
screen.on('display-metrics-changed', (_event, _display, changedMetrics) => {
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
// 当缩放因子变化时,重新应用页面缩放
if (changedMetrics.includes('scaleFactor')) {
this.applyContentZoom(this.mainWindow);
}
// 重新初始化最小尺寸
this.initMinimumWindowSize();
}
});
}
// 监听 store 中的缩放设置变化
this.store.onDidChange('set.contentZoomFactor', () => {
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
this.applyContentZoom(this.mainWindow);
}
});
}
}
// 创建窗口大小管理器实例
const windowSizeManager = new WindowSizeManager();
// 导出初始化函数
export const initWindowSizeManager = (): void => {
// 等待app ready后再初始化
if (app.isReady()) {
windowSizeManager.initialize();
} else {
app.on('ready', () => {
windowSizeManager.initialize();
});
}
};
// 导出实例方法
export const getWindowOptions = (): Electron.BrowserWindowConstructorOptions => {
return windowSizeManager.getWindowOptions();
};
export const applyInitialState = (win: BrowserWindow): void => {
windowSizeManager.applyInitialState(win);
};
export const saveWindowState = (win: BrowserWindow): WindowState => {
return windowSizeManager.saveWindowState(win);
};
export const getWindowState = (): WindowState | null => {
return windowSizeManager.getWindowState();
};
export const applyContentZoom = (win: BrowserWindow): void => {
windowSizeManager.applyContentZoom(win);
};
export const initWindowSizeHandlers = (mainWindow: BrowserWindow | null): void => {
// 确保app ready后再初始化
if (!app.isReady()) {
app.on('ready', () => {
if (mainWindow) {
windowSizeManager.setMainWindow(mainWindow);
}
});
} else {
if (mainWindow) {
windowSizeManager.setMainWindow(mainWindow);
}
}
};
export const calculateMinimumWindowSize = (): { minWidth: number; minHeight: number } => {
return { minWidth: MIN_WIDTH, minHeight: MIN_HEIGHT };
};

View File

@@ -1,81 +1,35 @@
import { is } from '@electron-toolkit/utils';
import { app, BrowserWindow, globalShortcut, ipcMain, screen, session, shell } from 'electron';
import Store from 'electron-store';
import windowStateKeeper from 'electron-window-state';
import { join } from 'path';
import {
DEFAULT_MAIN_WIDTH,
DEFAULT_MAIN_HEIGHT,
DEFAULT_MINI_WIDTH,
DEFAULT_MINI_HEIGHT,
applyContentZoom,
saveWindowState,
applyInitialState,
initWindowSizeHandlers,
getWindowOptions,
getWindowState,
WindowState
} from './window-size';
const store = new Store();
// 默认窗口尺寸
const DEFAULT_MAIN_WIDTH = 1200;
const DEFAULT_MAIN_HEIGHT = 780;
const DEFAULT_MINI_WIDTH = 340;
const DEFAULT_MINI_HEIGHT = 64;
const DEFAULT_MINI_EXPANDED_HEIGHT = 400;
// 保存主窗口引用,以便在 activate 事件中使用
let mainWindowInstance: BrowserWindow | null = null;
// 保存迷你模式前的窗口状态
let preMiniModeState = {
let preMiniModeState: WindowState = {
width: DEFAULT_MAIN_WIDTH,
height: DEFAULT_MAIN_HEIGHT,
x: undefined as number | undefined,
y: undefined as number | undefined,
x: undefined,
y: undefined,
isMaximized: false
};
/**
* 计算适合当前缩放比的缩放因子
* @returns 适合当前系统的页面缩放因子
*/
function calculateContentZoomFactor(): number {
// 获取系统的缩放因子
const { scaleFactor } = screen.getPrimaryDisplay();
// 缩放因子默认为1
let zoomFactor = 1;
// 只在高DPI情况下调整
if (scaleFactor > 1) {
// 自定义逻辑来根据不同的缩放比例进行调整
if (scaleFactor >= 2.5) {
// 极高缩放比例如4K屏幕用200%+缩放
zoomFactor = 0.7;
} else if (scaleFactor >= 2) {
// 高缩放比例如200%
zoomFactor = 0.8;
} else if (scaleFactor >= 1.5) {
// 中等缩放比例如150%
zoomFactor = 0.85;
} else if (scaleFactor > 1.25) {
// 略高缩放比例如125%-149%
zoomFactor = 0.9;
} else {
// 低缩放比,不做调整
zoomFactor = 1;
}
}
// 获取用户的自定义缩放设置(如果有)
const userZoomFactor = store.get('set.contentZoomFactor') as number | undefined;
if (userZoomFactor) {
zoomFactor = userZoomFactor;
}
return zoomFactor;
}
/**
* 应用页面内容缩放
* @param window 目标窗口
*/
function applyContentZoom(window: BrowserWindow): void {
const zoomFactor = calculateContentZoomFactor();
window.webContents.setZoomFactor(zoomFactor);
console.log(`应用页面缩放因子: ${zoomFactor}, 系统缩放比: ${screen.getPrimaryDisplay().scaleFactor}`);
}
/**
* 初始化代理设置
*/
@@ -124,6 +78,7 @@ export function initializeWindowManager() {
} else {
win.maximize();
}
// 状态保存在事件监听器中处理
}
});
@@ -146,15 +101,8 @@ export function initializeWindowManager() {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
// 保存当前窗口状态,以便之后恢复
const [width, height] = win.getSize();
const [x, y] = win.getPosition();
preMiniModeState = {
width,
height,
x,
y,
isMaximized: win.isMaximized()
};
preMiniModeState = saveWindowState(win);
console.log('保存正常模式状态用于恢复:', JSON.stringify(preMiniModeState));
// 获取屏幕工作区尺寸
const display = screen.getDisplayMatching(win.getBounds());
@@ -164,9 +112,9 @@ export function initializeWindowManager() {
win.unmaximize();
win.setMinimumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
win.setMaximumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
win.setSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
win.setSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT, false); // 禁用动画
// 将迷你窗口放在工作区的右上角,留出一些边距
win.setPosition(screenX + screenWidth - DEFAULT_MINI_WIDTH - 20, display.workArea.y + 20);
win.setPosition(screenX + screenWidth - DEFAULT_MINI_WIDTH - 20, display.workArea.y + 20, false);
win.setAlwaysOnTop(true);
win.setSkipTaskbar(false);
win.setResizable(false);
@@ -190,58 +138,12 @@ export function initializeWindowManager() {
win.setResizable(true);
win.setMaximumSize(0, 0); // 取消最大尺寸限制
// 根据屏幕尺寸计算合适的最小尺寸
const workArea = screen.getDisplayMatching(win.getBounds()).workArea;
const minWidth = Math.min(DEFAULT_MAIN_WIDTH, workArea.width * 0.6);
const minHeight = Math.min(DEFAULT_MAIN_HEIGHT, workArea.height * 0.6);
console.log('从迷你模式恢复,使用保存的状态:', JSON.stringify(preMiniModeState));
win.setMinimumSize(Math.round(minWidth), Math.round(minHeight));
// 设置适当的最小尺寸
win.setMinimumSize(Math.max(DEFAULT_MAIN_WIDTH * 0.5, 600), Math.max(DEFAULT_MAIN_HEIGHT * 0.5, 400));
// 恢复窗口状态
if (preMiniModeState.isMaximized) {
win.maximize();
} else {
// 确保窗口尺寸不超过当前屏幕
const { width, height } = screen.getDisplayMatching({
x: preMiniModeState.x || 0,
y: preMiniModeState.y || 0,
width: 1,
height: 1
}).workArea;
win.setSize(
Math.min(preMiniModeState.width, Math.round(width * 0.9)),
Math.min(preMiniModeState.height, Math.round(height * 0.9))
);
if (preMiniModeState.x !== undefined && preMiniModeState.y !== undefined) {
// 确保窗口位于屏幕内
const displays = screen.getAllDisplays();
let isVisible = false;
for (const display of displays) {
const { x, y, width, height } = display.workArea;
if (
preMiniModeState.x >= x &&
preMiniModeState.x < x + width &&
preMiniModeState.y >= y &&
preMiniModeState.y < y + height
) {
isVisible = true;
break;
}
}
if (isVisible) {
win.setPosition(preMiniModeState.x, preMiniModeState.y);
} else {
win.center(); // 如果位置不可见,则居中
}
} else {
win.center();
}
}
win.setAlwaysOnTop(false);
win.setSkipTaskbar(false);
@@ -251,8 +153,39 @@ export function initializeWindowManager() {
// 发送事件到渲染进程,通知退出迷你模式
win.webContents.send('mini-mode', false);
// 应用页面内容缩放
applyContentZoom(win);
// 应用保存的状态
setTimeout(() => {
// 如果有保存的位置,则应用
if (preMiniModeState.x !== undefined && preMiniModeState.y !== undefined) {
win.setPosition(preMiniModeState.x, preMiniModeState.y, false);
} else {
win.center();
}
// 使用存储的迷你模式前的状态
if (preMiniModeState.isMaximized) {
win.maximize();
} else {
// 设置正确的窗口大小
win.setSize(preMiniModeState.width, preMiniModeState.height, false);
}
// 应用页面缩放
applyContentZoom(win);
// 确保窗口大小被正确应用
setTimeout(() => {
if (!win.isDestroyed() && !win.isMaximized() && !win.isMinimized()) {
// 再次验证窗口大小
const [width, height] = win.getSize();
if (Math.abs(width - preMiniModeState.width) > 2 ||
Math.abs(height - preMiniModeState.height) > 2) {
console.log(`恢复后窗口大小不一致,再次调整: 当前=${width}x${height}, 目标=${preMiniModeState.width}x${preMiniModeState.height}`);
win.setSize(preMiniModeState.width, preMiniModeState.height, false);
}
}
}, 150);
}, 50);
}
});
@@ -261,87 +194,8 @@ export function initializeWindowManager() {
initializeProxy();
});
// 监听自定义内容缩放设置变化
store.onDidChange('set.contentZoomFactor', () => {
if (mainWindowInstance && !mainWindowInstance.isDestroyed()) {
applyContentZoom(mainWindowInstance);
}
});
// 监听窗口大小调整事件
ipcMain.on('resize-window', (event, width, height) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
// 设置窗口的大小
console.log(`调整窗口大小: ${width} x ${height}`);
win.setSize(width, height);
}
});
// 专门用于迷你模式下调整窗口大小的事件
ipcMain.on('resize-mini-window', (event, showPlaylist) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
if (showPlaylist) {
console.log(`主进程: 扩大迷你窗口至 ${DEFAULT_MINI_WIDTH} x ${DEFAULT_MINI_EXPANDED_HEIGHT}`);
// 调整最大尺寸限制,允许窗口变大
win.setMinimumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
win.setMaximumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_EXPANDED_HEIGHT);
// 调整窗口尺寸
win.setSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_EXPANDED_HEIGHT);
} else {
console.log(`主进程: 缩小迷你窗口至 ${DEFAULT_MINI_WIDTH} x ${DEFAULT_MINI_HEIGHT}`);
// 强制重置尺寸限制,确保窗口可以缩小
win.setMaximumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
win.setMinimumSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
// 调整窗口尺寸
win.setSize(DEFAULT_MINI_WIDTH, DEFAULT_MINI_HEIGHT);
}
}
});
// 允许用户通过IPC调整页面缩放比例
ipcMain.on('set-content-zoom', (event, zoomFactor) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
win.webContents.setZoomFactor(zoomFactor);
store.set('set.contentZoomFactor', zoomFactor);
}
});
// 获取当前缩放比例
ipcMain.handle('get-content-zoom', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
return win.webContents.getZoomFactor();
}
return 1; // 默认缩放比例
});
// 获取系统缩放因子
ipcMain.handle('get-system-scale-factor', () => {
return screen.getPrimaryDisplay().scaleFactor;
});
// 重置页面缩放到基于系统缩放比的默认值
ipcMain.on('reset-content-zoom', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
store.delete('set.contentZoomFactor');
applyContentZoom(win);
}
});
// 监听显示器变化事件
screen.on('display-metrics-changed', (_event, _display, changedMetrics) => {
if (mainWindowInstance && !mainWindowInstance.isDestroyed()) {
// 当缩放因子变化时,重新应用页面缩放
if (changedMetrics.includes('scaleFactor')) {
applyContentZoom(mainWindowInstance);
}
}
});
// 初始化窗口大小和缩放相关的IPC处理程序
initWindowSizeHandlers(mainWindowInstance);
// 监听 macOS 下点击 Dock 图标的事件
app.on('activate', () => {
// 当应用被激活时,检查主窗口是否存在
@@ -358,60 +212,72 @@ export function initializeWindowManager() {
* 创建主窗口
*/
export function createMainWindow(icon: Electron.NativeImage): BrowserWindow {
// 使用 electron-window-state 管理窗口状态
const mainWindowState = windowStateKeeper({
defaultWidth: DEFAULT_MAIN_WIDTH,
defaultHeight: DEFAULT_MAIN_HEIGHT
});
console.log('开始创建主窗口...');
// 计算适当的最小尺寸(基于工作区大小)
const primaryDisplay = screen.getPrimaryDisplay();
const { width: workAreaWidth, height: workAreaHeight } = primaryDisplay.workArea;
// 获取窗口创建选项
const options = getWindowOptions();
// 根据缩放因子和工作区大小调整最小尺寸
const minWidth = Math.min(Math.round(DEFAULT_MAIN_WIDTH * 0.6), Math.round(workAreaWidth * 0.5));
const minHeight = Math.min(Math.round(DEFAULT_MAIN_HEIGHT * 0.6), Math.round(workAreaHeight * 0.5));
const mainWindow = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth,
minHeight,
show: false,
frame: false,
autoHideMenuBar: true,
icon,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
contextIsolation: true,
webSecurity: false
}
});
// 确保最小尺寸设置正确
mainWindow.setMinimumSize(minWidth, minHeight);
mainWindow.removeMenu();
// 让 windowStateKeeper 管理窗口状态
mainWindowState.manage(mainWindow);
// 初始化时保存到 preMiniModeState以便从迷你模式恢复
preMiniModeState = {
width: mainWindowState.width,
height: mainWindowState.height,
x: mainWindowState.x,
y: mainWindowState.y,
isMaximized: mainWindowState.isMaximized
// 添加图标和预加载脚本
options.icon = icon;
options.webPreferences = {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
contextIsolation: true,
webSecurity: false
};
console.log(`创建窗口,使用选项: ${JSON.stringify({
width: options.width,
height: options.height,
x: options.x,
y: options.y,
minWidth: options.minWidth,
minHeight: options.minHeight
})}`);
// 创建窗口
const mainWindow = new BrowserWindow(options);
// 移除菜单
mainWindow.removeMenu();
// 应用初始状态 (例如最大化状态)
applyInitialState(mainWindow);
// 更新 preMiniModeState以便迷你模式可以正确恢复
const savedState = getWindowState();
if (savedState) {
preMiniModeState = { ...savedState };
}
mainWindow.on('ready-to-show', () => {
const [width, height] = mainWindow.getSize();
console.log(`窗口显示前的大小: ${width}x${height}`);
// 强制确保窗口使用正确的大小
if (savedState && !savedState.isMaximized) {
mainWindow.setSize(savedState.width, savedState.height, false);
}
// 显示窗口
mainWindow.show();
// 应用页面内容缩放
applyContentZoom(mainWindow);
// 再次检查窗口大小是否正确应用
setTimeout(() => {
if (!mainWindow.isDestroyed() && !mainWindow.isMaximized()) {
const [currentWidth, currentHeight] = mainWindow.getSize();
if (savedState && !savedState.isMaximized) {
if (Math.abs(currentWidth - savedState.width) > 2 ||
Math.abs(currentHeight - savedState.height) > 2) {
console.log(`窗口大小不匹配,再次调整: 当前=${currentWidth}x${currentHeight}, 目标=${savedState.width}x${savedState.height}`);
mainWindow.setSize(savedState.width, savedState.height, false);
}
}
}
}, 100);
});
mainWindow.webContents.setWindowOpenHandler((details) => {
@@ -433,6 +299,9 @@ export function createMainWindow(icon: Electron.NativeImage): BrowserWindow {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'));
}
initWindowSizeHandlers(mainWindow);
// 保存主窗口引用
mainWindowInstance = mainWindow;

View File

@@ -54,10 +54,7 @@ export function useZoom() {
// 重置缩放比例到系统建议值
const resetZoom = async () => {
try {
window.ipcRenderer.send('reset-content-zoom');
// 重置后重新获取系统计算的缩放比例
const newZoom = await window.ipcRenderer.invoke('get-content-zoom');
zoomFactor.value = newZoom;
setZoomFactor(1);
} catch (error) {
console.error('重置缩放比例失败:', error);
}