Files
quickdots/bar/modules/Clock.qml

343 lines
15 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
import "../../components"
ModuleChip {
id: root
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: TimezoneProvider.toTz(timeManager.now).toLocaleTimeString(Qt.locale(), "HH:mm")
color: root.t.clockTime
font.pixelSize: 13
font.bold: true
verticalAlignment: Text.AlignVCenter
}
Text {
id: secondsText
text: ":" + TimezoneProvider.toTz(timeManager.now).toLocaleTimeString(Qt.locale(), "ss")
color: root.t.clockSeconds
font.pixelSize: 13
font.bold: true
verticalAlignment: Text.AlignVCenter
opacity: (hoverHandler.hovered || calendarPopup.popupOpen) && Cfg.Config.clockExtendEnabled ? 1.0 : 0.0
visible: opacity > 0
Layout.preferredWidth: (hoverHandler.hovered || calendarPopup.popupOpen) && Cfg.Config.clockExtendEnabled ? implicitWidth : 0
clip: true
Behavior on opacity { NumberAnimation { duration: 200 } }
Behavior on Layout.preferredWidth { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
}
}
PopupPanel {
id: calendarPopup
ns: "calendar"
chipRoot: root
cardWidth: 284
PopupCard {
id: card
popupOpen: calendarPopup.popupOpen
isTop: root.isTop
animEnabled: calendarPopup.visible
height: cardCol.implicitHeight + 24
color: root.t.calCardBg
radius: Cfg.Config.popupRadius
border.color: root.t.calCardBorder
border.width: Cfg.Config.popupBorderWidth
layer.enabled: true
component MonthNavButton: Rectangle {
id: navBtn
required property int direction
width: 26; height: 26; radius: 6
color: btnMouse.containsMouse
? (btnMouse.pressed ? root.t.calArrowBgPress : root.t.calArrowBgHover)
: root.t.calArrowBg
scale: btnMouse.pressed ? 0.88 : 1.0
Behavior on color { ColorAnimation { duration: 120 } }
Behavior on scale { NumberAnimation { duration: 100; easing.type: Easing.OutCubic } }
MouseArea {
id: btnMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
function navigate() {
if (slideAnim.running) return
cal.slideDir = navBtn.direction
let m = cal.displayMonth + navBtn.direction
let y = cal.displayYear
if (m > 11) { m = 0; y += 1 }
if (m < 0) { m = 11; y -= 1 }
cal.pendingMonth = m
cal.pendingYear = y
slideAnim.restart()
}
onPressed: { navigate(); holdDelay.restart() }
onReleased: { holdDelay.stop(); holdRepeat.stop() }
onCanceled: { holdDelay.stop(); holdRepeat.stop() }
Timer { id: holdDelay; interval: 400; repeat: false; onTriggered: holdRepeat.restart() }
Timer { id: holdRepeat; interval: 200; repeat: true; onTriggered: btnMouse.navigate() }
}
Text {
anchors.centerIn: parent
text: navBtn.direction < 0 ? "" : ""
color: btnMouse.containsMouse ? root.t.calArrowIconHover : root.t.calArrowIcon
font.pixelSize: 18; font.bold: true
Behavior on color { ColorAnimation { duration: 120 } }
}
}
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: TimezoneProvider.toTz(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: TimezoneProvider.toTz(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
MonthNavButton { direction: -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
}
MonthNavButton { direction: 1 }
}
Item {
id: cal
Layout.fillWidth: true
implicitHeight: gridCol.implicitHeight
property int displayYear: TimezoneProvider.toTz(timeManager.now).getFullYear()
property int displayMonth: TimezoneProvider.toTz(timeManager.now).getMonth()
property int pendingYear: displayYear
property int pendingMonth: displayMonth
property int slideDir: 1
Connections {
target: calendarPopup
function onPopupOpenChanged() {
if (calendarPopup.popupOpen) {
let tzd = TimezoneProvider.toTz(timeManager.now)
cal.displayYear = tzd.getFullYear()
cal.displayMonth = tzd.getMonth()
}
}
}
SequentialAnimation {
id: slideAnim
NumberAnimation {
target: gridCol; property: "x"
to: -cal.slideDir * cal.width
duration: 80; easing.type: Easing.InCubic
}
ScriptAction {
script: {
cal.displayMonth = cal.pendingMonth
cal.displayYear = cal.pendingYear
gridCol.x = cal.slideDir * cal.width
}
}
NumberAnimation {
target: gridCol; property: "x"
to: 0
duration: 100; easing.type: Easing.OutCubic
}
}
Item {
id: calGridClip
anchors.fill: parent
clip: true
ColumnLayout {
id: gridCol
width: parent.width
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: {
let tzd = TimezoneProvider.toTz(timeManager.now)
return inMonth && dayNum === tzd.getDate()
&& cal.displayYear === tzd.getFullYear()
&& cal.displayMonth === tzd.getMonth()
}
Rectangle {
anchors.centerIn: parent; width: 22; height: 22; radius: 11
color: isToday ? root.t.accent : "transparent"
border.color: isToday ? root.t.borderToday : "transparent"
border.width: isToday ? 2 : 0
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
Layout.leftMargin: 6
Layout.rightMargin: 6
Text {
text: "UTC " + (TimezoneProvider.tzOffsetHours() >= 0 ? "+" : "") + TimezoneProvider.tzOffsetHours()
color: root.t.clockPopupUtc
font.pixelSize: 13
font.bold: true
font.family: "monospace"
}
Item { Layout.fillWidth: true }
Text {
text: {
let d = TimezoneProvider.toTz(timeManager.now)
let month = d.getMonth() + 1
let day = d.getDate()
let year = d.getFullYear()
if (Cfg.Config.dateFormat === "DMY")
return day + "/" + month + "/" + year
return month + "/" + day + "/" + year
}
color: root.t.clockPopupDate
font.pixelSize: 14
font.bold: true
font.family: "monospace"
}
}
Item { height: 4 }
}
}
}
}