Files
mtgodot-poc/audit/contracts/ui.equipment-tooltip.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

98 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ui.equipment-tooltip
## Scope
比较查看装备、装备属性计算、普通提示框与查看装备窗口的创建/销毁、定位、内容和重复打开行为。
## Reference call chain
- `UserInterface/PythonNetworkStreamPhaseGameItem.cpp`
- `UserInterface/PythonItem.cpp`
- `UserInterface/PythonItemModule.cpp`
- `UserInterface/PythonNetworkStreamPhaseGame.cpp`
- `UserInterface/PythonTextTail.cpp`
说明:40250 `ClientVS22/source` 树中没有 Python UI 脚本;迁移目录中的脚本只能作为历史/资产线索,不能替代 C++ 参考实现。
## Current call chain
- `project/ui/view_equipment_ui.gd`
- `project/ui/item_tooltip.gd`
- `project/ui/item_tooltip_view.gd`
- `project/ui/ui_manager.gd`
- `project/ui/inventory_ui.gd`
- `project/ui/shop_ui.gd`
- `project/ui/equip_model.gd`
- `project/ui/player_view.gd`
- `project/view_equipment_ui_test.gd`
- `project/item_tooltip_test.gd`
## Initial finding
当前已经针对“两个属性面板”和查看装备窗口增加了实现与测试,但尚未完成 40250 的窗口生命周期、属性来源、槽位顺序、装备/时装分层和关闭清理的 1:1 证明。重点检查重复创建、普通 tooltip 与查看装备窗口同时存在、远程角色更新和切换目标。
## Audit findings (2026-09-20)
`ViewEquipmentUI` 本身已经使用 `UiManager` 提供的单例 `ItemTooltipView`,测试也确认查看装备窗口的尺寸、11 个槽位和共享属性面板通过。但全局提示框路径仍存在明确的重复面板风险:
- `project/ui/inventory_ui.gd` 在 `_fill_cell` 中给每个格子写入 Godot 原生 `cell.tooltip_text`,同时在 `mouse_entered` 中显示共享 `ItemTooltipView`。
- `project/ui/shop_ui.gd` 在商店格子中同样写入 `slot.tooltip_text`,并在悬停时显示共享 `ItemTooltipView`。
- 40250 的 `uiToolTip.py` 是自绘/自管理的 `ItemToolTip` 窗口语义;当前端同时保留原生 tooltip 和自定义 tooltip,因此“查看装备窗口自身只有一个面板”的测试不能证明背包、商店和装备查看的全局单面板约束。
这解释了为什么局部查看装备测试可以通过,但实际切换到背包或商店仍可能出现两个属性面板。另一个未决点是 `ViewEquipmentUI` 的固定位置与 40250 窗口定位/屏幕边界处理尚未完成对照。当前仅记录差异,本轮不把它误标为已修复。
## Baseline tests
`view_equipment_ui_test.gd` 当前通过,确认已覆盖的 40250 对话框尺寸、11 个装备槽、共享属性面板和单实例约束正常;属性数据来源及完整窗口生命周期仍未完成对照。
## Equivalence matrix
| 项目 | 状态 | 备注 |
|---|---|---|
| Preconditions | UNASSESSED | 目标有效性、物品存在和查看权限 |
| Branch structure | UNASSESSED | 普通物品、装备、时装、空槽和未知物品 |
| Algorithms/formulas | UNASSESSED | 属性和附加属性展示来源 |
| State transition order | UNASSESSED | request → data → panel → tooltip → cleanup |
| Constants/units | UNASSESSED | 槽位、尺寸、偏移和格式化规则 |
| Timing/event sources | UNASSESSED | 网络查看结果和鼠标悬停更新 |
| Resource/data sources | UNASSESSED | item_proto、itemdesc、UI `.sub` 和 locale |
| Protocol side effects | UNASSESSED | 查看装备请求/响应及重复请求 |
| Interruption/failure/cleanup | UNASSESSED | 关闭、切换目标、重复打开和节点回收 |
## Remaining
- 对照 40250 的 `uiToolTip.py`/`uiCharacter.py` 及查看装备协议处理。
- 证明背包、商店、交易、仓库、商城仓库、个人摊位和查看装备共用同一个 UiManager 实例,而不是只在各自 headless fixture 中各自创建 fallback。
- 增加切换目标、快速打开关闭、空装备、远程更新和窗口边界定位测试。
## Next audit round 2026-09-20T18:19Z
本轮继续沿 40250 的 `PythonSlotWindow`/`uiToolTip.ItemToolTip` 生命周期核对全局 tooltip,而不是只检查 `ViewEquipmentUI`:
- 40250 的装备查看窗口与背包/商店格都通过 ItemToolTip 的 `ShowToolTip`/`HideToolTip` 事件管理自绘窗口;当前 `ViewEquipmentUI` 确实通过 `UiManager` 复用单个 `ItemTooltipView`,局部测试中的“只有一个属性面板”成立。
- 当前 `InventoryUI::_fill_cell`(`project/ui/inventory_ui.gd:195-209`)仍给格子写入 `cell.tooltip_text`,随后 `_on_cell_mouse_entered`(`:282-300`)又显示共享 `ItemTooltipView`。`ShopUI::_fill_slot`(`project/ui/shop_ui.gd:420-426`)同样同时设置 `slot.tooltip_text` 并连接 `_show_slot_tooltip`。鼠标悬停时存在 Godot 原生 tooltip 与自绘属性面板并存的路径,这正是“查看装备时出现两个面板”在背包/商店入口仍可能复现的结构性差异。
- `view_equipment_ui_test.gd` 通过并且退出时仍报告 5 个 ObjectDB 泄漏;该测试只覆盖查看装备窗口自身,不覆盖背包/商店 tooltip 互斥,也不能作为完整生命周期清理证据。
- `ViewEquipmentUI` 的固定 `870,300` 起始位置与 `ItemTooltipView::update_position` 的鼠标跟随/底部翻转策略仍不是同一定位算法;当前尚无屏幕边界和快速切换目标的回归测试。
结论:40250 语义的查看装备单例路径已经有局部实现,但全局 tooltip 互斥和窗口清理尚未闭合。本轮没有修改实现代码,合同保持 `PARTIAL`;下一步应由 `UiManager` 统一负责 item tooltip 的显示/隐藏,并清除物品格上的原生 `tooltip_text`,再补背包↔商店↔查看装备快速切换测试。
## Implementation fix round 2026-09-21
- `InventoryUI` 和 `ShopUI` 的物品格现在清空 Godot 原生 `tooltip_text`,属性内容保存在格子 metadata,并统一交给共享 `ItemTooltipView`;移动端详情读取同一份内容,不再产生第二个原生面板。
- `inventory_ui_test.gd`、`view_equipment_ui_test.gd` 和 `test_npc_shop_flow.gd` 已覆盖背包/装备/商店的单面板路径、属性/价格内容和刷新后的共享面板。
本合同继续为 `PARTIAL`:远程查看协议、窗口边界定位、快速切换和完整销毁生命周期仍需按 40250 继续审计。
## Implementation fix round 2026-09-21 — all item windows use the custom tooltip owner
继续按 40250 `PythonSlotWindow`/`ItemToolTip.ShowToolTip` 的单实例语义收口物品入口:新增
`ItemTooltipBridge`,并将交易、仓库、商城仓库和个人摊位的物品行/物品格接入
`UiManager.get_item_tooltip_view()`;没有 UiManager 的 headless fixture 使用同一 bridge 的
局部 fallback。所有这些 item control 现在清空 Godot 原生 `tooltip_text`,悬停进入/离开只
切换自绘 `ItemTooltipView`,关闭窗口时主动隐藏。
`test_safebox_mall_parity.gd` 新增原生重复面板和自绘悬停回归,40 项通过;同时回归
`p8_test.gd`、`test_npc_shop_flow.gd`、`private_shop_ui_test.gd` 和 `item_tooltip_test.gd`
均通过。真实运行时多窗口共享 UiManager、边界定位、快速切换和完整销毁顺序仍保持
`PARTIAL`。