328 lines
14 KiB
QML
328 lines
14 KiB
QML
import QtQuick
|
||
import QtQuick.Layouts
|
||
import Quickshell
|
||
import Quickshell.Wayland
|
||
|
||
import "../../config" as Cfg
|
||
import "../../components"
|
||
|
||
ModuleChip {
|
||
id: root
|
||
|
||
chipColor: t.clockBg
|
||
chipColorHovered: t.clockBgHover
|
||
chipBorderColor: t.clockBorder
|
||
chipBorderColorHovered: t.clockBorderHover
|
||
chipHoverEnabled: true
|
||
chipTapAction: () => calendarPopup.toggle()
|
||
chipAnimWidth: true
|
||
|
||
width: clockLayout.implicitWidth + 24
|
||
radius: panelRadius
|
||
|
||
Item {
|
||
id: timeManager
|
||
property date now: new Date()
|
||
property int lastSecond: -1
|
||
Timer {
|
||
interval: 10; running: true; repeat: true
|
||
onTriggered: {
|
||
let d = new Date()
|
||
let s = d.getSeconds()
|
||
if (s !== timeManager.lastSecond) {
|
||
timeManager.lastSecond = s
|
||
timeManager.now = d
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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: (root.hovered || calendarPopup.popupOpen) && Cfg.Config.clockExtendEnabled ? 1.0 : 0.0
|
||
visible: opacity > 0
|
||
Layout.preferredWidth: (root.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 } }
|
||
}
|
||
}
|
||
|
||
StandardPopup {
|
||
id: calendarPopup
|
||
ns: "calendar"
|
||
chipRoot: root
|
||
cardWidth: 284
|
||
cardColor: t.calCardBg
|
||
cardBorderColor: t.calCardBorder
|
||
cardHeight: cardCol.implicitHeight + 28
|
||
|
||
component MonthNavButton: Rectangle {
|
||
id: navBtn
|
||
required property int direction
|
||
|
||
width: 26; height: 26; radius: 8
|
||
|
||
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: (calendarPopup.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: (calendarPopup.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.calTodayBg : "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 }
|
||
}
|
||
}
|
||
}
|