mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-06 20:28:23 +02:00
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).
113 lines
3.2 KiB
QML
113 lines
3.2 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Hyprland
|
|
|
|
PopupWindow {
|
|
id: root
|
|
|
|
required property Item anchorItem
|
|
required property QtObject bar
|
|
property var owner: null
|
|
property int margin: 8
|
|
property int padding: 14
|
|
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()
|
|
else root.open = false
|
|
}
|
|
|
|
default property alias contentItem: contentHolder.children
|
|
|
|
visible: open
|
|
color: "transparent"
|
|
implicitWidth: contentWidth
|
|
implicitHeight: contentHeight
|
|
|
|
onOpenChanged: {
|
|
if (!bar) return
|
|
if (open) bar.requestPopout(coordinatorKey)
|
|
else if (bar.activePopout === coordinatorKey) bar.releasePopout(coordinatorKey)
|
|
}
|
|
|
|
// 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. Skipped for hover-mode popups so the cursor
|
|
// can move freely between the trigger and the popup.
|
|
HyprlandFocusGrab {
|
|
active: root.open && root.triggerMode === "click"
|
|
windows: root.anchorWindow ? [root, root.anchorWindow] : [root]
|
|
onCleared: root.closePopout()
|
|
}
|
|
|
|
anchor {
|
|
id: popupAnchor
|
|
window: anchorItem ? anchorItem.QsWindow.window : null
|
|
adjustment: PopupAdjustment.Slide
|
|
edges: Edges.Top | Edges.Left
|
|
gravity: Edges.Bottom | Edges.Right
|
|
rect.width: 1
|
|
rect.height: 1
|
|
|
|
onAnchoring: {
|
|
if (!root.anchorItem || !root.bar) return
|
|
|
|
var target = root.anchorItem
|
|
var popupWidth = root.implicitWidth
|
|
var popupHeight = root.implicitHeight
|
|
var localX = target.width / 2 - popupWidth / 2
|
|
var localY = target.height + root.margin
|
|
|
|
if (root.bar.position === "bottom") {
|
|
localY = -popupHeight - root.margin
|
|
} else if (root.bar.position === "left") {
|
|
localX = target.width + root.margin
|
|
localY = target.height / 2 - popupHeight / 2
|
|
} else if (root.bar.position === "right") {
|
|
localX = -popupWidth - root.margin
|
|
localY = target.height / 2 - popupHeight / 2
|
|
}
|
|
|
|
var window = target.QsWindow.window
|
|
if (!window) return
|
|
|
|
var point = window.contentItem.mapFromItem(target, localX, localY)
|
|
popupAnchor.rect.x = Math.round(point.x)
|
|
popupAnchor.rect.y = Math.round(point.y)
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: card
|
|
anchors.fill: parent
|
|
color: root.bar ? root.bar.background : "#101315"
|
|
border.color: root.bar ? root.bar.foreground : "#cacccc"
|
|
border.width: 1
|
|
radius: 0
|
|
opacity: root.open ? 1.0 : 0
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: 140; easing.type: Easing.OutCubic }
|
|
}
|
|
|
|
Item {
|
|
id: contentHolder
|
|
anchors.fill: parent
|
|
anchors.margins: root.padding
|
|
}
|
|
|
|
HoverHandler {
|
|
id: cardHover
|
|
}
|
|
}
|
|
}
|