Krolyxon
Commit 35754e3

Branches

Refs →

aurora35754e3

aurora: add calendar popup on the date/clock pills

Author
krolyxon
Date
2026-10-17 15:00 +0530
Parent
4c93d7a
Commit
35754e37a0fb2876f31ed6c0d81a30360b83b507

+209 −8

3 files changed

  1. M .config/quickshell/aurora/Bar.qml +3 −2
  2. A .config/quickshell/aurora/CalPopup.qml +180 −0
  3. M .config/quickshell/aurora/shell.qml +26 −6
M .config/quickshell/aurora/Bar.qml View
@@ -11,6 +11,7 @@ PanelWindow {
1111 required property var modelData
1212 screen: modelData
1313 signal togglePanel(string name, var scr)
14+ signal toggleOverlay(string name, var scr)
1415
1516 anchors { top: true; left: true; right: true }
1617 implicitHeight: Theme.barHeight
@@ -146,12 +147,12 @@ PanelWindow {
146147 BarPill {
147148 icon: Theme.iCal
148149 text: Qt.formatDateTime(clock.date, "dd MMM yyyy")
149− onActivated: bar.togglePanel("home", bar.modelData)
150+ onActivated: bar.toggleOverlay("cal", bar.modelData)
150151 }
151152 BarPill {
152153 icon: Theme.iClock
153154 text: Qt.formatDateTime(clock.date, "HH:mm")
154− onActivated: bar.togglePanel("home", bar.modelData)
155+ onActivated: bar.toggleOverlay("cal", bar.modelData)
155156 }
156157 }
157158 }
A .config/quickshell/aurora/CalPopup.qml View
@@ -0,0 +1,180 @@
1+import QtQuick
2+import QtQuick.Layouts
3+import Quickshell
4+import Quickshell.Wayland
5+import Quickshell.Hyprland
6+
7+// Month calendar under the clock pills. Monday first, ISO week numbers, scroll / arrows change month.
8+PanelWindow {
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+}
M .config/quickshell/aurora/shell.qml View
@@ -34,10 +34,20 @@ ShellRoot {
3434 lastTab = tabNames[tab]
3535 lastScreen = panelScreen ? panelScreen.name : ""
3636 }
37− function toggleOverlay(name) {
38− if (overlay === name) { overlay = ""; return }
37+ property double overlayClosedAt: 0
38+ property string lastOverlay: ""
39+ function closeOverlay() {
40+ if (overlay === "") return
41+ lastOverlay = overlay
42+ overlayClosedAt = Date.now()
43+ overlay = ""
44+ }
45+ // scr: where to open (defaults to the focused monitor). Same instant re-open guard as togglePanel.
46+ function toggleOverlay(name, scr) {
47+ if (overlay === name) { closeOverlay(); return }
48+ if (lastOverlay === name && Date.now() - overlayClosedAt < 250) return
3949 close()
40− overlayScreen = focusedScreen()
50+ overlayScreen = scr ? scr : focusedScreen()
4151 overlay = name
4252 }
4353 function openTab(name) {
@@ -64,7 +74,10 @@ ShellRoot {
6474
6575 Variants {
6676 model: Quickshell.screens
67− Bar { onTogglePanel: (name, scr) => root.togglePanel(name, scr) }
77+ Bar {
78+ onTogglePanel: (name, scr) => root.togglePanel(name, scr)
79+ onToggleOverlay: (name, scr) => root.toggleOverlay(name, scr)
80+ }
6881 }
6982
7083 ControlPanel {
@@ -76,16 +89,22 @@ ShellRoot {
7689 onOverlayRequested: name => { root.close(); root.toggleOverlay(name) }
7790 }
7891
92+ CalPopup {
93+ screen: root.overlayScreen
94+ opened: root.overlay === "cal"
95+ onCloseRequested: root.closeOverlay()
96+ }
97+
7998 ClipWindow {
8099 screen: root.overlayScreen
81100 opened: root.overlay === "clip"
82− onCloseRequested: root.overlay = ""
101+ onCloseRequested: root.closeOverlay()
83102 }
84103
85104 WallPicker {
86105 screen: root.overlayScreen
87106 opened: root.overlay === "wall"
88− onCloseRequested: root.overlay = ""
107+ onCloseRequested: root.closeOverlay()
89108 }
90109
91110 NotifPopups { screen: root.focusedScr; suppressed: root.opened }
@@ -110,6 +129,7 @@ ShellRoot {
110129 function dnd(): void { Notifs.dnd = !Notifs.dnd }
111130 function shot(mode: string): void { root.close(); Shot.take(mode) } // region | window | output | ocr
112131 function shotsave(): void { Shot.save = !Shot.save }
132+ function cal(): void { root.toggleOverlay("cal") }
113133 function clip(): void { root.toggleOverlay("clip") }
114134 function wall(): void { root.toggleOverlay("wall") }
115135 function wallrandom(): void { Wall.random() }