From 8d358ac849b5fd5cdee63c850099565db07152bd Mon Sep 17 00:00:00 2001 From: Trevor Arjeski Date: Wed, 1 Apr 2026 10:41:41 +0000 Subject: [PATCH] Added calendar widget The widget is an embedded gtk4 calendar which can be styled as such. Written with the assistance of Qwen Code. --- README.md | 1 + data/style/style.scss | 2 + data/style/widgets/calendar.scss | 34 +++++ man/swaync.5.scd | 47 +++++++ src/configSchema.json | 45 ++++++ .../widgets/calendar/calendar.vala | 131 ++++++++++++++++++ src/controlCenter/widgets/factory.vala | 3 + src/meson.build | 2 + 8 files changed, 265 insertions(+) create mode 100644 data/style/widgets/calendar.scss create mode 100644 src/controlCenter/widgets/calendar/calendar.vala diff --git a/README.md b/README.md index b16b3ebd..5dd493e4 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ These widgets can be customized, added, removed and even reordered - Do Not Disturb - Notifications (Will always be visible) - Label +- Calendar - Mpris (Media player controls for Spotify, Firefox, Chrome, etc...) - Menubar with dropdown and buttons - Button grid diff --git a/data/style/style.scss b/data/style/style.scss index bd05547c..409f8695 100644 --- a/data/style/style.scss +++ b/data/style/style.scss @@ -398,3 +398,5 @@ notificationwindow, blankwindow { @import "widgets/backlight"; /* Inhibitors widget */ @import "widgets/inhibitors"; +/* Calendar widget */ +@import "widgets/calendar"; diff --git a/data/style/widgets/calendar.scss b/data/style/widgets/calendar.scss new file mode 100644 index 00000000..db08f599 --- /dev/null +++ b/data/style/widgets/calendar.scss @@ -0,0 +1,34 @@ +:root { + --widget-calendar-font-size-day: 1.1rem; + --widget-calendar-font-size-date: 1.4rem; +} + +.widget-calendar { + padding: 12px; + margin: 8px; + + .calendar-container { + .day-label { + font-size: var(--widget-calendar-font-size-day); + font-weight: bold; + color: var(--text-color); + margin-bottom: 2px; + } + + .date-label { + font-size: var(--widget-calendar-font-size-date); + color: var(--text-color); + opacity: 0.8; + margin-bottom: 8px; + } + + calendar { + background: rgba(var(--noti-bg), var(--noti-bg-alpha)); + border: var(--border); + border-radius: var(--border-radius); + padding: 8px; + color: var(--text-color); + font-size: 0.9rem; + } + } +} diff --git a/man/swaync.5.scd b/man/swaync.5.scd index 823b7dae..c408483c 100644 --- a/man/swaync.5.scd +++ b/man/swaync.5.scd @@ -341,6 +341,8 @@ config file to be able to detect config errors #END pulse-audio *backlight*++ optional: true ++ + *calendar*++ + optional: true ++ *inhibitors*++ optional: true ++ description: ++ @@ -738,6 +740,51 @@ config file to be able to detect config errors default: 0 ++ description: Lowest possible value for brightness ++ description: Slider to control screen brightness ++ + *calendar*++ + type: object ++ + css classes: ++ + widget-calendar ++ + calendar-container ++ + day-label ++ + date-label ++ + properties: ++ + show-day-label: ++ + type: bool ++ + optional: true ++ + default: true ++ + description: Whether to show the day of the week label ++ + show-date-label: ++ + type: bool ++ + optional: true ++ + default: true ++ + description: Whether to show the date label ++ + show-heading: ++ + type: bool ++ + optional: true ++ + default: true ++ + description: Whether to show the month/year heading in the calendar grid ++ + show-day-names: ++ + type: bool ++ + optional: true ++ + default: true ++ + description: Whether to show the day of week abbreviations in the calendar grid ++ + show-week-numbers: ++ + type: bool ++ + optional: true ++ + default: false ++ + description: Whether to show ISO week numbers in the calendar grid ++ + day-format: ++ + type: string ++ + optional: true ++ + default: "%A" ++ + description: strftime format for the day label (e.g. "Monday") ++ + date-format: ++ + type: string ++ + optional: true ++ + default: "%B %-d, %Y" ++ + description: strftime format for the date label (e.g. "January 1, 2024") ++ + description: A calendar widget displaying the day, date, and a GTK4 calendar grid. ++ + Today's date is automatically marked. Clicking a day updates the labels. ++ *inhibitors*++ type: object ++ css class: widget-inhibitors ++ diff --git a/src/configSchema.json b/src/configSchema.json index c6924a14..5c2c673e 100644 --- a/src/configSchema.json +++ b/src/configSchema.json @@ -423,6 +423,9 @@ "^inhibitors(#[a-zA-Z0-9_-]{1,}){0,1}?$": { "$comment": "References the widget structure from \"widgets\" below", "$ref": "#/widgets/inhibitors" + }, + "^calendar(#[a-zA-Z0-9_-]{1,}){0,1}?$": { + "$ref": "#/widgets/calendar" } } } @@ -790,6 +793,48 @@ "default": "Clear All" } } + }, + "calendar": { + "type": "object", + "description": "Control Center Calendar Widget", + "additionalProperties": false, + "properties": { + "show-day-label": { + "type": "boolean", + "description": "Whether to show the day label (e.g., \"Monday\")", + "default": true + }, + "show-date-label": { + "type": "boolean", + "description": "Whether to show the date label (e.g., \"January 1, 2024\")", + "default": true + }, + "show-heading": { + "type": "boolean", + "description": "Whether to show the month/year heading in the calendar grid", + "default": true + }, + "show-day-names": { + "type": "boolean", + "description": "Whether to show the day of week abbreviations in the calendar grid", + "default": true + }, + "show-week-numbers": { + "type": "boolean", + "description": "Whether to show ISO week numbers in the calendar grid", + "default": false + }, + "day-format": { + "type": "string", + "description": "strftime format for the day label", + "default": "%A" + }, + "date-format": { + "type": "string", + "description": "strftime format for the date label", + "default": "%B %-d, %Y" + } + } } } } diff --git a/src/controlCenter/widgets/calendar/calendar.vala b/src/controlCenter/widgets/calendar/calendar.vala new file mode 100644 index 00000000..4195dfe8 --- /dev/null +++ b/src/controlCenter/widgets/calendar/calendar.vala @@ -0,0 +1,131 @@ +namespace SwayNotificationCenter.Widgets { + public class Calendar : BaseWidget { + public override string widget_name { + get { + return "calendar"; + } + } + + Gtk.Calendar calendar; + Gtk.Label date_label; + Gtk.Label day_label; + + string day_format = "%A"; + string date_format = "%B %-d, %Y"; + + public Calendar (string suffix) { + base (suffix); + + Json.Object ?config = get_config (this); + + var container = new Gtk.Box (Gtk.Orientation.VERTICAL, 6); + container.add_css_class ("calendar-container"); + container.set_hexpand (true); + + day_label = new Gtk.Label (null); + day_label.add_css_class ("day-label"); + day_label.set_halign (Gtk.Align.START); + + date_label = new Gtk.Label (null); + date_label.add_css_class ("date-label"); + date_label.set_halign (Gtk.Align.START); + + calendar = new Gtk.Calendar (); + calendar.set_hexpand (true); + + if (config != null) { + bool show_day_label_found; + bool ?show_day_label = get_prop (config, "show-day-label", + out show_day_label_found); + if (show_day_label_found && show_day_label == false) { + day_label.set_visible (false); + } + + bool show_date_label_found; + bool ?show_date_label = get_prop (config, "show-date-label", + out show_date_label_found); + if (show_date_label_found && show_date_label == false) { + date_label.set_visible (false); + } + + bool show_heading_found; + bool ?show_heading = get_prop (config, "show-heading", + out show_heading_found); + if (show_heading_found) { + calendar.show_heading = show_heading; + } + + bool show_day_names_found; + bool ?show_day_names = get_prop (config, "show-day-names", + out show_day_names_found); + if (show_day_names_found) { + calendar.show_day_names = show_day_names; + } + + bool show_week_numbers_found; + bool ?show_week_numbers = get_prop (config, "show-week-numbers", + out show_week_numbers_found); + if (show_week_numbers_found) { + calendar.show_week_numbers = show_week_numbers; + } + + string ?df = get_prop (config, "day-format"); + if (df != null) { + day_format = df; + } + + string ?dtf = get_prop (config, "date-format"); + if (dtf != null) { + date_format = dtf; + } + } + + container.append (day_label); + container.append (date_label); + container.append (calendar); + append (container); + + calendar.day_selected.connect (update_labels); + calendar.next_month.connect (update_labels); + calendar.prev_month.connect (update_labels); + calendar.next_year.connect (update_labels); + calendar.prev_year.connect (update_labels); + reset_to_today (); + } + + void update_marks (DateTime selected) { + var today = new DateTime.now_local (); + + calendar.clear_marks (); + + if (selected.get_year () == today.get_year () + && selected.get_month () == today.get_month ()) { + calendar.mark_day ((uint) today.get_day_of_month ()); + } + } + + void reset_to_today () { + var today = new DateTime.now_local (); + calendar.select_day (today); + update_labels (); + } + + void update_labels () { + var selected = calendar.get_date (); + update_marks (selected); + + var dt = new DateTime.local ((int) selected.get_year (), + (int) selected.get_month (), + (int) selected.get_day_of_month (), + 0, 0, 0.0); + day_label.set_label (dt.format (day_format)); + date_label.set_label (dt.format (date_format)); + } + + public override void on_cc_visibility_change (bool value) { + if (value) { + reset_to_today (); + } + } + } +} diff --git a/src/controlCenter/widgets/factory.vala b/src/controlCenter/widgets/factory.vala index 7f4b9d9a..02288c78 100644 --- a/src/controlCenter/widgets/factory.vala +++ b/src/controlCenter/widgets/factory.vala @@ -37,6 +37,9 @@ namespace SwayNotificationCenter.Widgets { case "slider": widget = new Slider (suffix); break; + case "calendar": + widget = new Calendar (suffix); + break; #if HAVE_PULSE_AUDIO case "volume": widget = new Volume (suffix); diff --git a/src/meson.build b/src/meson.build index 7b9700f5..e0eb5ef4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -50,6 +50,8 @@ widget_sources = [ 'controlCenter/widgets/backlight/backlightUtil.vala', # Widget: Inhibitors 'controlCenter/widgets/inhibitors/inhibitors.vala', + # Widget: Calendar + 'controlCenter/widgets/calendar/calendar.vala', ] app_sources = [