Krolyxon
Commit f0edd5d

Branches

Refs →

aurora.configquickshellauroraCalPopup.qml

180 lines · 6.6 KB · QML

1import QtQuick
2import QtQuick.Layouts
3import Quickshell
4import Quickshell.Wayland
5import Quickshell.Hyprland
6
7// Month calendar under the clock pills. Monday first, ISO week numbers, scroll / arrows change month.
8PanelWindow {
9 id: win
10 property bool opened: false
11 signal closeRequested()
12
13 visible: opened
14 anchors { top: true; right: true }
15 margins { top: 6; right: 8 }
16 implicitWidth: 300
17 implicitHeight: col.implicitHeight + 24
18 exclusiveZone: 0
19 color: "transparent"
20 WlrLayershell.layer: WlrLayer.Overlay
21 WlrLayershell.namespace: "aurora-calendar"
22 WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
23
24 HyprlandFocusGrab {
25 windows: [win]
26 active: win.opened
27 onCleared: win.closeRequested()
28 }
29
30 SystemClock { id: clock; precision: SystemClock.Seconds }
31
32 property int year: 0
33 property int month: 0 // 0..11
34 function today() {
35 var d = new Date()
36 year = d.getFullYear()
37 month = d.getMonth()
38 }
39 function shift(n) {
40 var m = month + n
41 year += Math.floor(m / 12)
42 month = ((m % 12) + 12) % 12
43 }
44 onOpenedChanged: if (opened) today()
45
46 function isoWeek(d) {
47 var t = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()))
48 var day = t.getUTCDay() || 7
49 t.setUTCDate(t.getUTCDate() + 4 - day)
50 var y0 = new Date(Date.UTC(t.getUTCFullYear(), 0, 1))
51 return Math.ceil(((t - y0) / 86400000 + 1) / 7)
52 }
53 // 6 rows x 7 days starting on the Monday on/before the 1st
54 readonly property var days: {
55 var first = new Date(year, month, 1)
56 var start = new Date(year, month, 1 - ((first.getDay() + 6) % 7))
57 var out = []
58 for (var i = 0; i < 42; i++)
59 out.push(new Date(start.getFullYear(), start.getMonth(), start.getDate() + i))
60 return out
61 }
62 readonly property var now: clock.date
63
64 Rectangle {
65 anchors.fill: parent
66 color: Theme.bgA
67 border.width: 2
68 border.color: Theme.accent
69 focus: true
70 Keys.onEscapePressed: win.closeRequested()
71 Keys.onLeftPressed: win.shift(-1)
72 Keys.onRightPressed: win.shift(1)
73 Keys.onUpPressed: win.shift(-12)
74 Keys.onDownPressed: win.shift(12)
75 Keys.onSpacePressed: win.today()
76
77 MouseArea {
78 anchors.fill: parent
79 onWheel: w => win.shift(w.angleDelta.y > 0 ? -1 : 1)
80 }
81
82 ColumnLayout {
83 id: col
84 anchors.left: parent.left
85 anchors.right: parent.right
86 anchors.top: parent.top
87 anchors.margins: 12
88 spacing: 8
89
90 // big clock
91 Text {
92 text: Qt.formatDateTime(win.now, "HH:mm:ss")
93 color: Theme.fg
94 font.family: Theme.font
95 font.pixelSize: 28
96 font.bold: true
97 }
98 Text {
99 text: Qt.formatDateTime(win.now, "dddd, d MMMM yyyy") + " · w" + win.isoWeek(win.now)
100 color: Theme.dim
101 font.family: Theme.font
102 font.pixelSize: Theme.fontSize - 1
103 }
104
105 Rectangle { Layout.fillWidth: true; Layout.preferredHeight: 1; color: Theme.line }
106
107 RowLayout {
108 Layout.fillWidth: true
109 IconBtn { icon: Theme.iPrev; implicitHeight: 24; onClicked: win.shift(-1) }
110 Text {
111 Layout.fillWidth: true
112 horizontalAlignment: Text.AlignHCenter
113 text: Qt.formatDate(new Date(win.year, win.month, 1), "MMMM yyyy").toUpperCase()
114 color: Theme.accent
115 font.family: Theme.font
116 font.pixelSize: Theme.fontSize
117 font.bold: true
118 font.letterSpacing: 1.5
119 MouseArea {
120 anchors.fill: parent
121 cursorShape: Qt.PointingHandCursor
122 onClicked: win.today()
123 }
124 }
125 IconBtn { icon: Theme.iNext; implicitHeight: 24; onClicked: win.shift(1) }
126 }
127
128 GridLayout {
129 Layout.fillWidth: true
130 columns: 8
131 columnSpacing: 0
132 rowSpacing: 0
133
134 Repeater {
135 model: ["wk", "mo", "tu", "we", "th", "fr", "sa", "su"]
136 delegate: Text {
137 required property var modelData
138 required property int index
139 Layout.fillWidth: true
140 Layout.preferredHeight: 24
141 horizontalAlignment: Text.AlignHCenter
142 verticalAlignment: Text.AlignVCenter
143 text: modelData
144 color: index === 0 ? Theme.line : (index >= 6 ? Theme.accentDim : Theme.dim)
145 font.family: Theme.font
146 font.pixelSize: Theme.fontSize - 2
147 }
148 }
149
150 Repeater {
151 model: 48
152 delegate: Rectangle {
153 id: cell
154 required property int index
155 readonly property bool isWeek: index % 8 === 0
156 // week-number cells use that row's Monday
157 readonly property var d: win.days[Math.floor(index / 8) * 7 + Math.max(0, (index % 8) - 1)]
158 readonly property bool inMonth: d.getMonth() === win.month
159 readonly property bool isToday: !isWeek && d.toDateString() === win.now.toDateString()
160 readonly property bool weekend: (index % 8) >= 6
161 Layout.fillWidth: true
162 Layout.preferredHeight: 28
163 color: isToday ? Theme.accent : "transparent"
164 Text {
165 anchors.centerIn: parent
166 text: cell.isWeek ? win.isoWeek(cell.d) : cell.d.getDate()
167 color: cell.isToday ? Theme.bg
168 : cell.isWeek ? Theme.line
169 : !cell.inMonth ? Theme.surfaceHi
170 : cell.weekend ? Theme.accent : Theme.fg
171 font.family: Theme.font
172 font.pixelSize: cell.isWeek ? Theme.fontSize - 2 : Theme.fontSize
173 font.bold: cell.isToday
174 }
175 }
176 }
177 }
178 }
179 }
180}