From bfa8e2234e3d8f72269d0bdd1c576be53da09328 Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Wed, 13 May 2026 18:47:03 -0400 Subject: [PATCH] Show weather popup on hover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PopupCard gains a triggerMode property — "click" stays as the default (focus-grab dismissal), "hover" skips the grab so the cursor can move freely between the trigger and the card. PopupCard also exposes a containsMouse alias for widgets that want to coordinate hover state. Weather widget now shows on hover and hides 220ms after the cursor leaves both the trigger and the popup. The redundant tooltip is gone — the popup is the detail view. Click is still wired for refresh (middle) and notification dump (right). --- .../plugins/bar/common/PopupCard.qml | 13 +++++- .../plugins/bar/widgets/weatherFlyout.qml | 43 ++++++++++++++++--- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/default/quickshell/omarchy-shell/plugins/bar/common/PopupCard.qml b/default/quickshell/omarchy-shell/plugins/bar/common/PopupCard.qml index 52bc3b89..de45bae4 100644 --- a/default/quickshell/omarchy-shell/plugins/bar/common/PopupCard.qml +++ b/default/quickshell/omarchy-shell/plugins/bar/common/PopupCard.qml @@ -13,9 +13,13 @@ PopupWindow { property int contentWidth: 280 property int contentHeight: 200 property bool open: false + // "click" — uses HyprlandFocusGrab so clicking outside dismisses the popup. + // "hover" — passive overlay; the owning widget controls open via hover. + property string triggerMode: "click" readonly property var coordinatorKey: owner || root readonly property var anchorWindow: anchorItem ? anchorItem.QsWindow.window : null + readonly property bool containsMouse: cardHover.hovered function closePopout() { if (owner && "closePopout" in owner) owner.closePopout() @@ -37,9 +41,10 @@ PopupWindow { // Outside-click dismissal via Hyprland's focus grab. While `active`, input // is routed only to the listed windows; clicking anywhere else clears the - // grab and we close the popup. + // grab and we close the popup. Skipped for hover-mode popups so the cursor + // can move freely between the trigger and the popup. HyprlandFocusGrab { - active: root.open + active: root.open && root.triggerMode === "click" windows: root.anchorWindow ? [root, root.anchorWindow] : [root] onCleared: root.closePopout() } @@ -99,5 +104,9 @@ PopupWindow { anchors.fill: parent anchors.margins: root.padding } + + HoverHandler { + id: cardHover + } } } diff --git a/default/quickshell/omarchy-shell/plugins/bar/widgets/weatherFlyout.qml b/default/quickshell/omarchy-shell/plugins/bar/widgets/weatherFlyout.qml index 11fba5c5..195d2150 100644 --- a/default/quickshell/omarchy-shell/plugins/bar/widgets/weatherFlyout.qml +++ b/default/quickshell/omarchy-shell/plugins/bar/widgets/weatherFlyout.qml @@ -18,8 +18,6 @@ Item { readonly property string label: bar ? bar.weatherText : "" readonly property string klass: bar ? bar.weatherClass : "" - // Parse the wttr.in single-line report into location + condition halves so - // we can render them with different emphasis. readonly property string reportLocation: { var parts = String(fullReport || "").split(":") return parts.length > 1 ? parts[0].trim() : "" @@ -38,6 +36,31 @@ Item { if (!forecastProc.running) forecastProc.running = true } + // Hover state across the trigger button and the popup. + property bool buttonHovered: false + property bool popupHovered: popup.containsMouse + + function showPopup() { + hideTimer.stop() + if (!popupOpen) refresh() + popupOpen = true + } + + function scheduleHide() { + hideTimer.restart() + } + + Timer { + id: hideTimer + interval: 220 + onTriggered: { + if (!root.buttonHovered && !root.popupHovered) root.popupOpen = false + } + } + + onButtonHoveredChanged: buttonHovered ? showPopup() : scheduleHide() + onPopupHoveredChanged: popupHovered ? hideTimer.stop() : scheduleHide() + Process { id: forecastProc command: ["bash", "-lc", "curl -fsS --max-time 5 'wttr.in/?T0&format=%l:+%C+%t+%f+wind+%w+%h+humidity' 2>/dev/null"] @@ -54,22 +77,28 @@ Item { text: root.label active: root.klass === "active" horizontalMargin: 7.5 - tooltipText: root.fullReport || "Weather" + // Tooltip suppressed — the popup itself is the detail view. + tooltipText: "" onPressed: function(b) { if (b === Qt.RightButton) root.bar.run("omarchy-notification-send \"$(omarchy-weather-status)\"") - else { - root.popupOpen = !root.popupOpen - if (root.popupOpen) root.refresh() - } + else if (b === Qt.MiddleButton) root.refresh() } } + HoverHandler { + id: hoverHandler + target: button + onHoveredChanged: root.buttonHovered = hovered + } + Common.PopupCard { + id: popup anchorItem: button owner: root bar: root.bar open: root.popupOpen + triggerMode: "hover" contentWidth: 320 contentHeight: card.implicitHeight + 28