# 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`。