nova-shell/shell/applets/ClockApplet.qml

148 lines
4.8 KiB
QML

import QtQuick
import "../services" as S
Column {
id: root
required property color accentColor
required property date currentDate
readonly property var _locale: Qt.locale()
readonly property int _year: currentDate.getFullYear()
readonly property int _month: currentDate.getMonth()
readonly property int _day: currentDate.getDate()
readonly property int _firstDayOfWeek: _locale.firstDayOfWeek
// Week number (ISO 8601)
function _weekNumber(d) {
const tmp = new Date(d.getTime());
tmp.setHours(0, 0, 0, 0);
tmp.setDate(tmp.getDate() + 3 - (tmp.getDay() + 6) % 7);
const jan4 = new Date(tmp.getFullYear(), 0, 4);
return 1 + Math.round(((tmp.getTime() - jan4.getTime()) / 86400000 - 3 + (jan4.getDay() + 6) % 7) / 7);
}
// Build 6x7 grid of day numbers for the current month view
readonly property var _calendarDays: {
const first = new Date(_year, _month, 1);
const firstDow = first.getDay();
// Offset: how many days from previous month to show
let offset = (firstDow - _firstDayOfWeek + 7) % 7;
const startDate = new Date(_year, _month, 1 - offset);
const days = [];
for (let i = 0; i < 42; i++) {
const d = new Date(startDate.getTime() + i * 86400000);
days.push({
day: d.getDate(),
month: d.getMonth(),
year: d.getFullYear(),
isCurrentMonth: d.getMonth() === _month,
isToday: d.getDate() === _day && d.getMonth() === _month && d.getFullYear() === _year
});
}
return days;
}
// Weekday header names (short, locale-aware)
readonly property var _dayNames: {
const names = [];
for (let i = 0; i < 7; i++) {
const idx = (_firstDayOfWeek + i) % 7;
names.push(_locale.dayName(idx, Locale.NarrowFormat));
}
return names;
}
// Date header
Item {
width: parent.width
height: 28
Text {
anchors.left: parent.left
anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter
text: root._locale.standaloneMonthName(root._month, Locale.LongFormat) + " " + root._year
color: root.accentColor
font.pixelSize: S.Theme.fontSize
font.family: S.Theme.fontFamily
font.bold: true
}
Text {
anchors.right: parent.right
anchors.rightMargin: 12
anchors.verticalCenter: parent.verticalCenter
text: "W" + root._weekNumber(root.currentDate)
color: S.Theme.base04
font.pixelSize: S.Theme.fontSize - 2
font.family: S.Theme.fontFamily
}
}
// Day-of-week headers
Row {
anchors.horizontalCenter: parent.horizontalCenter
Repeater {
model: root._dayNames
Text {
required property string modelData
width: Math.floor((root.width - 24) / 7)
height: 18
horizontalAlignment: Text.AlignHCenter
text: modelData
color: S.Theme.base04
font.pixelSize: S.Theme.fontSize - 3
font.family: S.Theme.fontFamily
font.bold: true
}
}
}
// Calendar grid (6 rows x 7 cols)
Grid {
anchors.horizontalCenter: parent.horizontalCenter
columns: 7
Repeater {
model: root._calendarDays
Item {
required property var modelData
width: Math.floor((root.width - 24) / 7)
height: Math.floor((root.width - 24) / 7)
Rectangle {
anchors.centerIn: parent
width: Math.min(parent.width, parent.height) - 4
height: width
radius: width / 2
color: modelData.isToday ? root.accentColor : "transparent"
}
Text {
anchors.centerIn: parent
text: modelData.day
color: modelData.isToday ? S.Theme.base00 : modelData.isCurrentMonth ? S.Theme.base05 : S.Theme.base03
font.pixelSize: S.Theme.fontSize - 1
font.family: S.Theme.fontFamily
font.bold: modelData.isToday
}
}
}
}
// Full date line
Text {
width: parent.width - 24
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
text: root.currentDate.toLocaleDateString(root._locale, Locale.LongFormat)
color: S.Theme.base04
font.pixelSize: S.Theme.fontSize - 2
font.family: S.Theme.fontFamily
}
Item {
width: 1
height: 4
}
}