Krolyxon
Commit 8ab0f85

Branches

Refs →

aurora.configquickshellauroraCalPopup.qml

187 lines · 6.8 KB · QML

1import QtQuick
2import QtQuick.Layouts
3import Quickshell
4import Quickshell.Wayland
5import Quickshell.Hyprland
6
7// Month calendar under the bar clock: Doto month, Monday first, ISO week numbers, today in a primary circle.
8// Scroll / arrows change the month, up / down the year, space = back to today, Esc closes.
9// After glyph-rice (github.com/Nebelung-cloud/glyph-rice, GPL-3.0), cal/Cal.qml.
10PanelWindow {
11 id: win
12 property bool opened: false
13 signal closeRequested()
14
15 property real t: opened ? 1 : 0
16 Behavior on t { NumberAnimation { duration: Theme.t; easing.type: Easing.BezierSpline; easing.bezierCurve: Theme.glide } }
17
18 visible: t > 0
19 anchors { top: true; right: true }
20 margins { top: 6; right: 8 }
21 implicitWidth: card.width
22 implicitHeight: card.height + 20 // room for the slide
23 exclusiveZone: 0
24 color: "transparent"
25 mask: Region { item: card }
26 WlrLayershell.layer: WlrLayer.Overlay
27 WlrLayershell.namespace: "aurora-calendar"
28 WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
29
30 HyprlandFocusGrab {
31 windows: [win]
32 active: win.opened
33 onCleared: win.closeRequested()
34 }
35
36 SystemClock { id: clock; precision: SystemClock.Minutes }
37 readonly property var en: Qt.locale("en_US")
38
39 property int year: 0
40 property int month: 0 // 0..11
41 function today() {
42 var d = new Date()
43 year = d.getFullYear()
44 month = d.getMonth()
45 }
46 function shift(n) {
47 var m = month + n
48 year += Math.floor(m / 12)
49 month = ((m % 12) + 12) % 12
50 }
51 onOpenedChanged: if (opened) today()
52
53 function isoWeek(d) {
54 var t = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()))
55 var day = t.getUTCDay() || 7
56 t.setUTCDate(t.getUTCDate() + 4 - day)
57 var y0 = new Date(Date.UTC(t.getUTCFullYear(), 0, 1))
58 return Math.ceil(((t - y0) / 86400000 + 1) / 7)
59 }
60 // 6 rows x 7 days starting on the Monday on/before the 1st
61 readonly property var days: {
62 var first = new Date(year, month, 1)
63 var start = new Date(year, month, 1 - ((first.getDay() + 6) % 7))
64 var out = []
65 for (var i = 0; i < 42; i++)
66 out.push(new Date(start.getFullYear(), start.getMonth(), start.getDate() + i))
67 return out
68 }
69 readonly property var now: clock.date
70
71 Rectangle {
72 id: card
73 y: -16 * (1 - win.t)
74 opacity: win.t
75 width: 384
76 height: col.implicitHeight + 44
77 radius: 36
78 color: Qt.rgba(Theme.s1.r, Theme.s1.g, Theme.s1.b, Theme.panelAlpha)
79 border.width: 1
80 border.color: Theme.s2
81 focus: true
82 Keys.onEscapePressed: win.closeRequested()
83 Keys.onLeftPressed: win.shift(-1)
84 Keys.onRightPressed: win.shift(1)
85 Keys.onUpPressed: win.shift(-12)
86 Keys.onDownPressed: win.shift(12)
87 Keys.onSpacePressed: win.today()
88
89 MouseArea {
90 anchors.fill: parent
91 onWheel: w => win.shift(w.angleDelta.y > 0 ? -1 : 1)
92 }
93
94 ColumnLayout {
95 id: col
96 x: 22
97 y: 22
98 width: parent.width - 44
99 spacing: 14
100
101 Lb {
102 Layout.leftMargin: 6
103 px: 10
104 text: win.en.toString(win.now, "dddd · d MMMM yyyy") + " · week " + win.isoWeek(win.now)
105 }
106
107 RowLayout {
108 Layout.fillWidth: true
109 Layout.leftMargin: 6
110 spacing: 12
111 Num {
112 text: win.en.standaloneMonthName(win.month, Locale.ShortFormat)
113 font.pixelSize: 44
114 MouseArea {
115 anchors.fill: parent
116 cursorShape: Qt.PointingHandCursor
117 onClicked: win.today()
118 }
119 }
120 Lb {
121 Layout.alignment: Qt.AlignVCenter
122 text: win.year
123 }
124 Item { Layout.fillWidth: true }
125 IconBtn { implicitHeight: 40; icon: Theme.iChevronLeft; onClicked: win.shift(-1) }
126 IconBtn { implicitHeight: 40; icon: Theme.iChevron; onClicked: win.shift(1) }
127 }
128
129 Grid {
130 id: grid
131 Layout.fillWidth: true
132 Layout.preferredWidth: 1
133 columns: 8
134 readonly property real cw: width / 8
135
136 Repeater {
137 model: ["wk", "m", "t", "w", "t", "f", "s", "s"]
138 delegate: Lb {
139 required property var modelData
140 required property int index
141 width: grid.cw
142 height: 28
143 horizontalAlignment: Text.AlignHCenter
144 verticalAlignment: Text.AlignVCenter
145 text: modelData
146 px: 10
147 color: index === 0 ? Theme.mute : Theme.grey
148 }
149 }
150 Repeater {
151 model: 48
152 delegate: Item {
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 width: grid.cw
162 height: 40
163 Rectangle {
164 visible: cell.isToday
165 anchors.centerIn: parent
166 width: 34
167 height: 34
168 radius: 17
169 color: Theme.fill
170 }
171 Text {
172 anchors.centerIn: parent
173 text: cell.isWeek ? win.isoWeek(cell.d) : cell.d.getDate()
174 color: cell.isToday ? Theme.fillFg
175 : cell.isWeek ? Theme.mute
176 : !cell.inMonth ? Theme.mute
177 : cell.weekend ? Theme.grey : Theme.fg
178 font.family: Theme.mono
179 font.pixelSize: cell.isWeek ? 10 : 13
180 font.bold: cell.isToday
181 }
182 }
183 }
184 }
185 }
186 }
187}