// The one Signal Glass layer surface. Transient curated events briefly take // precedence; when they expire, an explicitly revealed focus session returns. import Quickshell import Quickshell.Hyprland import Quickshell.Wayland import QtQuick import qs.config import qs.services PanelWindow { id: root property var modelData: null readonly property var event: StatusEvents.activeEvent readonly property string eventMonitor: root.event?.monitorName ?? Hyprland.focusedMonitor?.name ?? "" readonly property bool eventBelongsHere: StatusEvents.active && (!root.eventMonitor || !root.modelData || root.modelData.name === root.eventMonitor) readonly property bool focusBelongsHere: FocusSession.active && FocusSession.capsuleVisible && (!FocusSession.monitorName || !root.modelData || root.modelData.name === FocusSession.monitorName) readonly property bool showingEvent: root.eventBelongsHere readonly property bool requestedVisible: root.showingEvent || root.focusBelongsHere screen: root.modelData anchors.top: true // The gap ALONE, not the bar height plus the gap. exclusiveZone 0 means // "reserve nothing, but respect what others reserved", so this surface // already begins below the bar's zone -- adding the bar height here counted // it twice and left the surface floating 48px under the bar instead of 12. margins.top: Theme.barGap * 2 exclusiveZone: 0 implicitWidth: root.showingEvent ? 388 : 438 implicitHeight: root.showingEvent ? 76 : 132 color: "transparent" WlrLayershell.namespace: "qs-signal-glass" WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.keyboardFocus: WlrKeyboardFocus.None property bool mapped: false visible: root.mapped onRequestedVisibleChanged: { if (root.requestedVisible) { unmapTimer.stop(); root.mapped = true; entrance.restart(); } else if (root.mapped) { unmapTimer.restart(); } } onShowingEventChanged: { if (root.requestedVisible) entrance.restart(); } Component.onCompleted: { if (root.requestedVisible) root.mapped = true; } Timer { id: unmapTimer interval: Theme.durNormal onTriggered: root.mapped = false } Loader { id: content anchors.fill: parent sourceComponent: root.showingEvent ? eventComponent : focusComponent opacity: root.requestedVisible ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Theme.durNormal; easing.type: Easing.OutCubic } } transform: Translate { id: slide y: root.requestedVisible ? 0 : -6 Behavior on y { NumberAnimation { duration: Theme.durNormal; easing.type: Easing.OutCubic } } } } Component { id: eventComponent EventCard { event: root.event } } Component { id: focusComponent FocusCard {} } ParallelAnimation { id: entrance NumberAnimation { target: content; property: "opacity"; from: 0; to: 1; duration: Theme.durNormal; easing.type: Easing.OutCubic } NumberAnimation { target: slide; property: "y"; from: -6; to: 0; duration: Theme.durNormal; easing.type: Easing.OutCubic } } }