Files
omarchycn/default/quickshell/omarchy-shell/plugins/bar/widgets/weatherFlyout.qml
T
Ryan Hughes bfa8e2234e Show weather popup on hover
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).
2026-05-14 02:21:48 -04:00

170 lines
4.2 KiB
QML

import QtQuick
import Quickshell
import Quickshell.Io
import "../common" as Common
Item {
id: root
property QtObject bar: null
property string moduleName: "weatherFlyout"
property var settings: ({})
property bool popupOpen: false
function closePopout() { popupOpen = false }
property string fullReport: ""
readonly property string label: bar ? bar.weatherText : ""
readonly property string klass: bar ? bar.weatherClass : ""
readonly property string reportLocation: {
var parts = String(fullReport || "").split(":")
return parts.length > 1 ? parts[0].trim() : ""
}
readonly property string reportCondition: {
var parts = String(fullReport || "").split(":")
if (parts.length > 1) return parts.slice(1).join(":").trim()
return String(fullReport || "").trim()
}
visible: label !== ""
implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight
function refresh() {
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"]
stdout: StdioCollector {
waitForEnd: true
onStreamFinished: root.fullReport = String(text || "").trim()
}
}
Common.WidgetButton {
id: button
anchors.fill: parent
bar: root.bar
text: root.label
active: root.klass === "active"
horizontalMargin: 7.5
// 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 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
Column {
id: card
anchors.fill: parent
spacing: 12
Row {
width: parent.width
spacing: 10
Text {
id: glyph
text: root.label || "—"
color: root.bar.foreground
font.family: root.bar.fontFamily
font.pixelSize: 24
anchors.verticalCenter: location.verticalCenter
}
Text {
id: location
text: root.reportLocation || "Weather"
color: root.bar.foreground
font.family: root.bar.fontFamily
font.pixelSize: 13
font.bold: true
anchors.verticalCenter: parent.verticalCenter
width: parent.width - glyph.width - parent.spacing
elide: Text.ElideRight
}
}
Text {
text: root.reportCondition || "Fetching forecast…"
color: Qt.darker(root.bar.foreground, 1.2)
font.family: root.bar.fontFamily
font.pixelSize: 11
wrapMode: Text.WordWrap
width: parent.width
}
Row {
width: parent.width
spacing: 8
Common.PillButton {
iconText: "󰑐"
text: "Refresh"
foreground: root.bar.foreground
horizontalPadding: 12
verticalPadding: 6
onClicked: root.refresh()
}
Common.PillButton {
iconText: "󰏌"
text: "wttr.in"
foreground: root.bar.foreground
horizontalPadding: 12
verticalPadding: 6
onClicked: { root.bar.run("xdg-open https://wttr.in"); root.popupOpen = false }
}
}
}
}
}