Files
dotfiles/.config/quickshell/own/Popup.qml
T

110 lines
3.7 KiB
QML

// Popup.qml - a reusable popup component for Quickshell.
import Quickshell
import Quickshell.Wayland
import QtQuick
PanelWindow {
id: root
required property var screen
required property string popupName
property int rightMargin: 10
property int topMargin: 1
property int autoHideDelay: Theme.popupAutoHideDelay
default property alias content: contentItem.data
// ── Visibility ────────────────────────────────────────────
property bool logicalOpen: GlobalStates.popups.active === popupName
property bool _keepVisible: false
visible: logicalOpen || _keepVisible
onLogicalOpenChanged: {
if (logicalOpen) {
closeTimer.stop()
_keepVisible = false
openY.start()
openOpacity.start()
} else {
hideTimer.stop()
_keepVisible = true
closeY.start()
closeOpacity.start()
closeTimer.restart()
}
}
// Keeps the Overlay window alive for the duration of the close animation
Timer {
id: closeTimer
interval: 280
onTriggered: root._keepVisible = false
}
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.namespace: "quickshell-popup-" + popupName
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
anchors { top: true; left: true; right: true; bottom: true }
color: Qt.rgba(0, 0, 0, 0)
Timer {
id: hideTimer
interval: root.autoHideDelay
repeat: false
onTriggered: GlobalStates.popups.close()
}
MouseArea {
anchors.fill: parent
onClicked: GlobalStates.popups.close()
}
// ── Slide animations ──────────────────────────────────────
NumberAnimation { id: openY; target: box; property: "y"; to: root.topMargin; duration: 250; easing.type: Easing.OutCubic }
NumberAnimation { id: closeY; target: box; property: "y"; to: root.topMargin - 200; duration: 200; easing.type: Easing.InCubic }
NumberAnimation { id: openOpacity; target: box; property: "opacity"; to: Theme.popupOpacity; duration: 200 }
NumberAnimation { id: closeOpacity;target: box; property: "opacity"; to: 0.0; duration: 200 }
// ── Popup box ─────────────────────────────────────────────
Rectangle {
id: box
x: {
var ax = GlobalStates.popups.anchorX
var centered = ax > 0 ? ax - width / 2 : parent.width - width - root.rightMargin
return Math.max(4, Math.min(parent.width - width - 4, centered))
}
// Initial state: closed (above the bar, transparent)
y: root.topMargin - 40
opacity: 0.0
width: contentItem.implicitWidth + 10
height: contentItem.implicitHeight + 10
color: Theme.popupBackground
radius: Theme.radius * 2
border.color: Theme.popupBorderColor
border.width: Theme.popupBorderWidth
// Absorb clicks so the outer close-on-click doesn't fire inside the box
MouseArea { anchors.fill: parent }
// HoverHandler doesn't lose hover when a child MouseArea grabs a press,
// so clicking buttons won't accidentally start the hide timer.
HoverHandler {
onHoveredChanged: hovered ? hideTimer.stop() : hideTimer.start()
}
Item {
id: contentItem
x: 5; y: 5
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
}
}
}