123 lines
3.7 KiB
QML
123 lines
3.7 KiB
QML
// GNOME's date/time menu: calendar, ongoing activity and messages in one panel,
|
|
// dropping from the top centre because that is where GNOME put it and the
|
|
// muscle memory is the whole point.
|
|
//
|
|
// A layer-shell surface rather than a PopupWindow because the inline reply
|
|
// fields have to be typeable, and because the clock it hangs from is owned by a
|
|
// different module.
|
|
//
|
|
// Layer-shell centres a surface on whichever axis it is not anchored to, so
|
|
// anchoring only `top` is what puts it in the middle.
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import Quickshell.Hyprland
|
|
import qs.config
|
|
import qs.services
|
|
import qs.widgets
|
|
|
|
PanelWindow {
|
|
id: root
|
|
|
|
readonly property bool ongoingContentFits: panel.ongoingContentFits
|
|
|
|
// "notifications" remains the overlay key so existing keybinds keep their
|
|
// contract; the surface itself now has Agenda, Ongoing and Notifications.
|
|
visible: ShellState.notificationsOpen
|
|
color: "transparent"
|
|
|
|
anchors.top: true
|
|
margins.top: Theme.barHeight + Theme.barGap * 2
|
|
exclusiveZone: 0
|
|
|
|
implicitWidth: 760
|
|
implicitHeight: panel.implicitHeight
|
|
|
|
// The `qs-popover` prefix is matched by a layerrule in hypr/rules.lua.
|
|
WlrLayershell.namespace: "qs-popover-datemenu"
|
|
WlrLayershell.layer: WlrLayer.Overlay
|
|
// OnDemand rather than Exclusive: the panel must be able to take typing for
|
|
// inline replies, but must not steal it while merely visible.
|
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
|
|
|
onVisibleChanged: {
|
|
if (root.visible) {
|
|
CalendarAgenda.refresh();
|
|
if (ShellState.dateMenuPage === "notifications")
|
|
Notifs.markAllRead();
|
|
panel.prepare(ShellState.dateMenuPage);
|
|
openAnim.restart();
|
|
} else {
|
|
panel.reset();
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: ShellState
|
|
function onDateMenuPageChanged(): void {
|
|
if (root.visible && ShellState.dateMenuPage === "notifications")
|
|
Notifs.markAllRead();
|
|
}
|
|
}
|
|
|
|
// Closes the panel when a click lands anywhere else on the desktop.
|
|
HyprlandFocusGrab {
|
|
windows: [root]
|
|
active: root.visible
|
|
onCleared: ShellState.close()
|
|
}
|
|
|
|
Rectangle {
|
|
id: surface
|
|
anchors.fill: parent
|
|
radius: Theme.popoverRadius
|
|
color: Theme.alpha(Theme.bgPopover, Theme.popoverAlpha)
|
|
border.width: 1
|
|
border.color: Theme.alpha(Theme.fg, 0.08)
|
|
|
|
// The prism edge — see widgets/PrismEdge.qml. Sits just inside the 1px
|
|
// border so the two don't fight for the same row of pixels.
|
|
PrismEdge {
|
|
anchors.top: parent.top
|
|
anchors.topMargin: 1
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
inset: parent.radius
|
|
}
|
|
|
|
// Grows out of the top edge it is anchored to, rather than out of its
|
|
// own centre — the panel should look like it came from the bar.
|
|
transform: Scale {
|
|
id: openScale
|
|
origin.x: surface.width / 2
|
|
origin.y: 0
|
|
xScale: 1
|
|
yScale: 1
|
|
}
|
|
|
|
// Escape reaches here by propagating up from whatever holds focus
|
|
// (usually a reply field), so it works in every sub-state.
|
|
Item {
|
|
anchors.fill: parent
|
|
focus: true
|
|
Keys.onEscapePressed: ShellState.close()
|
|
|
|
DateMenuPanel {
|
|
id: panel
|
|
anchors.fill: parent
|
|
}
|
|
}
|
|
}
|
|
|
|
NumberAnimation {
|
|
id: openAnim
|
|
target: openScale
|
|
property: "yScale"
|
|
from: 0.94
|
|
to: 1.0
|
|
duration: Theme.durNormal
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|