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).
170 lines
4.2 KiB
QML
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 }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|