窗口外观
Mica 背景
窗口不使用 Tauri 的 transparent: true。它靠旧版 DwmEnableBlurBehindWindow 让整个窗口按透明窗口合成,拖动时每次移动都更贵:模拟拖动测得超过一帧(165 Hz)的移动占 12–19%,普通 Win32 窗口为 8–10%。
现在由 windowEffects 提供 Mica,再用 DwmExtendFrameIntoClientArea 把它延伸到客户区,只让 WebView 背景透明(backgroundColor: #00000000)。边框只从左边缘向内延伸,宽度超过任何窗口,覆盖整个客户区;不用四周 -1 的写法,原因见下文的最大化。窗口本身保持不透明,拖动开销与普通窗口持平,外观不变。实现在 src-tauri/src/shell.rs 的 extend_backdrop。
Windows 10 不提供 Mica。宿主通过 RtlGetVersion 判断系统是否达到 Windows 11 的内部版本 22000;Windows 10 不延伸客户区,也不安装仅用于 Windows 11 圆角的缩放防护。页面在首次渲染前读取宿主的 mica 标记:支持时使用透明背景,否则使用随主题切换的实色背景。仅以“运行在 Tauri 内”判断透明会使 Windows 10 的桌面壁纸透入窗口;浅色背景为 #f3f3f3,深色为 #202020。
Windows 10 的保护不能仅靠 CSS 实色背景替代。在 Windows 10 22H2(19045.6456、WebView2 155、发布构建)的隔离环境中,仅恢复 extend_backdrop 对 Windows 10 的调用,系统设置窗口便只显示强调色;保留系统版本判断则显示正常。空白窗口仍响应消息,UI Automation 也能读取控件,因此须检查实际合成画面,不能仅凭窗口响应或控件树判断显示正常。
显示与隐藏
最小化、隐藏到托盘与恢复显示采用原位置的淡入淡出,不改变窗口的位置和尺寸;该窗口的 DWM 缩放过渡关闭,不影响系统其他窗口。淡入为 180 毫秒,淡出为 140 毫秒;中途反向操作从当前透明度继续,重复操作不重启动画。效果与设置中的“界面动画”一致,关闭时立即切换。
window_motion.rs 只在过渡期间设置整窗透明度,结束后移除 WS_EX_LAYERED 并停止计时线程,平时仍使用原有的窗口合成方式。计时线程以短时高精度等待推进透明度,最多保留一个待处理的帧消息,不阻塞窗口线程。WebView 在淡出结束、窗口实际隐藏或最小化后休眠;恢复时先唤醒,再开始淡入。动画从首个可处理的帧开始计时,唤醒与聚焦的耗时不会消耗动画时长。
tao 重建窗口样式时会重设扩展样式,显示最大化窗口还会短暂地隐藏后再显示。过渡期间保留分层样式与透明度,并将这组调用合并为一次可见性变化,避免闪帧、重复休眠和重复的恢复提示。
调整大小时的斜线
现象:调整窗口大小(尤其是慢慢放大)时,客户区对角线上偶尔闪过一条亮线。
原因:Windows 11 会把窗口裁成圆角。WebView2 把页面画在一个半透明的子表面上(Chromium 的分层输出窗口 Intermediate D3D Window),它伸进了窗口底部的两个圆角。窗口尺寸变化时,DWM 处理圆角裁剪的快速路径偶尔会把这种表面沿客户区对角线多混合两到三次,于是闪出一条亮线。画面本身没有被拉伸:出现亮线的帧里,固定间距的竖条纹位置与前后帧完全相同。
排查:在只含 WebView2 的最小窗口里同样能复现。逐项对比:
| 条件 | 亮线 |
|---|---|
| 圆角,WebView 铺满客户区 | 出现 |
| 窗口改成直角 | 消失 |
| WebView 底部留边 9 像素以上 | 消失 |
| 底部只留 1–6 像素 | 出现 |
| WebView 的可见区域不是矩形(例如只少左上角 1 个像素) | 消失 |
| 只裁掉圆角本来就藏住的像素(例如底角 1 像素) | 仍有少量 |
| 不把 Mica 延伸到客户区 | 出现 |
| 没有 WebView 的普通窗口,铺满半透明内容 | 不出现 |
修复:这条快速路径只用于可见区域是普通矩形的子窗口。所以窗口尺寸一开始变化(WM_WINDOWPOSCHANGING,拖动、贴靠、最大化、改 DPI 都经过这里),Argus 就给 WebView 的容器窗口设置一个只少了左上角 1 个像素的区域:那里是透明的页面背景,Mica 照常透出。DWM 改走通用路径后,整个窗口只有圆角抗锯齿边缘的几个像素差几级亮度(最大 12/255)。通用路径每帧多一点合成开销(录制状态下 DWM 的 GPU 占用约 1.1% → 1.6%),所以尺寸停止变化 300 毫秒后撤掉区域,平时不付出这份开销。实现在 src-tauri/src/shell.rs 的 guard_resize_seam。
把 WebView 底色换成不透明、或者让页面底部留出 9 像素,同样能消除亮线,但会改变外观(拖动时失去 Mica,或底部多一条空白),所以没有采用。
验证:用 Windows.Graphics.Capture 录制窗口,逐帧检查客户区对角线(亮线总在这条线上),每秒约 50 帧,与调整大小时窗口内容变化的频率相当。不设区域时每次录制有 32–48 帧出现亮线;设置后为 0,包括从左边缘拖动和快速拖动。常速拖动的录制包含区域加上和撤掉的时刻,相邻帧的整体亮度变化最大 0.04/255,没有闪烁。
标题栏
窗口不使用系统标题栏(decorations: false):顶部工具栏兼作标题栏,空白处可以拖动窗口、双击最大化,左端是 Argus 的图标与名称(开发版为“Argus 开发版”,与原来系统标题栏上的一致),右端是 Argus 自己绘制的最小化、最大化与关闭按钮,字形取自系统的 Segoe Fluent Icons(Windows 10 上为 Segoe MDL2 Assets)。系统标题栏只显示一行“Argus”,开启“在标题栏和窗口边框上显示强调色”时还会整条染成强调色,与 Mica 背景不协调。tao 对这种窗口保留系统边框的其余部分:阴影、Windows 11 圆角、拖到屏幕边缘贴靠,以及左、右、下边的缩放;顶边的缩放由 Tauri 放在页面上方的一条子窗口提供。DWM 默认还会画一像素边框(同样会染成强调色),通过 DWMWA_BORDER_COLOR 设为 DWMWA_COLOR_NONE 去掉。
Windows 11 的贴靠布局只在指针停在“最大化按钮”上时出现,系统靠 WM_NCHITTEST 返回 HTMAXBUTTON 判断。页面里的按钮位于 WebView2 内部,命中测试由 WebView2 自己回答,因此 Argus 在页面最大化按钮的位置放一个透明的原生子窗口(位于 WebView 之上)替它回答。这个子窗口接收单击(最大化或还原),悬停与按下以事件交给页面,由页面画出对应的状态。按钮的位置由页面报告(CSS 像素),窗口尺寸或 DPI 变化时按比例重新摆放。
对话框打开时遮罩盖住工具栏,三个按钮仍在遮罩之上可用,与系统标题栏一致。
验证(2026-10-12,本开发机,发布构建,100% 缩放,窗口放在最底层不激活):用 ChildWindowFromPointEx 确认最大化按钮中心处最上层的子窗口是覆盖窗口,位置与页面按钮完全重合(46×56 像素),WM_NCHITTEST 返回 HTMAXBUTTON;向它发送悬停与按下消息,页面按钮依次进入对应状态。用 Windows.Graphics.Capture 截取窗口:系统标题栏版本四边各有一像素的系统边框(深色模式下比内容亮 15 级),新版本的边缘与内容一致。调整大小的斜线检查(向右下拖动与从左边缘拖动各两轮,每轮约 200 帧)均为 0 帧。
顶端的强调色:在 Windows 11 上,DWM 用标题栏底色画 tao 在窗口顶端保留的 1 像素非客户区(边框扩展包含顶边时,这条带子还延伸到透明页面的背后,100% 缩放时共 8 像素高)。平时它画的是 Mica,与窗口其余部分一致;开启“在标题栏和窗口边框上显示强调色”后,它变成强调色,压在工具栏上方。Argus 在这个设置开启时把窗口的标题栏颜色(DWMWA_CAPTION_COLOR)设为 DWMWA_COLOR_NONE,即不填充,透出背后的 Mica;设置关闭时恢复系统默认。运行中收到系统设置变化的通知(WM_SETTINGCHANGE、WM_DWMCOLORIZATIONCOLORCHANGED)时重新判断。
两种看似可行的做法都不行:
- 去掉那 1 像素非客户区:边框四周扩展时,客户区一旦从窗口顶边开始,DWM 就在透明页面背后画出完整的标题栏,包括系统的最小化、最大化、关闭按钮,与页面自己的按钮错开重叠;开启强调色时整条标题栏都是强调色。
- 用纯色代替强调色(例如 Mica 未激活时的 #202020):Mica 激活时带着壁纸的色调,纯色带子在偏色的壁纸下很明显。实际截图中带子是 32,32,32,下方的 Mica 是 26,32,47。这张截图同时说明,显式设置的标题栏颜色会盖过强调色。
验证(2026-10-13 与 2026-10-16,本开发机,发布构建,窗口放在最底层不激活):本机未开启强调色。向窗口发送 WM_NCACTIVATE 让 DWM 按激活状态绘制(不移动焦点),再从外部设置标题栏颜色:设为红色时,窗口捕获中只有顶端 1 行变红(边框四周扩展时为 8 行),其下仍是 Mica,最大化时没有红色;设为 DWMWA_COLOR_NONE 时与默认相同,深色激活 31,32,33、浅色激活 240,244,246,未激活时为静止颜色。DWMWA_COLOR_NONE 并不等于“恢复默认”:在一个普通的系统标题栏窗口上,它去掉了标题栏的 Mica(深色激活时由 30,33,33 变为 0,0,0,浅色由 241,244,244 变为纯白),说明它是“不填充”。边框四周扩展时,去掉 1 像素非客户区的构建在右上角多出一组系统按钮(放大窗口捕获才看得清,按钮是灰色细线)。
最大化
最大化的窗口四边各超出屏幕一个边框宽度(100% 缩放时 8 像素),tao 把客户区设为显示器的工作区,页面从屏幕最顶端开始。
系统按钮:边框扩展包含顶边时(四周 -1 即是),DWM 在最大化窗口的透明页面背后画出系统的最小化、还原、关闭按钮,比页面自己的按钮高十来像素,两组字形错开叠在一起。未最大化的窗口顶端有 tao 保留的 1 像素非客户区,DWM 不画;最大化时在顶端同样留出 1 像素、8 像素甚至 32 像素的非客户区,DWM 照画不误。SetWindowThemeAttribute 的 WTNCA_* 标志没有作用;最大化时关闭 DWM 的非客户区绘制(DWMNCRP_DISABLED)能去掉按钮,但 Mica 也随之消失,激活时只剩 32,32,32 的纯色。边框改为只从左边缘(或下边缘)延伸、宽度超过窗口后,DWM 不再画这组按钮,窗口的其余像素与四周扩展时完全相同,包括 Mica 的色调;未最大化时即使去掉那 1 像素非客户区也不出现系统标题栏。
顶边拖动:工具栏是拖动区域,但页面顶部原有 4 像素的空白(.app 的上内边距)不是。未最大化时这一条正好被 Tauri 的顶边缩放子窗口盖住(命中测试为 HTTOP);最大化时那个子窗口缩成 0×0,指针贴着屏幕顶边就落在这片空白上,拖不动窗口。现在这 4 像素并入工具栏的上内边距,工具栏从窗口顶边开始,布局不变,最大化时从屏幕最顶端的一行起就能拖动、双击还原。
验证(2026-10-16,本开发机,2560×1440,100% 缩放,发布构建,窗口放在最底层不激活,用 SetWindowPlacement 先不激活地最小化、再以 SW_SHOWNOACTIVATE 还原为最大化):通过 CDP 隐藏页面的标题栏按钮后截取窗口,原来的版本在右上角留下一组系统按钮,新版本右上角与背景一致,带着页面按钮的截图只有一组,位置与未最大化时相同。最大化时屏幕第 0–3 行的命中测试都落到 WebView,页面在这几行按 4 像素间距取样的 639 个点里 605 个是工具栏的拖动区域,其余是标题栏按钮(原来的版本这几行一个拖动点也没有)。未最大化时,左、右、下边与右下角的命中测试分别为 HTLEFT、HTRIGHT、HTBOTTOM、HTBOTTOMRIGHT,顶端 5 行(1 行非客户区加 4 行缩放子窗口)为 HTTOP,最大化按钮处为 HTMAXBUTTON;工具栏所在的顶部 60 行与改动前逐像素一致。调整大小的斜线检查(向右下拖动与从左边缘拖动各录 200 帧)均为 0 帧。实验窗口(与 tao 相同的 WM_NCCALCSIZE、Mica、透明客户区)里逐项对比了上面各种做法。
工具栏排版
工具栏的标题行是三列网格(minmax(max-content, 1fr) minmax(min-content, 360px) minmax(max-content, 1fr)):左列是图标、名称与页面的视图和筛选控件,中列是页面的搜索框,右列是末端控件。两侧的 1fr 平分搜索框剩下的空间,搜索框因此在窗口正中。针对所选项目的操作(例如服务页的启动、停止)放在右列开头:放进左列时,左侧变宽会把搜索框挤离中心。
窗口变窄时由 Toolbar.tsx 的 fitToolbar 实测哪些控件放得下,逐级收起标签,不按窗口宽度写媒体查询:曾有一条 max-width: 1180px 的规则隐藏所有分段按钮的标签,引导里只有文字的更新通道按钮在窄窗口下因此变成空白。比较宽度用 getBoundingClientRect() 的小数值:125%–175% 缩放下窗口的 CSS 宽度带小数(例如 2000 物理像素为 1333.33),与取整的 clientWidth 比较时,工具栏曾在放得下时误判溢出而换行。
有待安装的更新时,右列末尾出现一个强调色的圆形更新按钮,版本号在它的提示里。按钮不带文字:带“更新到 x.y.z”时,默认宽度(1240 像素)下搜索框偏离中心 63–160 像素,只用图标时仅服务页多偏 40 像素。新版本只通过这个按钮、每个版本一次的提示条和托盘图标上的圆点告知,不在启动时弹出对话框:Argus 常在系统卡顿时被打开,替换任务管理器后更是如此。更新完成后的首次启动是例外,默认显示本版本的更新内容,可在设置中关闭。
弹出层
菜单、下拉列表和提示不加投影,靠边框与背景区分。在一台 4K、150% 缩放的电脑上(RTX 4080 SUPER,未开 HDR),打开的下拉列表周围出现过一圈亮色光晕,范围与投影一致(向下偏、上窄下宽),只在半透明的卡片上变亮。光晕只在屏幕上可见:截图工具一启动就消失,窗口捕获和放在最底层的测试窗口也复现不出来,应是显卡叠加平面(MPO)的合成问题。去掉 backdrop-filter、改为不透明背景都无效,所以不再使用投影。