跨端: 全屏是**窗口级**的 —— 光给 MainPage 让位,等于把黑边换成顶栏压字

上一提交(cac026e)把黑边消掉了,但**只给 `MainPage` 加了避让**。
实测截图硬证:写邮件页的「取消」与时钟「09:49」重叠、「发送」与 wifi/电量图标重叠。

## 根因:`setWindowLayoutFullScreen(true)` 不只作用于当前页

它是**窗口级**的:一旦设上,这个窗口里**所有**用 `router.pushUrl` 推上来的页
(写邮件/会话/收件箱/邮件详情/用户管理)都从 y=0 开始画。

所以上次那个错与更早那次(6861934 只删全屏不留避让)**同源**:
都是"同一件事只做了一半"。上次少的是**步骤**,这次少的是**页面**。

## 改法:把"消费避让"变成每个 @Entry 页都得做的事

- `model/WindowInsets.ts` 加 `topInset(insets)`:取 0 时(未全屏/取不到)
  表达式的值与旧代码**逐字相同** ⇒ 没全屏的环境行为不变,不会把谁顶下去。
- 五个页各按自己的形状让位:
  - 固定 56vp 顶栏(写邮件/会话/收件箱/用户管理):
    `height(56 + topInset(...))` **与** `padding(… top: topInset(...))` 一起加 ——
    只加 padding 会把固定的 56 切掉 39(按钮压扁),只加 height 则内容仍贴 y=0。
  - 满高容器(邮件详情):`padding({ top })` 加在 `@Entry` 包装层。
    ★ **不能加在 `MailDetailView` 里面**:它同时被 `MainPage` 的 Navigation 内嵌复用,
      而那层已经让过位了 —— 加在里面就变成让两次(39vp 变 78vp)。
      这类"同一组件两种入口"的坑与"悬浮加号要放在 Navigation 内部"同源:
      **让位的量取决于它被挂在哪一层**。

## 判据(harmony-window 8 → 9 条)

接线⑤ 枚举**所有** `@Entry` 页并要求它们消费避让 —— 口径是
"有人在窗口上开了全屏 ⇒ 每个 @Entry 页都得让",而不是"检查 MainPage 做了没有"。
后者在新增一个推上来的页时会静默逃掉,而"新增一个页"正是最常发生的事。

豁免要带**可机器复核**的理由(不再是"这个页先不管"):
- `Index.ets`:DevEco 模板欢迎页,不在 `main_pages.json` 流程里;
- `LoginPage.ets`:根容器 `.align(Alignment.Center)` ⇒ 结构上碰不到 y=0。
  但"居中"是可能被改掉的性质 ⇒ 判据**断言那个居中写法仍然存在**,
  谁把它改成贴顶,这条先红,逼他回来重新想这个页要不要避让。

**变异自检两个方向都跑过**:
- 把 ComposePage 避让整个拿掉(重演"只给 MainPage 加")⇒ 红 ✓
- 只加 padding 不加 height(会压扁按钮)⇒ 红 ✓

★ 期间还修掉两处"判据锚在当时的字符串上"(不是放宽,是它把"加一个正当的避让"
  与"真犯那个错"判得一模一样):
  - `harmony-nav` ④ 的留白断言、`harmony-widescreen` ④ 的 navReserve 断言,
    都改成剥注释后验**形状与不变量**,而不是写死字面表达式。改完变异仍咬得住。
This commit is contained in:
2026-09-18 10:34:00 +08:00
parent cac026e9e2
commit a87a88ea2a
8 changed files with 218 additions and 8 deletions

View File

@ -20,6 +20,7 @@ import {
mailReplyTarget,
formatAddress
} from '../model/ReplyTarget';
import { Insets, KEY_WINDOW_INSETS, topInset } from '../model/WindowInsets';
/**
* 本地时间:与 WebUI `new Date(x).toLocaleString('zh-CN')` 同一口径。
@ -553,10 +554,24 @@ export struct MailDetailView {
@Entry
@Component
struct MailDetailPage {
/**
* 窗口避让区 —— 由 `EntryAbility.setupFullScreenWindow` 写进 AppStorage。
*
* ★ 避让加在**这个 `@Entry` 包装层**,**不在** `MailDetailView` 里面:
* 同一个 `MailDetailView` 还被 `MainPage` 的 `Navigation` 内嵌复用
* (`MailDetailDestination`),而 `MainPage` 已经给内容层加过避让了 ——
* 加在里面就变成**让两次**(39vp 变 78vp,白白多一条空档)。
* 这类"同一个组件两种入口"的坑与 `harmony-nav` 里那条"悬浮加号要放在
* Navigation 内部、不能当它的兄弟"同源:**让位的量取决于它被挂在哪一层**。
*/
@StorageLink(KEY_WINDOW_INSETS) windowInsets: Insets = new Insets();
build() {
Column() {
MailDetailView()
}
.width('100%').height('100%')
.padding({ top: topInset(this.windowInsets) })
.backgroundColor(Theme.surface)
}
}