Repository navigation
fix: prevent tooltip from showing when target is scrolled out of view #701
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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) | ||||||||||||||||||
|
Comment on lines
+29
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): Triggers: When the target is inside a Flickable or another scroll container whose content position changes during scrolling. Suggested fix: Read the relevant ancestor position/scroll properties explicitly in the binding, or update an explicit visibility check from the scroll container's movement signal.
Suggested change
|
||||||||||||||||||
| 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 | ||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): The visibility check considers only
target.visibleandtarget.opacity, so a target whose ancestor is hidden or fully transparent is still treated as visible. Its nonzero geometry can therefore keep_showntrue and leave the tooltip displayed even though the target is not rendered.Triggers: When a visible target is nested under an ancestor with
visible: falseoropacity: 0.Suggested fix: Traverse the ancestor chain and reject the target when an ancestor is not effectively visible or has zero effective opacity.