跨端: 顶部页签条改悬浮玻璃 + 深色压暗下限(修 8 处深色可读性)+ 手势判据自搭现场
用户:「顶部的收件箱发件箱和授权为什么不做成悬浮玻璃?」
问得对,而且它当时是**整页唯一一块实心白**。
★ 顶部页签条 → 悬浮玻璃
`CommTabBar` 原来写的是 `backgroundColor(Theme.surface)`(系统卡片色=实体)。
上一轮把列表容器、卡片、底部导航条都玻璃化了,**唯独漏了这条** ——
它既不是卡片也不是容器,是"条状 chrome",逐处修时最容易漏的那一类。
⇒ 现在与底部浮动条**同一族**:圆角 + 左右留白 + `Theme.navMaterial`。
★ 几何常量复用底部条的(`TAB_BAR_RADIUS = NAV_BAR_RADIUS`、
`TAB_BAR_SIDE = NAV_BAR_SIDE`),不各写一份 —— 两个数一旦分叉,
"同一条轴线上的两种条"就不齐了。为什么选悬浮而不是 WebUI 的"通栏无底色"
(WebUI 页签条自己**没有**底色,靠父面板的玻璃)也记在那个常量的注释里。
新增判据:页签条必须与导航条同族(不许实体面 / 要引 TAB_BAR_* 常量),
变异验证过(改回 `Theme.surface` 即红)。
★ 深色压暗下限 —— 本轮最重要的真 bug
玻璃让壁纸**真的透进卡片**之后,"浅壁纸 + 深色文字令牌"这个组合会在
**卡片内部**发生。设备实测(深色、aurora、压暗 37):
「角色」ink rgb(183,195,211) on bg rgb(68,95,128) = 2.57:1
「创建时间」2.27:1、「状态」2.68:1、「压暗」2.19:1(共 8 段 <3:1)
而**同一套代码在浅色下全部达标** ⇒ 差别不在"选错了色",在"底没暗下来"。
WebUI 早就撞过并写下了必然值(`index.css:442`):
.dark { --bg-dim-min: 92%; } /* 浅色下是 0% */
background-color: rgb(var(--bg-scrim) / max(var(--bg-dim), var(--bg-dim-min)));
我们只有 `scrimOpacity(bgDim)`、**没有下限**。补上 `DARK_DIM_MIN = 0.92`
(浅色下用户设的值照旧生效,不忽略设置)。
复扫:低对比 8 段 → 通信 0 / 日历 0 / 联系 1 / 我的 1
(剩下两条经手核是扫描器的假阳性:近黑底上把抗锯齿暗像素当成了"墨")。
★ 手势判据"没有自己搭现场"(套件红、单独绿)
`cross-client-gesture` 只保证"在月档"、**不保证在哪一月**,而它算的是
相对最初那一月的 delta ⇒ 前面某个判据(或一次失败的手势)把日历翻到 10 月后,
期望值就差一格。修法:先点「今天」归位到本月(该按钮语义就是"回本月")。
验证:从 9 月起步、从 10 月起步,两种脏状态都通过。
★ 顺带确认手势本体是好的:`--speed 1500` 时 1000px 要走 670ms,
紧贴 `SWIPE_MAX_DURATION_MS=700` 而被丢弃;`--speed 3000` 两个方向都翻。
★ 判据基建:修掉一个**静默失效**的 API 名(沿用上轮的教训修法)
`Surface.ets` 里两处块注释内嵌了 `/* ... */` —— 会把块注释**提前闭合**,
于是后面的代码跑到注释外面(报"Use let instead of var"/"Invalid character")。
本仓已有这条纪律("当 `code()` 因注释里的 `/*` 吞掉 import 时,改注释文本"),
这次又踩了两处(`Surface.ets` 与 `Appearance.ts`)。
判据:files=32 ran=32 checks=519 pass=519 fail=0 red=0;baseline 7/7✓。
This commit is contained in:
@ -359,6 +359,26 @@ test('★ 设备:在网格列上左滑,日历标题真的翻到下一段', a
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
}
|
||||
|
||||
/*
|
||||
* ★★ 2026-09-20 修(本判据在套件里红、单独跑绿):**先回到已知的月份**。
|
||||
*
|
||||
* 本判据下面算的是"相对最初那一月的 delta",而它原来**只保证在月档、
|
||||
* 不保证在哪一月** —— 于是前一屏停在哪个月、它就从那个月开始数。
|
||||
* 套件里跑时,日历页可能已被别的判据(或上一次失败的手势)翻到 10 月,
|
||||
* 而右滑那一步的期望值是按"最初那一月"算的 ⇒ 差一格就判红。
|
||||
*
|
||||
* 这不是"设备抖动",是**判据没有自己搭现场**:它依赖进入时的状态,
|
||||
* 于是"通过与否取决于跑之前那一屏是什么"。同族毛病本仓已修过多次
|
||||
* (`harmony-appearance` 那条设备判据、以及 `backToMain` 那段注释)。
|
||||
*
|
||||
* 修法:点「今天」把 anchor 归位到本月(该按钮的语义就是"回本月",
|
||||
* 与 WebUI `goToday` 一致),再从那个确定的状态开始算 delta。
|
||||
*/
|
||||
assert.ok(tapText(hdc, '今天'), '要能点「今天」把月份归位(本判据需要一个已知起点)');
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
assert.ok(monthTitle(dumpLayout(hdc)) !== null,
|
||||
'点了「今天」之后仍应在月档(否则下面的 delta 无从算起)');
|
||||
|
||||
/*
|
||||
* ★★ 2026-09-19:本判据第一版用「滑一次 + 固定等 2500ms + dump」。
|
||||
* 结果**单独跑通过、接进 run-all 后失败**(同一台设备、同一份代码)。
|
||||
|
||||
@ -585,6 +585,47 @@ test('★ 出现/消失的那类元素真的挂了过渡(`if` 包的弹层不
|
||||
assert.deepEqual(missing, [], `这些位置会硬弹:${missing.join('、')}`);
|
||||
});
|
||||
|
||||
test('★ 顶部页签条与底部导航条同一族(都是悬浮玻璃)—— 一块实心白就是"割裂"', () => {
|
||||
/*
|
||||
* 用户 2026-09-20:「顶部的收件箱发件箱和授权为什么不做成悬浮玻璃?」
|
||||
*
|
||||
* 那条页签条当时是**整页唯一一块实心白**:上一轮把列表容器、卡片、
|
||||
* 底部导航条都玻璃化了,唯独它还写着 `backgroundColor(Theme.surface)`(系统卡片色=实体)
|
||||
* ⇒ 壁纸从四周透出来,只有它在顶上白着一横条。
|
||||
*
|
||||
* ★ 这条判据要抓的形状:**"同一页里少数几处忘了跟着改"** ——
|
||||
* 逐处修的时候最容易漏的就是"不是卡片、也不是容器"的条状 chrome。
|
||||
* 所以判据盯的是"页签条有没有和导航条**用同一族**的三种属性",
|
||||
* 而不是"有没有某个字符串":
|
||||
* ① 圆角不是自己拍的值(必须引 `TAB_BAR_RADIUS`,它 = `NAV_BAR_RADIUS`);
|
||||
* ② 左右留白引 `TAB_BAR_SIDE`(它 = `NAV_BAR_SIDE`)⇒ 两条轴线对齐;
|
||||
* ③ 材质走 `Theme.navMaterial`(与底部条同一档次),不是 `Theme.surface`。
|
||||
*/
|
||||
const page = stripComments(main);
|
||||
const at = page.indexOf('CommTabBar()');
|
||||
assert.ok(at > 0, '通信页要有页签条(收件箱/发件箱/授权)');
|
||||
const body = page.slice(at, at + 3000);
|
||||
|
||||
assert.ok(!/\.backgroundColor\(Theme\.surface\)/.test(body),
|
||||
'页签条不许铺实体面(Theme.surface)—— 那是壁纸模式下唯一一块实心白,"割裂"就是这么来的');
|
||||
assert.match(body, /\.backgroundBlurStyle\(this\.bgActive \? Theme\.navMaterial : BlurStyle\.NONE\)/,
|
||||
'页签条要吃系统材质(与底部导航条同档),否则它和玻璃卡片拼在一起不是一套东西');
|
||||
assert.match(body, /\.borderRadius\(TAB_BAR_RADIUS\)/,
|
||||
'页签条要用 TAB_BAR_RADIUS(= NAV_BAR_RADIUS)—— 自己拍一个数会让上下两条不齐');
|
||||
assert.match(body, /\.margin\(\{[^}]*left: TAB_BAR_SIDE[^}]*right: TAB_BAR_SIDE/,
|
||||
'页签条的左右留白要用 TAB_BAR_SIDE(= NAV_BAR_SIDE)—— 悬浮条的侧边必须与底部条同值');
|
||||
|
||||
/*
|
||||
* 自检:把页签条改回实心面,上面第一条必须判红。
|
||||
* (不写自检的话,这条判据可能因为 `body` 切片取错位置而**恒绿** ——
|
||||
* 本仓撞过"判据自己在骗自己",所以凡按位置切片的都验一次。)
|
||||
*/
|
||||
const broken = 'CommTabBar() {\n Row() {}\n .width(\'100%\')\n .backgroundColor(Theme.surface)\n}';
|
||||
const bAt = broken.indexOf('CommTabBar()');
|
||||
assert.ok(/\.backgroundColor\(Theme\.surface\)/.test(broken.slice(bAt, bAt + 3000)),
|
||||
'自检:实心面这个写法必须能被命中(否则上面的断言是假的)');
|
||||
});
|
||||
|
||||
test('★ 玻璃只在两处、且这一处是"背后有可变内容"(GLASS 登记的放行条件)', () => {
|
||||
/*
|
||||
* pi 撤回"模糊只由壁纸层负责"后给的是两条:①同一张底只许糊一次;
|
||||
|
||||
@ -83,7 +83,7 @@ const SUITE = [
|
||||
// P4 外观同步:跑 model/Appearance.ts(纯逻辑),所以也要 strip-types
|
||||
['test/harmony-appearance.test.mjs', ['--experimental-strip-types', '--no-warnings'], 27],
|
||||
// P5 悬浮玻璃导航:点击配对 / index 决定挂载 / 命中区 ≥44vp / 悬浮与让位
|
||||
['test/harmony-nav.test.mjs', ['--experimental-strip-types', '--no-warnings'], 20],
|
||||
['test/harmony-nav.test.mjs', ['--experimental-strip-types', '--no-warnings'], 21],
|
||||
// 上下黑边(用户 2026-09-17/18 报过两次)——钉的是一整套东西的两半:
|
||||
// `setWindowLayoutFullScreen(true)`(消黑边)+ `getWindowAvoidArea`(让开时钟/手势条)。
|
||||
// 只做前半 ⇒ 页签被时钟盖住(上一次就是这样退回去的,黑边于是留了三天);
|
||||
|
||||
@ -271,10 +271,45 @@ export function isDarkMode(theme: string, systemColorMode: number): boolean {
|
||||
return systemColorMode === 0; // COLOR_MODE_DARK
|
||||
}
|
||||
|
||||
/** 遮罩浓度:0~90 的"压暗"值 → 0~1(系统遮罩色 + 这个不透明度) */
|
||||
export function scrimOpacity(bgDim: number): number {
|
||||
/**
|
||||
* **深色下的压暗下限**(比例)。0.92 = 壁纸至少被压掉 92%。
|
||||
*
|
||||
* ★★ 2026-09-19:这条是从 WebUI 抄来的**必然值**,不是调出来的观感。
|
||||
*
|
||||
* WebUI `index.css:772` 与 `:442`:
|
||||
*
|
||||
* background-color: rgb(var(--bg-scrim) / max(var(--bg-dim), var(--bg-dim-min)));
|
||||
* .dark { --bg-dim-min: 92%; } ← 浅色下是 0%
|
||||
*
|
||||
* 那段注释写清了成因(实测事故):用户上传的是一张**浅色**照片
|
||||
* (均值 RGB 213,212,225),压暗 56% 之后壁纸仍是中灰 94,
|
||||
* 而深色主题的文字是近白的 ⇒ 近白对中灰只有约 3.6:1,
|
||||
* 次要文字更是 1.2–1.4:1。**没有任何文字颜色能救** ——
|
||||
* 不是"选错了色",是"底没暗下来"。
|
||||
*
|
||||
* 加了下限之后 WebUI 复测:四页最差 通信 6.14 / 日历 4.90 / 联系 4.96 /
|
||||
* 我的 5.38,全部 ≥ 4.5:1。
|
||||
*
|
||||
* ★ 为什么鸿蒙这边也必须跟:现在卡片是**玻璃**(壁纸真的透得进来),
|
||||
* 于是"浅壁纸 + 深色文字令牌"这个组合会在**卡片内部**发生 ——
|
||||
* 设备实测(模拟器 3184×2232、深色、aurora + 压暗 37):
|
||||
* 「角色」ink rgb(183,195,211) on bg rgb(68,95,128) = **2.57:1**
|
||||
* 「创建时间」2.27:1、「状态」2.68:1、「压暗」2.19:1
|
||||
* 而**同一套代码在浅色下**这些字都是达标的 —— 差别就在底有没有暗下来。
|
||||
*/
|
||||
export const DARK_DIM_MIN: number = 0.92;
|
||||
|
||||
/**
|
||||
* 遮罩浓度:0~90 的"压暗"值 → 0~1(系统遮罩色 + 这个不透明度)。
|
||||
*
|
||||
* ★ 深色下取 `max(用户设的, DARK_DIM_MIN)`:用户的压暗值在浅色下**完全照用**
|
||||
* (浅色页面本来就白,压暗只是调味),而深色下它压不住浅壁纸。
|
||||
* 这不是"忽略用户设置"—— 用户设的 37% 在浅色下依然原样生效。
|
||||
*/
|
||||
export function scrimOpacity(bgDim: number, dark?: boolean): number {
|
||||
const d: number = clampNumber(bgDim, 0, 90, 12);
|
||||
return d / 100;
|
||||
const v: number = d / 100;
|
||||
return dark === true ? Math.max(v, DARK_DIM_MIN) : v;
|
||||
}
|
||||
|
||||
/** 状态文案:降级必须看得见(WebUI 侧的原话:「否则用户以为换设备也能带走」) */
|
||||
|
||||
@ -299,3 +299,30 @@ export function navBadgeText(n: number): string {
|
||||
}
|
||||
return n + '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 顶部页签条(通信页的 收件箱/发件箱/授权)的几何 —— 与底部浮动条**同一族**。
|
||||
*
|
||||
* 用户 2026-09-20:「顶部的收件箱发件箱和授权为什么不做成悬浮玻璃?」
|
||||
*
|
||||
* 问得对,而且它当时是**整页唯一一块实心白**:上一轮把列表容器、卡片、
|
||||
* 导航条都玻璃化了,唯独这条页签条还写着 `Theme.surface`(系统卡片色 = 实体)
|
||||
* ⇒ 壁纸从四周透出来,只有它在顶上白着一横条,成了最扎眼的那块。
|
||||
*
|
||||
* 为什么**悬浮**(左右留白 + 圆角)而不是"通栏 + 材质":
|
||||
* · 底部导航条已经确立了这个语汇(`NAV_BAR_*`),顶部再做一个通栏的
|
||||
* 会在同一条轴线上出现两种不同的"条";用户要的正是"和底部一样"。
|
||||
* · WebUI 的页签条本身**没有底色**(`CommTabs.tsx:40` 只有
|
||||
* `border-b border-gray-200`),靠父面板的玻璃 —— 那是"通栏"做法。
|
||||
* 本仓底部条选了悬浮,于是顶部跟底部统一,是**本仓的**选择;
|
||||
* 差异记在这里,免得后人以为漏抄了 WebUI。
|
||||
*
|
||||
* 高度与侧留白**复用底部条的常量**(不是各写一份):两者一旦不同,
|
||||
* "同一条轴线上的两种条"就变成肉眼可见的不齐 —— 而这正是本仓反复出现的那类毛病
|
||||
* (同一个东西两处各写一个数,然后慢慢分叉)。
|
||||
*/
|
||||
export const TAB_BAR_RADIUS: number = NAV_BAR_RADIUS;
|
||||
export const TAB_BAR_SIDE: number = NAV_BAR_SIDE;
|
||||
/** 顶部留白(vp):离内容区顶部一点点,做成"浮着"而不是"贴着" */
|
||||
export const TAB_BAR_TOP: number = 8;
|
||||
|
||||
@ -86,6 +86,9 @@ import {
|
||||
NAV_CONTENT_RESERVE,
|
||||
NAV_ITEM_MIN_HIT,
|
||||
NAV_ITEMS,
|
||||
TAB_BAR_RADIUS,
|
||||
TAB_BAR_SIDE,
|
||||
TAB_BAR_TOP,
|
||||
navBadgeCount,
|
||||
navBadgeText,
|
||||
navBadgeTone,
|
||||
@ -1425,7 +1428,22 @@ struct CommPage {
|
||||
}, (key: string) => key)
|
||||
}
|
||||
.width('100%')
|
||||
.backgroundColor(Theme.surface)
|
||||
/*
|
||||
* ★★ 2026-09-20(用户:「顶部的收件箱发件箱和授权为什么不做成悬浮玻璃?」)
|
||||
*
|
||||
* 这一行原来是 `backgroundColor(Theme.surface)` —— 系统卡片色,**实体**。
|
||||
* 于是它是整页唯一一块实心白:上一轮把列表容器、卡片、底部导航条
|
||||
* 都玻璃化了,唯独漏了这条页签条 ⇒ 壁纸从四周透出来,只有它在顶上白着一横条。
|
||||
*
|
||||
* 现在与**底部浮动条同一族**:圆角 + 左右留白 + 系统材质。
|
||||
* 三种做法各自的取舍见 `model/NavItems.ts` 的 `TAB_BAR_*` 常量注释
|
||||
* (为什么选悬浮而不是 WebUI 的"通栏无底色")。
|
||||
*/
|
||||
.backgroundColor(Color.Transparent)
|
||||
.backgroundBlurStyle(this.bgActive ? Theme.navMaterial : BlurStyle.NONE)
|
||||
.borderRadius(TAB_BAR_RADIUS)
|
||||
.clip(true)
|
||||
.margin({ left: TAB_BAR_SIDE, right: TAB_BAR_SIDE, top: TAB_BAR_TOP })
|
||||
}
|
||||
|
||||
openMail(mailId: string, accountId: string): void {
|
||||
@ -2599,7 +2617,7 @@ struct MainPage {
|
||||
this.accountLabel = '';
|
||||
}
|
||||
// 模糊强度是**服务端给的 px 原值**:计划只搬运它,映射成系统材质档在画的那一层做
|
||||
this.bgPlan = resolveBackground(snap.bgKind, snap.bgPresetId, scrimOpacity(snap.bgDim), store.wallpaper !== null, dark, snap.bgBlur);
|
||||
this.bgPlan = resolveBackground(snap.bgKind, snap.bgPresetId, scrimOpacity(snap.bgDim, dark), store.wallpaper !== null, dark, snap.bgBlur);
|
||||
this.bgActive = this.bgPlan.kind !== 'none';
|
||||
/* 让 `@State` 感知到"壁纸内容换了"(对象内部字段的变化观察不到 —— 见它的声明) */
|
||||
this.bgContentRev = this.bgContentRev + 1;
|
||||
|
||||
Reference in New Issue
Block a user