Files
quickdots/bar/modules/Clock.qml
2026-04-24 02:14:35 +00:00

332 lines
14 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 }
}
}
}
}
}