From 4d705d61f230b80f9decef54ece8acff1a15e4fe Mon Sep 17 00:00:00 2001 From: Wu JiangYu Date: Fri, 18 Sep 2026 13:29:15 +0800 Subject: [PATCH] fix: prevent tooltip from showing when target is scrolled out of view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add _targetOutOfView property to detect when the target item is clipped outside its scroll container's visible area 2. Traverse up the parent chain to find the nearest clipping container and check target position relative to it 3. Integrate _targetOutOfView into _shown condition so tooltip hides when target scrolls away 4. Refactor y positioning: separate spacing offset from slide animation using __slideOffset property 5. Apply Behavior animation only to __slideOffset so tooltip follows target immediately during scroll without animation lag Log: Fixed tooltip remaining visible after its target input field scrolls out of view Influence: 1. Test tooltip display when target is visible within scroll container 2. Verify tooltip hides immediately when scrolling target out of the visible area 3. Test tooltip slide-in animation on normal appearance 4. Test tooltip slide-out animation on dismissal 5. Verify tooltip follows target smoothly during scroll without lag or animation restart issues 6. Test with nested scroll containers and verify correct clipping detection 7. Verify tooltip behavior when target is hidden or has zero dimensions fix: 修复目标滚出可视区域后提示框仍然显示的问题 1. 添加 _targetOutOfView 属性,检测目标项是否被滚动容器裁剪出可视区域 2. 向上遍历父级链查找最近的裁剪容器,并检查目标相对其的位置 3. 将 _targetOutOfView 集成到 _shown 条件中,使目标滚走后提示框自动隐藏 4. 重构 y 轴定位:使用 __slideOffset 属性将间距偏移与滑动动画分离 5. 仅对 __slideOffset 应用动画,使滚动时提示框立即跟随目标而不会出现动画 滞后 Log: 修复输入框滚出可视区域后提示框仍然悬浮显示的问题 Influence: 1. 测试目标在滚动容器内可见时提示框的显示 2. 验证滚动目标离开可视区域时提示框是否立即隐藏 3. 测试提示框正常出现时的滑入动画 4. 测试提示框消失时的滑出动画 5. 验证滚动过程中提示框是否平滑跟随目标,无滞后或动画重启问题 6. 测试嵌套滚动容器场景,验证裁剪检测是否正确 7. 验证目标隐藏或尺寸为零时提示框的行为 PMS: BUG-311801 --- qt6/src/qml/AlertToolTip.qml | 46 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/qt6/src/qml/AlertToolTip.qml b/qt6/src/qml/AlertToolTip.qml index 04d17aa9a..0872fed8b 100644 --- a/qt6/src/qml/AlertToolTip.qml +++ b/qt6/src/qml/AlertToolTip.qml @@ -13,7 +13,44 @@ Control { property string text property int timeout: 0 property bool _expired: false - readonly property bool _shown: control.visible && !_expired + + // 目标是否已被裁剪出可视区域(例如编辑页向上滚动后输入框已不可见)。 + // 此时提示框不应继续显示,否则会出现输入框已滚走、提示框仍悬浮在页面上的问题。 + // 做法参照 Qt 内部 Qt::ImInputItemClipRectangle 的实现(qquickitem.cpp): + // 从目标自身开始逐级向上映射矩形,对每一个 clip 为 true 的祖先求交, + // 最后再与窗口矩形求交,结果为空即表示目标已完全不可见。 + readonly property bool _targetOutOfView: { + if (!target || !target.visible || target.opacity <= 0) + return true + + let rect = Qt.rect(0, 0, target.width, target.height) + let par = target + while (par.parent) { + const parpar = par.parent + rect = parpar.mapFromItem(par, rect) + if (parpar.clip) + rect = _intersectRect(rect, Qt.rect(0, 0, parpar.width, parpar.height)) + if (rect.width <= 0 || rect.height <= 0) + return true + par = parpar + } + // 映射到场景坐标后与窗口矩形求交。循环结束时 par 即为根项, + // 也就是窗口的 contentItem,其尺寸等同窗口尺寸。 + rect = par.mapToItem(null, rect) + rect = _intersectRect(rect, Qt.rect(0, 0, par.width, par.height)) + return rect.width <= 0 || rect.height <= 0 + } + + // QML 的 rect 值类型未暴露 intersected(),这里手动求交 + function _intersectRect(a, b) { + const x = Math.max(a.x, b.x) + const y = Math.max(a.y, b.y) + const right = Math.min(a.x + a.width, b.x + b.width) + const bottom = Math.min(a.y + a.height, b.y + b.height) + return Qt.rect(x, y, Math.max(0, right - x), Math.max(0, bottom - y)) + } + + readonly property bool _shown: control.visible && !_expired && !_targetOutOfView property point __itemGlobalPos: { let x = 0, y = 0 @@ -26,8 +63,11 @@ Control { return Qt.point(x, y) } x: __itemGlobalPos.x - y: __itemGlobalPos.y + (target ? target.height : 0) + (_shown ? DS.Style.control.spacing : 0) - Behavior on y { + y: __itemGlobalPos.y + (target ? target.height : 0) + DS.Style.control.spacing + __slideOffset + // 仅显示/隐藏时做滑入滑出动画;滚动或布局变化时 y 立即跟随, + // 避免动画反复重启导致提示框滞后于输入框 + property real __slideOffset: _shown ? 0 : -DS.Style.control.spacing + Behavior on __slideOffset { NumberAnimation { duration: 200 } } parent: _shown ? Overlay.overlay : target