Skip to content

Commit c66fd1d

Browse files
xfalcoxclaude
andcommitted
feat: add upcoming events sidebar widget with independent configs
Add scrollable upcoming events widget to sidebar (below notifications), default on. Dashboard upcoming events default off. Split upcomingHours into dashUpcomingHours (24h) and sidebarUpcomingHours (5 days) for independent control. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent a31b6fb commit c66fd1d

7 files changed

Lines changed: 133 additions & 10 deletions

File tree

config/Config.qml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ Singleton {
225225
mediaUpdateInterval: dashboard.mediaUpdateInterval,
226226
resourceUpdateInterval: dashboard.resourceUpdateInterval,
227227
dragThreshold: dashboard.dragThreshold,
228+
showUpcoming: dashboard.showUpcoming,
228229
performance: {
229230
showBattery: dashboard.performance.showBattery,
230231
showGpu: dashboard.performance.showGpu,
@@ -345,6 +346,7 @@ Singleton {
345346
function serializeSidebar(): var {
346347
return {
347348
enabled: sidebar.enabled,
349+
showUpcoming: sidebar.showUpcoming,
348350
dragThreshold: sidebar.dragThreshold
349351
};
350352
}
@@ -368,7 +370,8 @@ Singleton {
368370
enabled: services.calendar.enabled,
369371
command: services.calendar.command,
370372
agendaDays: services.calendar.agendaDays,
371-
upcomingHours: services.calendar.upcomingHours,
373+
dashUpcomingHours: services.calendar.dashUpcomingHours,
374+
sidebarUpcomingHours: services.calendar.sidebarUpcomingHours,
372375
reminderMinutes: services.calendar.reminderMinutes,
373376
refreshInterval: services.calendar.refreshInterval
374377
}

config/DashboardConfig.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ JsonObject {
1010
property bool showMedia: true
1111
property bool showPerformance: true
1212
property bool showWeather: true
13+
property bool showUpcoming: false
1314
property Sizes sizes: Sizes {}
1415
property Performance performance: Performance {}
1516

config/ServiceConfig.qml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ JsonObject {
2626
property bool enabled: false // Requires gws CLI in PATH
2727
property string command: "gws" // Path or name of the gws CLI binary
2828
property int agendaDays: 30 // How many days ahead to fetch events
29-
property int upcomingHours: 24 // Hours ahead to show in upcoming list
29+
property int dashUpcomingHours: 24 // Hours ahead to show in dashboard upcoming list
30+
property int sidebarUpcomingHours: 120 // Hours ahead to show in sidebar upcoming list
3031
property int reminderMinutes: 10 // Minutes before event to send notification, 0 to disable
3132
property int refreshInterval: 900 // Refresh interval in seconds
3233
}

config/SidebarConfig.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Quickshell.Io
22

33
JsonObject {
44
property bool enabled: true
5+
property bool showUpcoming: true
56
property int dragThreshold: 80
67
property Sizes sizes: Sizes {}
78

modules/dashboard/Dash.qml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ GridLayout {
9191
Rect {
9292
Layout.row: 0
9393
Layout.column: 5
94-
Layout.rowSpan: GCalendar.upcoming.length > 0 ? 3 : 2
94+
Layout.rowSpan: Config.dashboard.showUpcoming && GCalendar.upcomingDash.length > 0 ? 3 : 2
9595
Layout.preferredWidth: media.implicitWidth
9696
Layout.fillHeight: true
9797

@@ -110,7 +110,7 @@ GridLayout {
110110
Layout.fillWidth: true
111111
Layout.preferredHeight: eventsCol.implicitHeight
112112

113-
visible: GCalendar.upcoming.length > 0
113+
visible: Config.dashboard.showUpcoming && GCalendar.upcomingDash.length > 0
114114
radius: Appearance.rounding.large
115115

116116
ColumnLayout {
@@ -130,7 +130,7 @@ GridLayout {
130130
}
131131

132132
Repeater {
133-
model: GCalendar.upcoming
133+
model: GCalendar.upcomingDash
134134

135135
RowLayout {
136136
id: eventRow
@@ -163,7 +163,7 @@ GridLayout {
163163
StyledText {
164164
Layout.fillWidth: true
165165
text: {
166-
let line = GCalendar.formatEventTime(eventRow.modelData);
166+
let line = GCalendar.formatEventTime(eventRow.modelData, Config.services.calendar.dashUpcomingHours);
167167
if (eventRow.modelData.location)
168168
line += ` · ${eventRow.modelData.location}`;
169169
return line;

modules/sidebar/Content.qml

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
pragma ComponentBehavior: Bound
2+
13
import qs.components
4+
import qs.components.containers
5+
import qs.components.controls
26
import qs.services
37
import qs.config
48
import QtQuick
@@ -29,6 +33,111 @@ Item {
2933
}
3034
}
3135

36+
// Upcoming events
37+
StyledRect {
38+
Layout.fillWidth: true
39+
Layout.preferredHeight: Math.min(upcomingCol.implicitHeight, 300)
40+
41+
visible: Config.sidebar.showUpcoming && GCalendar.enabled && GCalendar.upcomingSidebar.length > 0
42+
radius: Appearance.rounding.normal
43+
color: Colours.tPalette.m3surfaceContainerLow
44+
45+
ColumnLayout {
46+
id: upcomingHeader
47+
48+
anchors.top: parent.top
49+
anchors.left: parent.left
50+
anchors.right: parent.right
51+
anchors.margins: Appearance.padding.normal
52+
53+
StyledText {
54+
Layout.topMargin: Appearance.padding.small
55+
text: qsTr("Upcoming Events")
56+
color: Colours.palette.m3primary
57+
font.pointSize: Appearance.font.size.small
58+
font.weight: 600
59+
}
60+
}
61+
62+
StyledFlickable {
63+
id: upcomingView
64+
65+
clip: true
66+
anchors.top: upcomingHeader.bottom
67+
anchors.left: parent.left
68+
anchors.right: parent.right
69+
anchors.bottom: parent.bottom
70+
anchors.margins: Appearance.padding.normal
71+
anchors.topMargin: Appearance.spacing.small
72+
73+
flickableDirection: Flickable.VerticalFlick
74+
contentWidth: width
75+
contentHeight: upcomingCol.implicitHeight
76+
77+
StyledScrollBar.vertical: StyledScrollBar {
78+
flickable: upcomingView
79+
}
80+
81+
ColumnLayout {
82+
id: upcomingCol
83+
84+
width: parent.width
85+
spacing: Appearance.spacing.small
86+
87+
Repeater {
88+
model: GCalendar.upcomingSidebar
89+
90+
RowLayout {
91+
id: sidebarEventRow
92+
93+
required property var modelData
94+
95+
Layout.fillWidth: true
96+
spacing: Appearance.spacing.small
97+
98+
Rectangle {
99+
width: 3
100+
Layout.fillHeight: true
101+
radius: 1.5
102+
color: Colours.palette.m3tertiary
103+
}
104+
105+
ColumnLayout {
106+
Layout.fillWidth: true
107+
spacing: 0
108+
109+
StyledText {
110+
Layout.fillWidth: true
111+
text: sidebarEventRow.modelData.summary
112+
color: Colours.palette.m3onSurface
113+
font.pointSize: Appearance.font.size.small
114+
font.weight: 500
115+
elide: Text.ElideRight
116+
}
117+
118+
StyledText {
119+
Layout.fillWidth: true
120+
text: {
121+
let line = GCalendar.formatEventTime(sidebarEventRow.modelData, Config.services.calendar.sidebarUpcomingHours);
122+
if (sidebarEventRow.modelData.location)
123+
line += ` · ${sidebarEventRow.modelData.location}`;
124+
return line;
125+
}
126+
color: Colours.palette.m3onSurfaceVariant
127+
font.pointSize: Appearance.font.size.small * 0.9
128+
elide: Text.ElideRight
129+
}
130+
}
131+
}
132+
}
133+
134+
Item {
135+
Layout.preferredHeight: Appearance.padding.small
136+
}
137+
}
138+
}
139+
}
140+
32141
StyledRect {
33142
Layout.topMargin: Appearance.padding.large - layout.spacing
34143
Layout.fillWidth: true

services/GCalendar.qml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ Singleton {
1818
s.add(ev.dateKey);
1919
return s;
2020
}
21-
readonly property list<var> upcoming: {
21+
readonly property list<var> upcomingDash: {
2222
const now = Date.now();
23-
const cutoff = now + Config.services.calendar.upcomingHours * 3600000;
23+
const cutoff = now + Config.services.calendar.dashUpcomingHours * 3600000;
24+
return events.filter(ev => {
25+
const t = ev.startTime;
26+
return t >= now && t < cutoff;
27+
});
28+
}
29+
readonly property list<var> upcomingSidebar: {
30+
const now = Date.now();
31+
const cutoff = now + Config.services.calendar.sidebarUpcomingHours * 3600000;
2432
return events.filter(ev => {
2533
const t = ev.startTime;
2634
return t >= now && t < cutoff;
@@ -47,9 +55,9 @@ Singleton {
4755
: Qt.formatDateTime(d, "hh:mm");
4856
}
4957

50-
function formatEventTime(ev: var): string {
58+
function formatEventTime(ev: var, upcomingHours: int): string {
5159
let parts = [];
52-
if (Config.services.calendar.upcomingHours > 24) {
60+
if (upcomingHours > 24) {
5361
const d = new Date(ev.start);
5462
const target = ev.isAllDay ? new Date(ev.start + "T00:00:00") : d;
5563
const now = new Date();

0 commit comments

Comments
 (0)