From 8eb08631f785c32139611670fb9faba13f09b636 Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 2 Sep 2026 00:27:32 +0200 Subject: [PATCH] fix: autoclose only when not hovered --- modules/notification/NotificationBody.qml | 15 ++++- modules/notification/NotificationHistory.qml | 61 +++++++++++--------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/modules/notification/NotificationBody.qml b/modules/notification/NotificationBody.qml index 2486125..ca2f9a6 100644 --- a/modules/notification/NotificationBody.qml +++ b/modules/notification/NotificationBody.qml @@ -1,6 +1,7 @@ import QtQuick import Quickshell import "../../config" +import "./notificationServer" import QtQuick.Layouts import QtQuick.Controls @@ -46,6 +47,18 @@ Rectangle { property color textColor: urgency != 3 ? Colors.primary : Colors.tertiary property color textBGColor: urgency != 3 ? Qt.alpha(Colors.primaryContainer, 0.75) : Qt.alpha(Colors.tertiaryContainer, 0.75) + // Expose hover state for history auto-close (reuse paused logic) + readonly property bool isHovered: mouseArea.containsMouse || closingMouseArea.containsMouse || inlineReplyButtonMouseArea.containsMouse || _hoveredActionCount > 0 || inlineReplyTextField.hovered || inlineReplyTextField.activeFocus + + HoverHandler { + enabled: root.isHistory + blocking: false + grabPermissions: PointerHandler.CanTakeOverFromAnything + onHoveredChanged: if (hovered) NotificationS.historyHovered = true + } + // When in history, also keep historyHovered true while any inner element is hovered + onIsHoveredChanged: if (isHistory && isHovered) NotificationS.historyHovered = true + function closeNotification(dismiss){ if (closing) { return @@ -420,7 +433,7 @@ Rectangle { from: 1.0 to: 0.0 duration: root.duration - paused: (mouseArea.containsMouse || closingMouseArea.containsMouse || inlineReplyButtonMouseArea.containsMouse || root._hoveredActionCount > 0 || inlineReplyTextField.hovered || inlineReplyTextField.activeFocus) && !isHistory + paused: isHovered && !isHistory onFinished: root.closeNotification(false) } } diff --git a/modules/notification/NotificationHistory.qml b/modules/notification/NotificationHistory.qml index 629ce73..9a4fc95 100644 --- a/modules/notification/NotificationHistory.qml +++ b/modules/notification/NotificationHistory.qml @@ -74,27 +74,12 @@ PanelWindow { } // ── Hover tracking via NotificationServer ── - // History reports its hover state to the singleton; button does the same. - // Timer auto-closes when neither is hovered for historyAutoCloseDelay ms. + // History reports its hover state via background HoverHandler (non-exclusive) + // so hovering over inner buttons (closeArea, clearGroupArea, NotificationBody) + // does NOT clear historyHovered. Timer auto-closes when neither history nor + // bar button is hovered for historyAutoCloseDelay ms. property bool _allowAutoClose: false - HoverHandler { - id: historyHoverHandler - onHoveredChanged: NotificationS.historyHovered = hovered - } - - // Fallback MouseArea that doesn't steal clicks (acceptedButtons: NoButton) - // Ensures hover is detected even if HoverHandler is missed on PanelWindow boundaries. - // Placed with high z but NoButton so it doesn't block inner MouseAreas. - MouseArea { - id: historyHoverMouseArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton - propagateComposedEvents: true - onContainsMouseChanged: NotificationS.historyHovered = containsMouse - } - // Grace timer: don't allow auto-close immediately after opening, // giving keyboard users time to move mouse to history. Timer { @@ -156,14 +141,22 @@ PanelWindow { border.color: textColor radius: Config.screen_big_radius_fun(screen.height) - // Text { - // anchors.centerIn: parent - // font.family: Config.font - // font.pixelSize: bigFontSize - // color: textColor - // wrapMode: Text.Wrap - // text: NotificationS.trackedNotifications.values[0] - // } + // HoverHandler on background (parent of all history UI) stays hovered + // when pointer is over any descendant (topBar buttons, NotificationBody etc.) + // because HoverHandler is non-blocking PointerHandler and tracks parent + // bounds rather than exclusive MouseArea containsMouse. This fixes bug + // where hovering over inner buttons made historyHovered false and triggered + // auto-close. Using CanTakeOverFromAnything ensures it isn't blocked by + // child MouseAreas. + HoverHandler { + id: backgroundHoverHandler + blocking: false + grabPermissions: PointerHandler.CanTakeOverFromAnything + onHoveredChanged: { + NotificationS.historyHovered = hovered + if (Config.debug == "Notification") console.log("[NotificationHistory] background hovered:", hovered) + } + } // Component.onCompleted: { // for (var notification in NotificationS.trackedNotifications.values) { @@ -279,6 +272,9 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter hoverEnabled: true cursorShape: Qt.PointingHandCursor + // Safety: hovering over button should keep history considered hovered + // even if background HoverHandler is blocked (exclusive hover) + onContainsMouseChanged: if (containsMouse) NotificationS.historyHovered = true Rectangle{ anchors.fill: parent color: closeArea.containsMouse ? "red" : textBGColor @@ -356,6 +352,14 @@ PanelWindow { implicitHeight: isGrouped ? groupedCard.implicitHeight : singleBody.implicitHeight height: implicitHeight + // Keep history hovered when pointer is over this delegate + // (covers NotificationBody and its inner buttons). + HoverHandler { + blocking: false + grabPermissions: PointerHandler.CanTakeOverFromAnything + onHoveredChanged: if (hovered) NotificationS.historyHovered = true + } + // ── Singleton: plain NotificationBody ── NotificationBody { id: singleBody @@ -400,6 +404,8 @@ PanelWindow { MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onContainsMouseChanged: if (containsMouse) NotificationS.historyHovered = true onClicked: root.toggleGroup(group.appName) } @@ -451,6 +457,7 @@ PanelWindow { height: bigFontSize + Config.spacing hoverEnabled: true cursorShape: Qt.PointingHandCursor + onContainsMouseChanged: if (containsMouse) NotificationS.historyHovered = true onClicked: function(mouse) { mouse.accepted = true root.clearGroup(group.appName)