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 @@ - - - - - - image/svg+xml - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - - 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 @@ - - - - - - image/svg+xml - - - - - - - - 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 @@ - - - + xmlns='http://www.w3.org/2000/svg' + xmlns:svg='http://www.w3.org/2000/svg' + xmlns:gpa='https://www.gtk.org/grappa' + gpa:version='1' + gpa:state-names='normal active disabled' + gpa:state='0' + id='svg4' + width='24' + height='24'> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 (); + }); + } +}