add ClockApplet with locale-aware calendar grid, refactor ClockModule with hover+pin
This commit is contained in:
parent
0e9c1723f2
commit
472b4e62ab
3 changed files with 198 additions and 4 deletions
148
shell/applets/ClockApplet.qml
Normal file
148
shell/applets/ClockApplet.qml
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ module applets
|
||||||
# keep-sorted start
|
# keep-sorted start
|
||||||
BacklightApplet 1.0 BacklightApplet.qml
|
BacklightApplet 1.0 BacklightApplet.qml
|
||||||
BatteryApplet 1.0 BatteryApplet.qml
|
BatteryApplet 1.0 BatteryApplet.qml
|
||||||
|
ClockApplet 1.0 ClockApplet.qml
|
||||||
CpuApplet 1.0 CpuApplet.qml
|
CpuApplet 1.0 CpuApplet.qml
|
||||||
DiskApplet 1.0 DiskApplet.qml
|
DiskApplet 1.0 DiskApplet.qml
|
||||||
GpuApplet 1.0 GpuApplet.qml
|
GpuApplet 1.0 GpuApplet.qml
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,59 @@ import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import "." as M
|
import "." as M
|
||||||
import "../services" as S
|
import "../services" as S
|
||||||
|
import "../applets" as C
|
||||||
|
|
||||||
|
M.BarSection {
|
||||||
|
id: root
|
||||||
|
spacing: S.Theme.moduleSpacing
|
||||||
|
tooltip: ""
|
||||||
|
|
||||||
M.BarLabel {
|
|
||||||
SystemClock {
|
SystemClock {
|
||||||
id: clock
|
id: clock
|
||||||
precision: SystemClock.Seconds
|
precision: SystemClock.Seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
font.pixelSize: S.Theme.fontSize + 1
|
property bool _pinned: false
|
||||||
label: Qt.formatDateTime(clock.date, "ddd, dd. MMM HH:mm")
|
readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered
|
||||||
tooltip: Qt.formatDateTime(clock.date, "dddd, dd. MMMM yyyy\nHH:mm:ss")
|
readonly property bool _showPanel: _anyHover || _pinned
|
||||||
|
|
||||||
|
on_AnyHoverChanged: {
|
||||||
|
if (_anyHover)
|
||||||
|
_unpinTimer.stop();
|
||||||
|
else if (_pinned)
|
||||||
|
_unpinTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: _unpinTimer
|
||||||
|
interval: 500
|
||||||
|
onTriggered: root._pinned = false
|
||||||
|
}
|
||||||
|
|
||||||
|
M.BarLabel {
|
||||||
|
font.pixelSize: S.Theme.fontSize + 1
|
||||||
|
label: Qt.formatDateTime(clock.date, "ddd, dd. MMM HH:mm")
|
||||||
|
minText: "Wed, 00. Sep 00:00"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
TapHandler {
|
||||||
|
onTapped: root._pinned = !root._pinned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
M.HoverPanel {
|
||||||
|
id: hoverPanel
|
||||||
|
showPanel: root._showPanel
|
||||||
|
screen: QsWindow.window?.screen ?? null
|
||||||
|
anchorItem: root
|
||||||
|
accentColor: root.accentColor
|
||||||
|
panelNamespace: "nova-clock"
|
||||||
|
panelTitle: Qt.formatTime(clock.date, "HH:mm:ss")
|
||||||
|
contentWidth: 220
|
||||||
|
|
||||||
|
C.ClockApplet {
|
||||||
|
width: hoverPanel.contentWidth
|
||||||
|
accentColor: root.accentColor
|
||||||
|
currentDate: clock.date
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue