Skip to content

fix(windows): 恢复胶囊四边 clamp + 工作区定位,修被回退的 #470 (#689)#690

Open
appergb wants to merge 2 commits into
betafrom
fix/issue-470-capsule-clamp-regression
Open

fix(windows): 恢复胶囊四边 clamp + 工作区定位,修被回退的 #470 (#689)#690
appergb wants to merge 2 commits into
betafrom
fix/issue-470-capsule-clamp-regression

Conversation

@appergb

@appergb appergb commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

User description

关联 issue

Closes #689#470 的四边 clamp 回退)

问题

多显示器 / 负坐标 / 异常 DPI 下,Windows 胶囊可能被定位到屏幕外压在任务栏上#470 曾用 fab9651(+4824080 防溢出)加入「四边 clamp + 工作区(rcWork)定位」,被 Android 移植 commit 1ea467a 回退:

  • ForegroundMonitor 丢失 work_* 字段;
  • position_capsule_bottom_center 退回只夹上边 y.max(mon.top)(x/下/右不夹、不避任务栏);
  • clamp_to_monitor 沦为死码(本体仍在 lib.rs:1823,仅 test import 引用),单测被删。

改动(恢复 fab9651,单一职责)

  • ForegroundMonitor 加回 work_left/top/right/bottomforeground_window_monitormi.rcWork 填充;
  • position_capsule_bottom_center 改回用 clamp_to_monitor 四边夹工作区(取不到 rcWork 退回整屏矩形);
  • 恢复 clamp_to_monitor 的 3 个纯函数单测。

clamp_to_monitor 本体(含 saturating_sub 防溢出)一直在,只是没被调用。

测试

cargo test --lib clamp_to_monitor   # 3 passed(纯函数,三平台跑)
  ..._leaves_on_screen_position_untouched
  ..._pulls_back_off_screen_right_and_bottom
  ..._pulls_back_when_right_edge_overflows_inside_area

⚠️ 验证范围

本地 macOS 只能编译非 Windows 分支;ForegroundMonitor.work_* + rcWork 是 Windows-only 代码,由 CI 的 Windows 构建编译验证,并需 Windows 真机确认多屏下胶囊不再出屏/压任务栏。

来源:2026-06-16 全仓多 Agent 审计(#470 专项),git 溯源回退提交为 1ea467a


PR Type

Bug fix, Tests


Description

  • Restored work_* fields in ForegroundMonitor from rcWork for taskbar avoidance

  • Used clamp_to_monitor with work area to prevent capsule positioning off-screen

  • Restored 5 unit tests covering negative origin multi-monitor and taskbar avoidance


Diagram Walkthrough

flowchart LR
  A["ForegroundMonitor: add work_* fields from rcWork"] --> B["position_capsule_bottom_center"]
  B --> C["clamp_to_monitor: 4-sided clamp to work area"]
  C --> D["Window position inside visible area, avoiding taskbar"]
  E["5 unit tests"] -.->|"validate" C
Loading

File Walkthrough

Relevant files
Bug fix
lib.rs
Restore capsule clamp to work area with unit tests             

openless-all/app/src-tauri/src/lib.rs

  • Added work_left, work_top, work_right, work_bottom fields to
    ForegroundMonitor
  • Populated work_* from mi.rcWork in foreground_window_monitor()
  • Modified position_capsule_bottom_center to use clamp_to_monitor with
    work area
  • Restored 5 unit tests for clamp_to_monitor (on-screen, off-screen,
    negative origin, taskbar)
+72/-7   

#470 曾用 fab9651(+4824080 防溢出)加入「四边 clamp + 工作区(rcWork,避开任务栏)
定位」,被 Android 移植 commit 1ea467a 回退:ForegroundMonitor 丢了 work_* 字段、
position_capsule_bottom_center 退回只夹上边 `y.max(mon.top)`(x/下/右不夹、不避任务栏)、
clamp_to_monitor 沦为死码、专用单测被删。多显示器/负坐标/异常 DPI 下胶囊可能被算到
屏幕外或压在任务栏上。

- ForegroundMonitor 重新加回 work_left/top/right/bottom,foreground_window_monitor
  从 mi.rcWork 填充;
- position_capsule_bottom_center 改回用 clamp_to_monitor 四边夹工作区(取不到 rcWork
  退回整屏);
- 恢复 clamp_to_monitor 的 3 个纯函数单测(on-screen 不动 / 右下溢出收回 / 右缘内溢收回)。

clamp_to_monitor 本体一直在(含 saturating_sub 防溢出),只是没被调用。Windows
结构 + rcWork 由 CI 编译验证;需 Windows 真机确认多屏下胶囊不再出屏/压任务栏。
@appergb

appergb commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

@claude 请审核:恢复 #4701ea467a 回退的胶囊四边 clamp + 工作区定位。⚠️本地只编了 macOS,Windows 的 ForegroundMonitor.work_* + mi.rcWork 由 CI 验。重点看 (1) mi.rcWork 是否随 GetMonitorInfoW 一同填充、字段访问是否正确;(2) clamp 用工作区(避任务栏)、x 居中仍用 rcMonitor 宽度是否合理;(3) 取不到 rcWork 退回整屏的兜底。

@claude

claude Bot commented Jun 16, 2026

Copy link
Copy Markdown

Claude finished @appergb's task in 2m 29s —— View job


Code Review: 恢复胶囊四边 clamp + 工作区定位 (#470 回退修复)

Todo List

  • 获取 PR diff 和变更上下文
  • 读取 lib.rs 相关代码段
  • 分析 (1) mi.rcWorkGetMonitorInfoW 填充正确性
  • 分析 (2) clamp 用工作区、x 居中用 rcMonitor 宽度的合理性
  • 分析 (3) 取不到 rcWork 退回整屏的兜底逻辑
  • 发布完整审核结果

(1) mi.rcWork 是否随 GetMonitorInfoW 一同填充、字段访问是否正确

结论:正确。

Win32 的 MONITORINFO 同时包含 rcMonitor(全屏矩形)和 rcWork(工作区矩形),两者都由同一次 GetMonitorInfoW 调用填充,无需额外 API。关键路径:

// lib.rs:2391-2395
let mut mi = MONITORINFO {
    cbSize: std::mem::size_of::<MONITORINFO>() as u32,
    ..Default::default()  // rcWork 被零初始化
};
if !GetMonitorInfoW(hmon, &mut mi).as_bool() {
    return None;          // 失败时整体退出,不会用到未填充的 rcWork
}
// 成功后 mi.rcWork 已由系统填入

windows crate 的 MONITORINFO 与 Win32 结构体完全一一对应:rcWork: RECTRECT.left/.top/.right/.bottomi32)。字段访问路径 mi.rcWork.left 等完全正确。

唯一需要注意:若 Default::default() 的零初始化让 rcWork 全为 0,而 GetMonitorInfoW 调用成功后系统会覆盖这些值。失败的分支已有 return None。下面问题(3)中的兜底也能处理极端情况。


(2) clamp 用工作区(避任务栏)、x 居中仍用 rcMonitor 宽度是否合理

结论:合理,有一处值得知悉的细微偏差。

Y 轴(正确)

// lib.rs:2442
let y = ((mon.bottom as f64) - offset_from_bottom).round() as i32;
// 之后 clamp 到 work_b = rcWork.bottom(< rcMonitor.bottom,即任务栏顶边)

计算起点是 rcMonitor.bottom(全屏底),向上偏移后再由 clamp_to_monitor 压住 work_b。若任务栏较高(或 DPI 异常导致 offset 计算偏小),clamp 会阻止胶囊压到任务栏,逻辑正确。

X 轴(合理,轻微边界情况)

// lib.rs:2437-2438
let mon_w = mon.right - mon.left;          // 用 rcMonitor 宽度
let x = mon.left + ((mon_w - phys_w) / 2).max(0);
// 再 clamp 到 work_l..work_r
  • 常见场景(任务栏在底部):rcWork.left == rcMonitor.leftrcWork.right == rcMonitor.right,两种宽度完全相同,无任何差异。
  • 少见场景(任务栏在左/右侧):rcWork.leftrcWork.rightrcMonitor 不同,胶囊 x 中心点会相对工作区偏移约半个任务栏宽度(~24px)。因为 clamp 的左/右边界是工作区,若计算出的 x 落在工作区内,胶囊不会被调整(仍轻微偏离工作区中心)。

这是原 #470 (fab9651) 的一贯策略:视觉中心对齐显示器,工作区 clamp 仅作安全网。对于竖向任务栏的用户,24px 的偏差视觉上几乎不可感知,属于可接受的妥协。若追求完美可将 x 计算改为用 (work_l + work_r) / 2,但那属于额外改动,不在本 PR 范围内。


(3) 取不到 rcWork 时退回整屏的兜底

结论:兜底逻辑健壮。

// lib.rs:2446-2451
let (work_l, work_t, work_r, work_b) =
    if mon.work_right > mon.work_left && mon.work_bottom > mon.work_top {
        (mon.work_left, mon.work_top, mon.work_right, mon.work_bottom)
    } else {
        (mon.left, mon.top, mon.right, mon.bottom)
    };

条件 work_right > work_left && work_bottom > work_top 验证工作区矩形面积 > 0:

情形 条件值 行为
正常填充(rcWork 有效) true 使用工作区,避开任务栏 ✓
Default::default() 零初始化未被覆盖(理论上不可能) 0 > 0 = false 退回整屏 ✓
任务栏自动隐藏 / 无任务栏(rcWork == rcMonitor) true 使用 rcWork(等价整屏)✓
极端异常(rcWork 为负或倒置矩形) false 退回整屏 ✓

clamp_to_monitor 本身还用 saturating_sub 防止区域比窗口更小时的减法溢出(lib.rs:1835-1836),双重保护。


测试用例

3 个纯函数测试覆盖了核心分支,且三平台均可运行:

测试 输入 期望 逻辑覆盖
leaves_on_screen_position_untouched (800,900) in 1920×1040 (800,900) 无需 clamp
pulls_back_off_screen_right_and_bottom (2000,1200) (1656,914) 右+下都溢出
pulls_back_when_right_edge_overflows_inside_area (1800,500) (1656,500) 左上在内但右缘溢出

缺少的测试场景(不影响本 PR 合并,仅供参考):

  • 负坐标显示器(area_left < 0),如左侧副屏
  • 工作区比胶囊更窄时 max(area_left) 分支(面积极小退化情形)

总体评价

✅ 可以合并(待 Windows CI 绿灯)

三个审核点均无问题:

  1. rcWorkGetMonitorInfoW 一次调用填充,字段访问正确。
  2. X 居中用 rcMonitor、Y/全局 clamp 用 rcWork 是合理的策略,与 [Windows] [bug] 语音输入不显示录音胶囊 #470 原意一致。
  3. 兜底逻辑(空矩形检测 + 退回整屏)健壮,saturating_sub 防溢出已到位。

代码改动极小(恢复已有函数的调用,新增 4 个结构体字段),改动风险低,单测清晰。唯一外部依赖是 Windows 真机验证多屏/负坐标场景,这在 PR 描述中已说明。

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit 68b3111)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

689 - Partially compliant

Compliant requirements:

  • 恢复 ForegroundMonitor 的 work_* 字段
  • position_capsule_bottom_center 使用 clamp_to_monitor 四边夹工作区
  • 恢复 clamp_to_monitor 的 3 个纯函数单测

Non-compliant requirements:

  • 验证:Windows 真机确认(无法通过代码审查完成)

Requires further human verification:

  • 验证:Windows 真机确认

470 - Partially compliant

Compliant requirements:

  • (PR 恢复了 clamp 逻辑,理论上解决胶囊被定位到屏幕外/任务栏的问题)

Non-compliant requirements:

Requires further human verification:

  • 需 Windows 真机确认胶囊是否正常显示
⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

审核指出原 fab9651 的 5 个 clamp 单测只恢复了 3 个,漏掉的恰是本 PR 主打的两类
场景:负原点多显示器(左侧副屏)与工作区避让任务栏。补回这两个纯函数单测。
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 68b3111

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug][windows] 多屏胶囊可能跑出屏/压任务栏:#470 的四边 clamp 被 1ea467a 回退成死码

1 participant