Skip to content

fix(macos): 恢复隐藏标题栏窗口的原生全屏能力 - #1410

Merged
1lck merged 6 commits into
1lck:previewfrom
zher52:fix/macos-native-fullscreen-space
Oct 22, 2026
Merged

1lck merged 6 commits into
1lck:previewfrom
zher52:fix/macos-native-fullscreen-space

Conversation

@zher52

@zher52 zher52 commented Oct 22, 2026

Copy link
Copy Markdown

问题

macOS 主窗口(项目工作台、欢迎页、独立文件窗口)点击绿色交通灯按钮后,不会进入 macOS 标准的独立全屏 Space,而是在当前桌面上直接最大化;再次点击也无法还原到进入前的位置。从源码构建的 debug 版本与 v0.6.3 预编译版本均能复现。

根因

通过进程内探针(应用自动点击绿色按钮并记录窗口状态)定位到三个事实:

  1. 窗口的 collectionBehavior 含 fullScreenNone(实测原始值 0x10200 = FullScreenNone | Primary)。全仓库源码历史从未设置过该标志,它来自 SwiftUI 对 .windowStyle(.hiddenTitleBar) 窗口的默认行为,随构建工具链/SDK 版本变化——这解释了较早版本正常、近期构建异常。
  2. 绿色按钮的 target/action 在窗口创建时被 AppKit 固化为私有的 _setNeedsZoom:(最大化),而不是全屏窗口应有的 toggleFullScreen:;collectionBehavior 事后变化不会触发按钮 action 重新求值。
  3. 只要 fullScreenNone 还在,NSWindow.toggleFullScreen(_:) 会被静默拒绝。

探针前后对比:

阶段 点击后 frame styleMask 含 .fullScreen
修复前 {0, 85, 2560, 1330}(铺满可视区域=最大化) 否
修复后 {0, 0, 2560, 1440}(整屏=原生全屏 Space) 是

修复方案

RootView 挂载 WindowFullScreenCapabilityGuard(NSViewRepresentable),宿主视图 WindowFullScreenCapabilityView 做三件事:

  • viewDidMoveToWindow 时订阅宿主窗口的 NSWindow.didUpdateNotification;
  • 每个窗口 update 周期剥离 SwiftUI 反复重设的 fullScreenNone 并补上 fullScreenPrimary(SwiftUI 会在场景更新中反复重设,一次性归一化实测无效);
  • 剥离的同时把绿色按钮重接到 zoomButtonClicked(_:)——先剥离标志位再调用 toggleFullScreen(_:),消除"点击瞬间标志位又被设置"的竞争。

由于三类主窗口共用 RootView,欢迎页、项目工作台与独立文件窗口一并覆盖;设置窗口与软件更新窗口不经过 RootView,不受影响。

改动清单

  • macos/Sources/Lithe/Views/App/RootView.swift:新增 WindowFullScreenCapabilityGuard / WindowFullScreenCapabilityView(+68 行),RootView body 挂载一行。
  • macos/Tests/LitheTests/WindowFullScreenCapabilityGuardTests.swift:新增 3 个确定性测试——安装即剥离、按钮重接校验、SwiftUI 重设后经 didUpdateNotification 再次剥离(同步触发,无轮询、无实时等待)。
  • shared/platform-feature-matrix/features/window-controls.json:能力描述明确"绿色按钮可进入原生全屏 Space(而非主桌面最大化)",macOS evidence 补充新测试,验证动作补充全屏 Space 检查。
  • .agents/notes/implemented/bug-fix/2026-10-21-macos-hidden-titlebar-native-fullscreen.md:新增 Agent Note,记录根因证据、被否方案(仅 coordinator 归一化 / 不重接按钮 / 放弃 hiddenTitleBar)与验证方式。

验证

  • 进程内探针验证:修复前点击绿色按钮为主桌面最大化且 toggleFullScreen 被拒;修复后点击直接进入原生全屏 Space(fullScreen=true、styleMask=49167 含 .fullScreen、frame 覆盖整屏)。
  • ./scripts/verify-agent-notes.sh、./scripts/verify-platform-feature-matrix-change.sh origin/preview HEAD、./scripts/verify-platform-feature-matrix.sh、./.agents/skills/write-stable-tests/scripts/verify-test-stability.sh 全部通过。
  • 说明:验证机为 Swift 6.2.4 工具链(仓库固定 6.3.3),部分既有测试文件在旧编译器上类型检查超时,单元测试未能在本地执行,需以 CI 结果为准;修复代码本身已在本地构建并完成上述行为验证。

风险评估

  • didUpdateNotification 中的 contains(_:) 位检查开销可忽略。
  • 绿色按钮 target/action 被接管到守卫视图;视图与按钮共享窗口生命周期,非零引用的 NSButton.target 不会悬垂,option 点击的窗口缩放仍由按钮子类在 mouseDown 中处理。
  • 若未来 SwiftUI 不再设置 fullScreenNone,守卫的检查自然变为空操作。

@ghfind-review ghfind-review Bot added the review: low ghfind author score; see https://ghfind.com label Oct 22, 2026
@zher52
zher52 force-pushed the fix/macos-native-fullscreen-space branch from 989b96c to a298493 Compare October 22, 2026 03:00
@xiaoyumuxi
xiaoyumuxi marked this pull request as ready for review October 22, 2026 03:21
@lithe-code-review

lithe-code-review Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⚠️ 有建议
范围: preview ← fix/macos-native-fullscreen · head: a298493

未发现需要阻塞合并的问题。

Scope Check

  • 关联 Issue 或需求是否完成:未找到可验证的关联 Issue;按 PR 描述审查恢复主窗口绿色按钮的原生全屏行为。
  • 已完成范围:在 RootView 挂载窗口能力守卫,更新窗口能力矩阵,并新增守卫行为测试与决策笔记。
  • 未完成范围:设置窗口与软件更新窗口不经过 RootView,不受此改动覆盖;给定需求未说明需要覆盖它们。
  • 是否复用了已有上游能力,或对自研边界给出充分证据:改动使用 AppKit 的窗口全屏能力与 toggleFullScreen(_:),未新增 IDE 领域逻辑。
  • 每个 Finding 是否由本次变更引入或加剧:无 Findings。
  • 是否属于本次变更职责范围:是,改动集中于 macOS 主窗口全屏交互及其验证记录。

Suggestions

  1. 请在 PR 描述或评论中补充受影响窗口的前后截图,展示普通窗口、进入原生全屏 Space 及退出后的状态。当前上下文没有截图,视觉效果尚未独立核验。

Summary

静态检查未发现明确的代码回归。守卫会在窗口挂载时处理 fullScreenNone,并在窗口更新通知及按钮 action 中再次处理;测试覆盖了标志清理、按钮重接和通知后的再次清理。

Verification

  • 实际检查:指定 base...head 的四个变更文件;追踪了 RootView 到主窗口场景的挂载路径,并检查新增测试、矩阵记录和 Agent Note。
  • 已提供结果:head 检查中 macOS CI gate、Agent Notes 验证和平台矩阵验证成功;Swift tests 为 skipped。PR 描述另报告本地探针与测试稳定性静态检查通过,这些结果未在本次独立核验。
  • 本次未运行代码或测试,符合只读审查范围。
  • 非阻塞验证缺口:没有受影响界面的截图;提供的检查结果未包含 Swift 测试执行。

问题:当前 macOS 工具链下,SwiftUI 为 .hiddenTitleBar 窗口默认标记
collectionBehavior.fullScreenNone,绿色交通灯按钮因此退化为普通最大化
(action 被固化为 _setNeedsZoom:),toggleFullScreen(_:) 也被静默拒绝,
主窗口无法进入独立全屏 Space。源码构建版本与 v0.6.3 发布版本均复现。

修复:RootView 挂载 WindowFullScreenCapabilityGuard,通过
NSWindow.didUpdateNotification 持续剥离 SwiftUI 反复重设的
fullScreenNone 并补上 fullScreenPrimary;同时把绿色按钮重接到
zoomButtonClicked(_:)(先剥离再 toggleFullScreen),消除标志位竞争。
欢迎页、项目工作台与独立文件窗口共用 RootView,一并覆盖。

- 新增 WindowFullScreenCapabilityGuardTests 覆盖剥离、按钮重接与
  SwiftUI 重设后的再次剥离(同步触发,无轮询)
- 更新 window-controls 平台能力矩阵:明确绿色按钮应进入原生全屏 Space
- 新增 Agent Note 记录根因、备选方案与验证方式
进入原生全屏后 macOS 会隐藏红绿灯按钮,但顶栏仍按普通窗口预留
WorkbenchTopBarMetrics.leadingInset(83pt),左上角出现一段空白。

新增 WindowFullScreenStateReader:复用 NSWindow.didUpdateNotification
机制把窗口全屏状态同步到 SwiftUI @State(无轮询),顶栏在全屏态将
leading padding 降为 0,退出全屏自动恢复预留,行为对齐 IDEA。

新增 WindowFullScreenStateReaderTests 覆盖进入/退出全屏的状态同步
(同步触发,无实时等待);同步更新 window-controls 能力矩阵验证动作
与全屏 Agent Note。
问题一:欢迎页全屏后打开项目,工作台只显示为居中的 1440×900,四周黑屏。
原因:布局切换的换框 setFrame 落在原生全屏窗口上。现在全屏期间跳过换框
(内容继续填满屏幕),退出全屏时补一次换框落到当前布局的正常尺寸;布局
未变的正常退出保持"恢复进入全屏前尺寸"的行为不变。已实测确认修复。

问题二:全屏相关测试直接写 styleMask 的 .fullScreen 位,AppKit 在真实
全屏过渡之外抛 NSGenericException,CI 的 macOS 测试进程整体崩溃。为
WindowFullScreenStateReader 与 LitheWindowCoordinator 增加可注入的全屏
判定接缝(默认行为不变),测试改用注入桩值驱动状态,不再触碰受保护
标志位。

新增 WindowLayoutFullScreenTransitionTests 覆盖全屏期间换框延迟与未变
布局退出不换框;既有 reader 测试改为注入式驱动。
@zher52
zher52 force-pushed the fix/macos-native-fullscreen-space branch from 881a364 to 43f3ac9 Compare October 22, 2026 05:15
zher52 and others added 2 commits October 22, 2026 16:52
CI 的虚拟显示器只有 1024pt 宽,applyFrame 的屏幕钳制会把
1440pt 的目标宽度缩到 1000pt,导致
layoutTransitionWhileFullScreenDefersFrameSwapUntilExit 断言
window.frame.width == workspaceContentSize.width 失败。改为用
LitheWindowLayout.frame(_:fitting:) 按 applyFrame 相同的规则
计算期望宽度,无可见屏幕时不钳制,保证测试在不同显示尺寸下
结果一致。

@xiaoyumuxi xiaoyumuxi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

跨 PR 审查(head dca90cd4)

结论:暂不建议合并;最新 head 的 macOS CI 仍为失败。 对应工作流。

读取 Swift tests job 日志后,失败位置在 SearchEverywhereBehaviorTests.swift:844-845 的 providerAndExclusionChangesKeepTheNativeFilterAnchorAlive(scheme:)(dark case,2 个断言),并有 1 个 Swift 测试超过本地时长门限;Run Swift unit tests 失败,macOS CI gate 因此失败。这些失败不在此次全屏 PR 的直接修改范围,暂不能归因于本次改动。 请在当前 head/基线对照复测并明确记录处置,不要因为打包 job 绿色而判定 CI 通过。

源码中全屏能力守卫、绿色按钮 action 重接及全屏退出后的窗口 frame 恢复路径已覆盖主要场景。另建议实机验证主窗口、Welcome 和独立编辑器窗口的进入/退出全屏、窗口按钮焦点与非全屏最大化行为,特别是在切换工作区布局期间进出全屏;同时解决当前 mergeable=false 的目标分支同步问题。

本次未在 macOS 上进行实机窗口操作。

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

跑过了的话记得艾特我,我给你合入一下

@1lck
1lck merged commit 3926e23 into 1lck:preview Oct 22, 2026
18 checks passed
@zher52
zher52 deleted the fix/macos-native-fullscreen-space branch October 23, 2026 02:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: low ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants