From 5110b0d75394ffed545cd596a04c8335dead4f87 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 20 May 2026 15:52:43 +0200 Subject: [PATCH] Guard bar tooltips with live hover state --- shell/Ui/WidgetButton.qml | 1 + shell/plugins/bar/Bar.qml | 48 +++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/shell/Ui/WidgetButton.qml b/shell/Ui/WidgetButton.qml index 3ca7c345..d5e2fe80 100644 --- a/shell/Ui/WidgetButton.qml +++ b/shell/Ui/WidgetButton.qml @@ -56,6 +56,7 @@ Item { readonly property real scaledHorizontalMargin: Style.spaceReal(horizontalMargin) readonly property real scaledRightExtraMargin: Style.spaceReal(rightExtraMargin) readonly property real scaledVerticalPadding: Style.spaceReal(verticalPadding) + readonly property bool tooltipHovered: visible && interactive && !concealed && mouseArea.containsMouse visible: text !== "" || keepSpace opacity: text === "" || concealed ? 0 : (dimmed ? 0.45 : 1) diff --git a/shell/plugins/bar/Bar.qml b/shell/plugins/bar/Bar.qml index a28fe17c..bb7d43e7 100644 --- a/shell/plugins/bar/Bar.qml +++ b/shell/plugins/bar/Bar.qml @@ -104,6 +104,19 @@ Item { return !!target && !!window && targetWindow(target) === window } + function targetTooltipHovered(target) { + return !!target && target.visible !== false && target.opacity !== 0 && target.tooltipHovered === true + } + + function clearTooltip() { + tooltipTimer.stop() + pendingTooltipTarget = null + pendingTooltipText = "" + tooltipTarget = null + tooltipText = "" + tooltipShown = false + } + function requestPopout(owner) { if (activePopout === owner) return if (activePopout && "close" in activePopout) activePopout.close() @@ -388,14 +401,9 @@ Item { } function showTooltip(target, text) { - tooltipTimer.stop() - tooltipTarget = null - tooltipText = "" - tooltipShown = false + clearTooltip() - if (!target || !text) { - pendingTooltipTarget = null - pendingTooltipText = "" + if (!targetTooltipHovered(target) || !text) { tooltipRequest += 1 return } @@ -407,6 +415,10 @@ Item { Qt.callLater(function() { if (request !== tooltipRequest) return + if (!targetTooltipHovered(pendingTooltipTarget)) { + clearTooltip() + return + } tooltipTarget = pendingTooltipTarget tooltipText = pendingTooltipText pendingTooltipTarget = null @@ -418,13 +430,8 @@ Item { function hideTooltip(target) { if (tooltipTarget !== target && pendingTooltipTarget !== target) return - tooltipTimer.stop() tooltipRequest += 1 - pendingTooltipTarget = null - pendingTooltipText = "" - tooltipTarget = null - tooltipText = "" - tooltipShown = false + clearTooltip() } function refreshUpdate() { @@ -510,7 +517,17 @@ Item { Timer { id: tooltipTimer interval: 400 - onTriggered: root.tooltipShown = true + onTriggered: { + if (root.targetTooltipHovered(root.tooltipTarget)) root.tooltipShown = true + else root.clearTooltip() + } + } + + Timer { + interval: 100 + running: root.tooltipShown + repeat: true + onTriggered: if (!root.targetTooltipHovered(root.tooltipTarget)) root.hideTooltip(root.tooltipTarget) } Timer { @@ -1053,6 +1070,7 @@ Item { property real fixedWidth: -1 property real fixedHeight: -1 property string tooltipText: "" + readonly property bool tooltipHovered: visible && opacity > 0 && mouseArea.containsMouse signal pressed(int button) signal wheelMoved(int delta) @@ -1743,6 +1761,8 @@ Item { trayItemRoot.modelData.scroll(wheel.angleDelta.y, false) } } + + readonly property bool tooltipHovered: visible && opacity > 0 && mouseArea.containsMouse } }