import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Wayland import "../../config" as Cfg Rectangle { id: root property real panelRadius: Cfg.Config.radius / 2 property int borderWidth: Cfg.Config.panelBorderWidth property bool isTop: true property var targetScreen: null property int barHeight: Cfg.Config.barHeight property int barMargin: Cfg.Config.margin readonly property var t: Cfg.Config.theme readonly property int tzOffsetHours: -timeManager.now.getTimezoneOffset() / 60 width: clockLayout.implicitWidth + 24 height: Cfg.Config.moduleHeight radius: panelRadius color: hoverHandler.hovered ? t.clockBgHover : t.clockBg border.color: hoverHandler.hovered ? t.clockBorderHover : t.clockBorder border.width: borderWidth anchors.right: parent.right Behavior on width { NumberAnimation { duration: 250; easing.type: Easing.OutCubic } } Behavior on color { ColorAnimation { duration: 150 } } Behavior on border.color { ColorAnimation { duration: 150 } } HoverHandler { id: hoverHandler cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: calendarPopup.toggle() } Item { id: timeManager property date now: new Date() Timer { interval: 1000; running: true; repeat: true onTriggered: timeManager.now = new Date() } } RowLayout { id: clockLayout anchors.centerIn: parent spacing: 0 Text { text: "󱑂" color: root.t.clockIcon font.pixelSize: 14 Layout.alignment: Qt.AlignVCenter Layout.rightMargin: 8 } Text { text: timeManager.now.toLocaleTimeString(Qt.locale(), "HH:mm") color: root.t.clockTime font.pixelSize: 13 font.bold: true verticalAlignment: Text.AlignVCenter } Text { id: secondsText text: ":" + timeManager.now.toLocaleTimeString(Qt.locale(), "ss") color: root.t.clockSeconds font.pixelSize: 13 font.bold: true verticalAlignment: Text.AlignVCenter opacity: (hoverHandler.hovered || calendarPopup.popupOpen) ? 1.0 : 0.0 visible: opacity > 0 Layout.preferredWidth: (hoverHandler.hovered || calendarPopup.popupOpen) ? implicitWidth : 0 clip: true Behavior on opacity { NumberAnimation { duration: 200 } } Behavior on Layout.preferredWidth { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } } } PanelWindow { id: calendarPopup screen: root.targetScreen visible: false color: "transparent" property bool popupOpen: false function open() { hideTimer.stop(); visible = true; popupOpen = true } function close() { popupOpen = false; hideTimer.restart() } function toggle() { popupOpen ? close() : open() } Timer { id: hideTimer interval: Cfg.Config.popupAnimDuration + 70 onTriggered: calendarPopup.visible = false } anchors { top: true; bottom: true; left: true; right: true } WlrLayershell.layer: WlrLayer.Top WlrLayershell.namespace: "main-shell-calendar" WlrLayershell.exclusiveZone: -1 mask: popupOpen ? null : _noInput Region { id: _noInput } TapHandler { onTapped: calendarPopup.close() } Item { id: calClipContainer readonly property int cardW: 284 readonly property int screenPad: root.barMargin + 10 x: { let centerX = root.mapToItem(null, 0, 0).x + root.width / 2 let desiredX = centerX - cardW / 2 return Math.max(screenPad, Math.min(desiredX, parent.width - cardW - screenPad)) } width: cardW anchors.top: root.isTop ? parent.top : undefined anchors.bottom: root.isTop ? undefined : parent.bottom anchors.topMargin: root.isTop ? (root.barHeight + 10) : 0 anchors.bottomMargin: root.isTop ? 0 : (root.barHeight + 10) height: parent.height - root.barHeight - 10 clip: true Rectangle { id: card width: parent.width height: cardCol.implicitHeight + 24 anchors.top: root.isTop ? parent.top : undefined anchors.bottom: root.isTop ? undefined : parent.bottom transform: Translate { y: calendarPopup.popupOpen ? 0 : (root.isTop ? -card.height : card.height) Behavior on y { NumberAnimation { duration: Cfg.Config.popupAnimDuration; easing.type: Easing.OutExpo } enabled: calendarPopup.visible } } opacity: calendarPopup.popupOpen ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { duration: 200 } } color: root.t.calCardBg radius: Cfg.Config.popupRadius border.color: root.t.calCardBorder border.width: Cfg.Config.popupBorderWidth layer.enabled: true MouseArea { anchors.fill: parent; propagateComposedEvents: false } ColumnLayout { id: cardCol anchors { left: parent.left; right: parent.right; top: parent.top } anchors.margins: 16 anchors.topMargin: 18 spacing: 14 ColumnLayout { spacing: 2 Layout.fillWidth: true Text { text: timeManager.now.toLocaleDateString(Qt.locale(), "dddd") color: root.t.clockPopupDim font.pixelSize: 12 font.capitalization: Font.AllUppercase Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter } Text { text: timeManager.now.toLocaleDateString(Qt.locale(), "d MMMM yyyy") color: root.t.clockPopupHeader font.pixelSize: 22 font.bold: true Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter } } Rectangle { Layout.fillWidth: true; height: 1; color: root.t.calSeparator } RowLayout { Layout.fillWidth: true Text { text: "‹"; color: root.t.clockPopupDim; font.pixelSize: 18; font.bold: true MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor onClicked: { cal.displayMonth -= 1 if (cal.displayMonth < 0) { cal.displayMonth = 11; cal.displayYear -= 1 } } } } Text { text: new Date(cal.displayYear, cal.displayMonth, 1).toLocaleDateString(Qt.locale(), "MMMM yyyy") color: root.t.clockPopupText font.pixelSize: 13; font.bold: true Layout.fillWidth: true; horizontalAlignment: Text.AlignHCenter } Text { text: "›"; color: root.t.clockPopupDim; font.pixelSize: 18; font.bold: true MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor onClicked: { cal.displayMonth += 1 if (cal.displayMonth > 11) { cal.displayMonth = 0; cal.displayYear += 1 } } } } } Item { id: cal Layout.fillWidth: true implicitHeight: gridCol.implicitHeight property int displayYear: timeManager.now.getFullYear() property int displayMonth: timeManager.now.getMonth() Connections { target: calendarPopup function onPopupOpenChanged() { if (calendarPopup.popupOpen) { cal.displayYear = timeManager.now.getFullYear() cal.displayMonth = timeManager.now.getMonth() } } } ColumnLayout { id: gridCol anchors.fill: parent spacing: 2 Row { spacing: 0 Repeater { model: ["Mo","Tu","We","Th","Fr","Sa","Su"] Text { width: (card.width - 32) / 7 text: modelData; color: root.t.clockPopupDim font.pixelSize: 11; horizontalAlignment: Text.AlignHCenter } } } Repeater { model: 6 Row { id: weekRow property int weekIndex: index Repeater { model: 7 delegate: Item { width: (card.width - 32) / 7; height: 26 readonly property int dayNum: (weekRow.weekIndex * 7 + index) - ((new Date(cal.displayYear, cal.displayMonth, 1).getDay() + 6) % 7) + 1 readonly property bool inMonth: dayNum >= 1 && dayNum <= new Date(cal.displayYear, cal.displayMonth + 1, 0).getDate() readonly property bool isToday: inMonth && dayNum === timeManager.now.getDate() && cal.displayYear === timeManager.now.getFullYear() && cal.displayMonth === timeManager.now.getMonth() Rectangle { anchors.centerIn: parent; width: 22; height: 22; radius: 11 color: isToday ? root.t.accent : "transparent" visible: inMonth } Text { anchors.fill: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: inMonth ? dayNum : "" color: isToday ? root.t.todayText : (index >= 5 ? root.t.clockPopupDim : root.t.clockPopupText) font.pixelSize: 12; font.bold: isToday } } } } } } } Rectangle { Layout.fillWidth: true; height: 1; color: root.t.calSeparator } RowLayout { Layout.fillWidth: true; spacing: 12 Text { text: "󰣐"; color: root.t.clockIcon; font.pixelSize: 16 } ColumnLayout { spacing: 0; Layout.fillWidth: true Text { text: timeManager.now.toLocaleTimeString(Qt.locale(), "t") || "UTC" color: root.t.clockPopupText; font.pixelSize: 12; font.bold: true } Text { text: "UTC " + (root.tzOffsetHours >= 0 ? "+" : "") + root.tzOffsetHours color: root.t.clockPopupDim; font.pixelSize: 10 } } Text { text: timeManager.now.toLocaleTimeString(Qt.locale(), "HH:mm:ss") color: root.t.clockTime; font.pixelSize: 20; font.bold: true; font.family: "monospace" } } Item { height: 4 } } } } } }