Skip to content

fix: 修复关闭应用后窗口大小无法恢复的问题#417

Merged
J3n5en merged 1 commit into
J3n5en:mainfrom
lwt-sadais:fix/window-state-not-restored
Apr 11, 2026
Merged

fix: 修复关闭应用后窗口大小无法恢复的问题#417
J3n5en merged 1 commit into
J3n5en:mainfrom
lwt-sadais:fix/window-state-not-restored

Conversation

@lwt-sadais

Copy link
Copy Markdown
Contributor

问题描述

点击关闭按钮或按 Cmd+Q 退出应用后,再次打开应用时窗口大小没有恢复为关闭前的大小,而是变成了偏小的固定尺寸。

根因分析

createMainWindowreplacementState 的判断条件使用了可选链 ?.

const replacementState = options.replaceWindow?.isDestroyed()
  ? null
  : { ...options.replaceWindow?.getBounds(), ... };

options.replaceWindownull 时,null?.isDestroyed() 返回 undefined(假值),走了 else 分支,创建了空对象 {}。随后 state = {} ?? loadWindowState() 中,空对象不是 null/undefined?? 不会触发 loadWindowState(),导致窗口使用空对象的默认值创建,大小不正确。

修复内容

  1. 修复 replacementState 判断逻辑:先判断 replaceWindow 存在且未销毁才使用其状态,否则为 null,让 ?? 正确走到 loadWindowState()

  2. 调整 saveWindowState 时机:在 forceReplaceCloseCurrentWindow 中移至 win.hide() 之前调用,确保 getBounds() 在窗口可见时获取正确的值,避免 hide() 后 bounds 异常

退出时 `replacementState` 判断条件有误,`replaceWindow` 为 null 时
走 `getBounds()` 分支创建了空对象,导致 `??` 短路未加载已保存的窗口
状态。同时调整 `saveWindowState` 在 `win.hide()` 之前调用,确保获取
正确的窗口 bounds。
@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Claude finished @lwt-sadais's task in 2m 13s —— View job


🔍 代码审查报告

👋 欢迎查看审查结果! 本次审查已完成,以下是详细分析和建议。
💡 使用提示:点击 📁 文件链接可直接跳转到代码位置,建议按优先级处理问题。

📊 审查概览 · 一目了然

📋 审查项目 🔢 发现数量 📈 占比 🎯 处理状态
📁 审查文件 1 100% 已完成
🚨 严重问题 0 0%
⚠️ 中等问题 0 0%
轻微问题 1 100% 🔵 后续优化

🏆 代码质量评分

📊 综合评分: 90/100  🌟🌟🌟🌟⭐
📋 本次等级: 优秀

📋 问题分布

🚨 严重: ░░░░░░░░░░  0%
⚠️ 中等: ░░░░░░░░░░  0%
⚡ 轻微: █░░░░░░░░░ 10%
总体评分: ⭐⭐⭐⭐⭐ (5/5)

🎯 关键发现

🏷️ 类别 🔍 发现数量 🎯 优先级 📈 趋势
🔒 安全性 0 个 ✅ 无风险 ✅ 良好
⚡ 性能 0 个 ✅ 无影响 ✅ 良好
🎨 代码质量 1 个 🔵 低 📉 改善

✅ 变更分析

本次 PR 修改了 src/main/windows/MainWindow.ts,共 3 处改动,均围绕窗口状态保存与恢复逻辑:

改动 1:修复 replacementState 判断逻辑 ✅

src/main/windows/MainWindow.ts:100-106

// src/main/windows/MainWindow.ts:100-106
const replacementState =
    options.replaceWindow && !options.replaceWindow.isDestroyed()
      ? {
          ...options.replaceWindow.getBounds(),
          isMaximized: options.replaceWindow.isMaximized(),
        }
      : null;

分析:这是核心修复。原代码使用 options.replaceWindow?.isDestroyed() 作为条件,当 replaceWindownull 时返回 undefined(假值),错误地走入 else 分支,生成空对象 {}。空对象不是 null/undefined?? 运算符不触发 loadWindowState(),导致窗口使用默认尺寸。

新逻辑清晰明确:存在 replaceWindow 且未销毁 → 使用其 bounds,否则 → null,让 ?? 正确回退到 loadWindowState()。同时移除了不必要的可选链 ?.,因为条件分支已经保证了 replaceWindow 非空。

改动 2:saveWindowState 移至 hide() 之前 ✅

src/main/windows/MainWindow.ts:266-274

// src/main/windows/MainWindow.ts:266-274
const forceReplaceCloseCurrentWindow = () => {
    if (win.isDestroyed()) {
      return;
    }
    saveWindowState(win);  // ← 在 hide() 之前保存
    forceClose = true;
    win.hide();
    win.close();
  };

分析:将 saveWindowState 调用移至 win.hide() 之前,确保 getBounds() 在窗口可见状态下获取准确的尺寸和位置。hide() 后窗口 bounds 可能不可靠,这是一个重要的时序修正。

改动 3:拆分 close 事件处理条件 ✅

src/main/windows/MainWindow.ts:373-380

// src/main/windows/MainWindow.ts:373-380
win.on('close', (e) => {
    if (forceClose) {
      return;  // 状态已在 forceReplaceCloseCurrentWindow 中保存
    }
    if (autoUpdaterService.isQuittingForUpdate()) {
      saveWindowState(win);  // 自动更新退出时窗口仍可见,此时保存
      return;
    }

分析:将 forceCloseautoUpdaterService.isQuittingForUpdate() 拆为两个独立条件,避免在 forceClose 路径下重复调用 saveWindowState(此时窗口可能已被 hide())。对于自动更新退出路径,窗口仍然可见,保存状态是安全的。逻辑拆分后语义更清晰,每条路径的保存时机都正确。


🎯 问题详细分析 · 📊 点击展开查看

📊 **👆 点击此处展开详细统计和图表分析**

📈 问题趋势分析

本次审查: 🔵⚪⚪⚪⚪ (发现 1 个轻微问题)
代码质量: █████████░ 90%
安全评分: ██████████ 100% ✅
性能评分: ██████████ 100% ✅
🏆 质量指标 📊 当前分数 🎯 目标分数 📈 改进方向
代码质量 90/100 90+ ✅ 达标
安全等级 100/100 100 ✅ 优秀
性能优化 100/100 85+ ✅ 优秀

🐛 问题清单与修复指南 · 📋 点击展开查看

📋 **👆 点击此处展开详细问题列表 (共 1 个问题) · 一键复制追踪 📌**

🚀 快速开始

⚡ 轻微级别 (Minor) 优化建议

🔵 #1 src/main/windows/MainWindow.ts:270

🏷️ 类型: 代码健壮性 | ⏰ 优先级: P2 - 后续版本 | 🎯 影响范围: 局部

  • 🔍 问题描述forceReplaceCloseCurrentWindowsaveWindowState 后立即设置 forceClose = true 并依次调用 hide()close()。如果 saveWindowState 抛出异常(虽然内部有 try-catch 保护),后续流程仍会继续,这是正确的。但如果未来 saveWindowState 的实现变更移除了 try-catchforceClose 将不会被设置,可能导致 close 事件中再次触发确认弹窗。当前实现没有问题,仅作为代码演进时的注意点。

  • 🛠️ 建议:当前 saveWindowState 内部的 try-catch {} 已经保证了健壮性,无需修改。后续如果该函数有重构,需注意保持异常安全。

  • ⚠️ 潜在影响:当前无影响,仅为防御性建议。


📊 修复优先级矩阵

🎯 优先级 🚨 严重 ⚠️ 中等 ⚡ 轻微 📈 总计
🔴 P0 0 个 - - 0 个
🟡 P1 - 0 个 - 0 个
🔵 P2 - - 1 个 1 个
📊 合计 0 0 1 1 个

🎯 合并决策建议

📋 合并评估结果

📊 评估维度 ⭐ 得分 🎯 状态 💭 说明
🔐 安全性 10/10 ✅ 通过 无安全风险,纯逻辑修正
📊 代码质量 9/10 ✅ 优秀 修复精准,代码清晰可读
⚡ 性能影响 10/10 ✅ 无影响 无新增开销
🛠️ 功能完整性 9/10 ✅ 完整 覆盖了所有关闭路径
🧪 测试覆盖 7/10 ⚠️ 基本 Electron 主进程逻辑难以自动化测试,建议手动验证

📈 综合评分: 45/50 分 · 等级: 优秀


🚦 最终建议

建议合并

📝 详细理由

  • 🟢 根因分析准确,修复精准针对可选链 ?.null 值上的假值陷阱
  • 🟢 三处改动互相配合,形成完整的修复方案:条件修正 + 保存时序修正 + 路径拆分
  • 🟢 saveWindowState 移至 hide() 前调用,确保 getBounds() 数据可靠
  • 🟢 无安全风险,无性能影响,变更范围可控
  • 🟢 PR 描述清晰,根因分析和修复思路一目了然

🎯 具体行动建议

  1. 合并前建议手动验证

    • 正常关闭应用 → 重新打开 → 确认窗口尺寸和位置已恢复
    • 最大化后关闭 → 重新打开 → 确认恢复为最大化状态
    • 自动更新退出场景下窗口状态也能正确保存(如有条件)
  2. 后续版本改进

    • 考虑为窗口状态保存/恢复添加日志,便于排查类似问题

@J3n5en J3n5en merged commit a1f22e9 into J3n5en:main Apr 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants