diff --git a/README.md b/README.md
index 2214fae7..f677d083 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ You'll need the following dependencies:
* libgdk-pixbuf-2.0-dev
* libglib2.0-dev
* libgranite-7-dev >= 7.7.0
-* libgtk-4-dev
+* libgtk-4-dev >= 4.22
* libwingpanel-9-dev
* meson >= 0.58.0
* valac
diff --git a/data/gresource.xml b/data/gresource.xml
index 9cd66643..6bd9acfc 100644
--- a/data/gresource.xml
+++ b/data/gresource.xml
@@ -2,56 +2,7 @@
AppEntry.css
- indicator.css
NotificationEntry.css
-
-
- icons/disabled-10.svg
- icons/disabled-20.svg
- icons/disabled-30.svg
- icons/disabled-40.svg
- icons/disabled-50.svg
- icons/disabled-60.svg
- icons/disabled-70.svg
- icons/disabled-80.svg
- icons/disabled-90.svg
- icons/disabled.svg
-
- icons/new-10.svg
- icons/new-20.svg
- icons/new-30.svg
- icons/new-40.svg
- icons/new-50.svg
- icons/new-60.svg
- icons/new-70.svg
- icons/new-80.svg
- icons/new-90.svg
- icons/new.svg
-
- icons/notification.svg
-
- icons/disabled-10.svg
- icons/disabled-20.svg
- icons/disabled-30.svg
- icons/disabled-40.svg
- icons/disabled-50.svg
- icons/disabled-60.svg
- icons/disabled-70.svg
- icons/disabled-80.svg
- icons/disabled-90.svg
- icons/disabled.svg
-
- icons/new-10.svg
- icons/new-20.svg
- icons/new-30.svg
- icons/new-40.svg
- icons/new-50.svg
- icons/new-60.svg
- icons/new-70.svg
- icons/new-80.svg
- icons/new-90.svg
- icons/new.svg
-
- icons/notification.svg
+ icons/notification.svg
diff --git a/data/icons/disabled-10.svg b/data/icons/disabled-10.svg
deleted file mode 100644
index dd44f4ad..00000000
--- a/data/icons/disabled-10.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
diff --git a/data/icons/disabled-20.svg b/data/icons/disabled-20.svg
deleted file mode 100644
index 33888b73..00000000
--- a/data/icons/disabled-20.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
diff --git a/data/icons/disabled-30.svg b/data/icons/disabled-30.svg
deleted file mode 100644
index 31e8dc17..00000000
--- a/data/icons/disabled-30.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-40.svg b/data/icons/disabled-40.svg
deleted file mode 100644
index c9b41254..00000000
--- a/data/icons/disabled-40.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-50.svg b/data/icons/disabled-50.svg
deleted file mode 100644
index 63aad306..00000000
--- a/data/icons/disabled-50.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-60.svg b/data/icons/disabled-60.svg
deleted file mode 100644
index 6c47d540..00000000
--- a/data/icons/disabled-60.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-70.svg b/data/icons/disabled-70.svg
deleted file mode 100644
index ee21180e..00000000
--- a/data/icons/disabled-70.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-80.svg b/data/icons/disabled-80.svg
deleted file mode 100644
index 065f4f7f..00000000
--- a/data/icons/disabled-80.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/disabled-90.svg b/data/icons/disabled-90.svg
deleted file mode 100644
index f3fc6f45..00000000
--- a/data/icons/disabled-90.svg
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
diff --git a/data/icons/disabled.svg b/data/icons/disabled.svg
deleted file mode 100644
index bbf984e1..00000000
--- a/data/icons/disabled.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
diff --git a/data/icons/new-10.svg b/data/icons/new-10.svg
deleted file mode 100644
index 05aec32a..00000000
--- a/data/icons/new-10.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/data/icons/new-20.svg b/data/icons/new-20.svg
deleted file mode 100644
index d7b195de..00000000
--- a/data/icons/new-20.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/data/icons/new-30.svg b/data/icons/new-30.svg
deleted file mode 100644
index 9e3b34f1..00000000
--- a/data/icons/new-30.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/data/icons/new-40.svg b/data/icons/new-40.svg
deleted file mode 100644
index 6499179a..00000000
--- a/data/icons/new-40.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/data/icons/new-50.svg b/data/icons/new-50.svg
deleted file mode 100644
index 5325b9c6..00000000
--- a/data/icons/new-50.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/data/icons/new-60.svg b/data/icons/new-60.svg
deleted file mode 100644
index f92abb01..00000000
--- a/data/icons/new-60.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/data/icons/new-70.svg b/data/icons/new-70.svg
deleted file mode 100644
index b29efbca..00000000
--- a/data/icons/new-70.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/data/icons/new-80.svg b/data/icons/new-80.svg
deleted file mode 100644
index eab3c453..00000000
--- a/data/icons/new-80.svg
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
diff --git a/data/icons/new-90.svg b/data/icons/new-90.svg
deleted file mode 100644
index 94c80ad4..00000000
--- a/data/icons/new-90.svg
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
diff --git a/data/icons/new.svg b/data/icons/new.svg
deleted file mode 100644
index d86a94e2..00000000
--- a/data/icons/new.svg
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
diff --git a/data/icons/notification.svg b/data/icons/notification.svg
index 308ab118..17ff96ee 100644
--- a/data/icons/notification.svg
+++ b/data/icons/notification.svg
@@ -1,18 +1,266 @@
-
diff --git a/data/indicator.css b/data/indicator.css
deleted file mode 100644
index f527b235..00000000
--- a/data/indicator.css
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-or-later
- * SPDX-FileCopyrightText: 2017-2023 elementary, Inc. (https://elementary.io)
- */
-
-.notification-icon {
- animation: none;
- min-width: 24px;
- opacity: 1;
- transition: none;
- -gtk-icon-source: -gtk-icontheme("notification-symbolic");
-}
-
-.notification-icon.new {
- animation: notification-new 500ms cubic-bezier(0.4, 0.0, 0.2, 1);
- -gtk-icon-source: -gtk-icontheme("notification-new-symbolic");
-}
-
-.notification-icon.disabled {
- animation: notification-disabled 160ms cubic-bezier(0.4, 0.0, 0.2, 1);
- -gtk-icon-source: -gtk-icontheme("notification-disabled-symbolic");
-}
-
-@keyframes notification-disabled {
- 0% { -gtk-icon-source: -gtk-icontheme("notification-symbolic"); }
- 10% { -gtk-icon-source: -gtk-icontheme("notification-disabled-10-symbolic"); }
- 20% { -gtk-icon-source: -gtk-icontheme("notification-disabled-20-symbolic"); }
- 30% { -gtk-icon-source: -gtk-icontheme("notification-disabled-30-symbolic"); }
- 40% { -gtk-icon-source: -gtk-icontheme("notification-disabled-40-symbolic"); }
- 50% { -gtk-icon-source: -gtk-icontheme("notification-disabled-50-symbolic"); }
- 60% { -gtk-icon-source: -gtk-icontheme("notification-disabled-60-symbolic"); }
- 70% { -gtk-icon-source: -gtk-icontheme("notification-disabled-70-symbolic"); }
- 80% { -gtk-icon-source: -gtk-icontheme("notification-disabled-80-symbolic"); }
- 90% { -gtk-icon-source: -gtk-icontheme("notification-disabled-90-symbolic"); }
- 100% { -gtk-icon-source: -gtk-icontheme("notification-disabled-symbolic"); }
-}
-
-@keyframes notification-new {
- 0% { -gtk-icon-source: -gtk-icontheme("notification-symbolic"); }
- 10% { -gtk-icon-source: -gtk-icontheme("notification-new-10-symbolic"); }
- 20% { -gtk-icon-source: -gtk-icontheme("notification-new-20-symbolic"); }
- 30% { -gtk-icon-source: -gtk-icontheme("notification-new-30-symbolic"); }
- 40% { -gtk-icon-source: -gtk-icontheme("notification-new-40-symbolic"); }
- 50% { -gtk-icon-source: -gtk-icontheme("notification-new-50-symbolic"); }
- 60% { -gtk-icon-source: -gtk-icontheme("notification-new-60-symbolic"); }
- 70% { -gtk-icon-source: -gtk-icontheme("notification-new-70-symbolic"); }
- 80% { -gtk-icon-source: -gtk-icontheme("notification-new-80-symbolic"); }
- 90% { -gtk-icon-source: -gtk-icontheme("notification-new-90-symbolic"); }
- 100% { -gtk-icon-source: -gtk-icontheme("notification-new-symbolic"); }
-}
diff --git a/meson.build b/meson.build
index 28c68e5d..75f10a71 100644
--- a/meson.build
+++ b/meson.build
@@ -42,6 +42,7 @@ shared_module(
'src/Widgets/NotificationsList.vala',
'src/Widgets/NotificationEntry.vala',
'src/Widgets/AppEntry.vala',
+ 'src/Widgets/Symbol.vala',
'src/Services/NotificationsMonitor.vala',
'src/Services/Notification.vala',
'src/Services/Session.vala',
@@ -51,7 +52,7 @@ shared_module(
dependency('glib-2.0'),
dependency('gobject-2.0'),
dependency('granite-7', version: '>=7.7.0'),
- dependency('gtk4'),
+ dependency('gtk4', version: '>=4.22'),
dependency('libadwaita-1'),
wingpanel_dep
],
diff --git a/src/Indicator.vala b/src/Indicator.vala
index fa4c8820..ee58e59f 100644
--- a/src/Indicator.vala
+++ b/src/Indicator.vala
@@ -13,7 +13,7 @@ public class Notifications.Indicator : Wingpanel.Indicator {
private Gtk.Box? main_box = null;
private Wingpanel.PopoverMenuItem clear_all_btn;
- private Gtk.Spinner? dynamic_icon = null;
+ private NotificationsIndicator.Symbol? dynamic_icon = null;
private NotificationsList nlist;
private NotificationsMonitor monitor;
@@ -36,20 +36,10 @@ public class Notifications.Indicator : Wingpanel.Indicator {
public override Gtk.Widget get_display_widget () {
if (dynamic_icon == null) {
- var provider = new Gtk.CssProvider ();
- provider.load_from_resource ("io/elementary/wingpanel/notifications/indicator.css");
-
- Gtk.StyleContext.add_provider_for_display (
- Gdk.Display.get_default (),
- provider,
- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
- );
-
- dynamic_icon = new Gtk.Spinner () {
- spinning = true,
+ dynamic_icon = new NotificationsIndicator.Symbol ("/io/elementary/wingpanel/notifications/icons/notification.svg") {
+ pixel_size = 24,
tooltip_markup = _("Updating notifications…")
};
- dynamic_icon.add_css_class ("notification-icon");
nlist = new NotificationsList ();
nlist.items_changed.connect (set_display_icon_name);
@@ -189,13 +179,11 @@ public class Notifications.Indicator : Wingpanel.Indicator {
private void set_display_icon_name () {
if (notify_settings.get_boolean ("do-not-disturb")) {
- dynamic_icon.add_css_class ("disabled");
+ dynamic_icon.state = NotificationsIndicator.SymbolState.DISABLED;
} else if (nlist != null && nlist.app_entries.size > 0) {
- dynamic_icon.remove_css_class ("disabled");
- dynamic_icon.add_css_class ("new");
+ dynamic_icon.state = NotificationsIndicator.SymbolState.ACTIVE;
} else {
- dynamic_icon.remove_css_class ("disabled");
- dynamic_icon.remove_css_class ("new");
+ dynamic_icon.state = NotificationsIndicator.SymbolState.NORMAL;
}
update_tooltip ();
}
diff --git a/src/Widgets/Symbol.vala b/src/Widgets/Symbol.vala
new file mode 100644
index 00000000..954111d4
--- /dev/null
+++ b/src/Widgets/Symbol.vala
@@ -0,0 +1,78 @@
+/*
+* SPDX-License-Identifier: LGPL-2.1-or-later
+* SPDX-FileCopyrightText: 2026 elementary, Inc. (https://elementary.io)
+*/
+
+namespace NotificationsIndicator.SymbolState {
+ // The default state
+ public const string NORMAL = "normal";
+ // Disabled state represented by a slash
+ public const string DISABLED = "disabled";
+ // e.g. paired, connected, needs attention
+ public const string ACTIVE = "active";
+}
+
+public class NotificationsIndicator.Symbol : Granite.Bin {
+ public string resource_path { get; construct; }
+
+ public int pixel_size {
+ get { return image.pixel_size; }
+ set { image.pixel_size = value; }
+ }
+
+ public uint state_index {
+ get { return svg.state; }
+ set {
+ uint length = -1;
+ svg.get_state_names (out length);
+
+ if (value > length - 1) {
+ warning ("Granite.Symbol set to undefined state. Ignoring.");
+ return;
+ }
+
+ svg.state = value;
+ notify_property ("state");
+ }
+ }
+
+ public string state {
+ get {
+ uint length = -1;
+ return svg.get_state_names (out length)[state_index];
+ }
+ set {
+ uint length = -1;
+ var names = svg.get_state_names (out length);
+
+ for (int i = 0; i < length; i++) {
+ if (names[i] == value) {
+ state_index = i;
+ return;
+ }
+ }
+
+ warning ("Granite.Symbol set to undefined state. Ignoring.");
+ }
+ }
+
+ private Gtk.Image image;
+ private Gtk.Svg svg;
+
+ public Symbol (string resource_path) {
+ Object (resource_path: resource_path);
+ }
+
+ construct {
+ svg = new Gtk.Svg.from_resource (resource_path);
+
+ image = new Gtk.Image.from_paintable (svg);
+
+ child = image;
+
+ child.realize.connect (() => {
+ svg.set_frame_clock (get_frame_clock ());
+ svg.play ();
+ });
+ }
+}