diff --git a/src/main/index.ts b/src/main/index.ts index e4c9656..bab4bae 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -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(); diff --git a/src/main/modules/fileManager.ts b/src/main/modules/fileManager.ts index 8ab399a..e1fb433 100644 --- a/src/main/modules/fileManager.ts +++ b/src/main/modules/fileManager.ts @@ -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'; diff --git a/src/main/modules/window-size.ts b/src/main/modules/window-size.ts new file mode 100644 index 0000000..3e76d92 --- /dev/null +++ b/src/main/modules/window-size.ts @@ -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 }; +}; \ No newline at end of file diff --git a/src/main/modules/window.ts b/src/main/modules/window.ts index ed84026..25345df 100644 --- a/src/main/modules/window.ts +++ b/src/main/modules/window.ts @@ -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,69 +138,54 @@ 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.max(DEFAULT_MAIN_WIDTH * 0.5, 600), Math.max(DEFAULT_MAIN_HEIGHT * 0.5, 400)); - win.setMinimumSize(Math.round(minWidth), Math.round(minHeight)); - // 恢复窗口状态 - 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); - + // 导航回主页面 win.webContents.send('navigate', '/'); - + // 发送事件到渲染进程,通知退出迷你模式 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)); + // 添加图标和预加载脚本 + options.icon = icon; + options.webPreferences = { + preload: join(__dirname, '../preload/index.js'), + sandbox: false, + contextIsolation: true, + webSecurity: false + }; - 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); + 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(); - // 让 windowStateKeeper 管理窗口状态 - mainWindowState.manage(mainWindow); + // 应用初始状态 (例如最大化状态) + applyInitialState(mainWindow); - // 初始化时保存到 preMiniModeState,以便从迷你模式恢复 - preMiniModeState = { - width: mainWindowState.width, - height: mainWindowState.height, - x: mainWindowState.x, - y: mainWindowState.y, - isMaximized: mainWindowState.isMaximized - }; + // 更新 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,8 +299,11 @@ export function createMainWindow(icon: Electron.NativeImage): BrowserWindow { mainWindow.loadFile(join(__dirname, '../renderer/index.html')); } + initWindowSizeHandlers(mainWindow); + + // 保存主窗口引用 mainWindowInstance = mainWindow; - + return mainWindow; } diff --git a/src/renderer/hooks/useZoom.ts b/src/renderer/hooks/useZoom.ts index 6c57616..ff43592 100644 --- a/src/renderer/hooks/useZoom.ts +++ b/src/renderer/hooks/useZoom.ts @@ -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); }