From bc013a199c68f60ae279a25075e24678c18b1d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 3 Sep 2026 11:28:13 -0700 Subject: [PATCH 1/8] NotificationList: use ListView --- src/Widgets/NotificationEntry.vala | 17 +++-- src/Widgets/NotificationsList.vala | 116 ++++++++++++++++------------- 2 files changed, 77 insertions(+), 56 deletions(-) diff --git a/src/Widgets/NotificationEntry.vala b/src/Widgets/NotificationEntry.vala index fc59d8f0..221bab72 100644 --- a/src/Widgets/NotificationEntry.vala +++ b/src/Widgets/NotificationEntry.vala @@ -261,11 +261,12 @@ public class Notifications.NotificationEntry : Gtk.ListBoxRow { new Variant.string (notification.desktop_id), null ); - notification.notify["server-id"].connect (() => { - if (notification.server_id == 0) { - dismiss (); - } - }); + notification.notify["server-id"].connect (dismiss_if_stale); + } + + public void unbind () { + notification.notify.disconnect (dismiss_if_stale); + Settings.unbind (settings, "headers"); } private static bool get_bind_func (Value value, Variant variant, void* user_data) { @@ -295,6 +296,12 @@ public class Notifications.NotificationEntry : Gtk.ListBoxRow { } } + private void dismiss_if_stale () { + if (notification.server_id == 0) { + dismiss (); + } + } + private class DeleteAffordance : Granite.Bin { public Gtk.Align alignment { get; construct; } diff --git a/src/Widgets/NotificationsList.vala b/src/Widgets/NotificationsList.vala index 7ec4b20f..502ea7a5 100644 --- a/src/Widgets/NotificationsList.vala +++ b/src/Widgets/NotificationsList.vala @@ -13,6 +13,8 @@ public class Notifications.NotificationsList : Granite.Bin { private static GLib.HashTable app_datetime; private Gee.HashMap app_entries; + private Gtk.SortListModel sort_list_model; + private ListStore list_store; public ListModel notification_items { get { @@ -36,23 +38,33 @@ public class Notifications.NotificationsList : Granite.Bin { list_store = new GLib.ListStore (typeof (Notification)); - var sort_list_model = new Gtk.SortListModel (list_store, new Gtk.CustomSorter ((GLib.CompareDataFunc) Notification.compare)) { + sort_list_model = new Gtk.SortListModel (list_store, new Gtk.CustomSorter ((GLib.CompareDataFunc) Notification.compare)) { section_sorter = new Gtk.CustomSorter ((GLib.CompareDataFunc) section_func) }; - var listbox = new Gtk.ListBox () { - activate_on_single_click = true, - selection_mode = NONE + var item_factory = new Gtk.SignalListItemFactory (); + item_factory.setup.connect (setup_factory); + item_factory.bind.connect (bind_factory); + item_factory.unbind.connect (unbind_factory); + + var header_factory = new Gtk.SignalListItemFactory (); + header_factory.setup.connect (setup_header_factory); + header_factory.bind.connect (bind_header_factory); + + var list_view = new Gtk.ListView (new Gtk.NoSelection (sort_list_model), item_factory) { + header_factory = header_factory }; - listbox.bind_model (sort_list_model, create_widget_func); - listbox.set_placeholder (placeholder); - listbox.set_header_func (header_func); - child = listbox; + // var listbox = new Gtk.ListBox () { + // activate_on_single_click = true, + // }; + // listbox.set_placeholder (placeholder); + + child = list_view; insert_action_group (ACTION_GROUP_PREFIX, new NotificationsMonitor ().notifications_action_group); - listbox.row_activated.connect (on_row_activated); + list_view.activate.connect (on_row_activated); list_store.items_changed.connect (on_items_changed); @@ -80,38 +92,43 @@ public class Notifications.NotificationsList : Granite.Bin { return 0; } - private void header_func (Gtk.ListBoxRow row, Gtk.ListBoxRow? before) { - unowned var row_entry = (NotificationEntry) row; - unowned NotificationEntry? before_entry = (NotificationEntry) before; - unowned string row_app_id = row_entry.notification.desktop_id; + private void setup_factory (Object item) { + var notification_entry = new NotificationEntry (); + notification_entry.remove.connect (remove_notification); - if (before != null && row_app_id == before_entry.notification.desktop_id) { - row.set_header (null); - return; - } + var list_item = (Gtk.ListItem) item; + list_item.child = notification_entry; + } - var app_entry = app_entries[row_app_id]; - if (app_entry == null) { - app_entry = new AppEntry () { - app_name = row_entry.notification.app_name, - app_id = row_app_id - }; - app_entry.clear.connect (clear_app_entry); + private void bind_factory (Object item) { + var list_item = (Gtk.ListItem) item; - app_entries[row_app_id] = app_entry; - } + var notification_entry = (NotificationEntry) list_item.child; + notification_entry.bind ((Notification) list_item.item); + } + + private void unbind_factory (Object item) { + var list_item = (Gtk.ListItem) item; - row.set_header (app_entries[row_app_id]); + var notification_entry = (NotificationEntry) list_item.child; + notification_entry.unbind (); } - private Gtk.Widget create_widget_func (Object item) { - var notification = (Notification) item; + private void setup_header_factory (Object item) { + var app_entry = new AppEntry (); + app_entry.clear.connect (clear_app_entry); - var notification_entry = new NotificationEntry (); - notification_entry.bind (notification); - notification_entry.remove.connect (remove_notification); + var list_item = (Gtk.ListItem) item; + list_item.child = app_entry; + } + + private void bind_header_factory (Object item) { + var list_item = (Gtk.ListItem) item; + var notification = (Notification) list_item.item; - return notification_entry; + var notification_entry = (AppEntry) list_item.child; + notification_entry.app_name = notification.app_name; + notification_entry.app_id = notification.desktop_id; } public async void add_entry (Notification notification) { @@ -197,25 +214,22 @@ public class Notifications.NotificationsList : Granite.Bin { } } - private void on_row_activated (Gtk.ListBoxRow row) { - if (row is NotificationEntry) { - unowned var notification_entry = (NotificationEntry) row; - - if (notification_entry.notification.default_action != null) { - activate_action ( - ACTION_PREFIX + notification_entry.notification.default_action, - null - ); + private void on_row_activated (uint pos) { + var notification = (Notification) sort_list_model.get_item (pos); + if (notification.default_action != null) { + activate_action ( + ACTION_PREFIX + notification.default_action, + null + ); + close_popover (); + } else { + try { + var context = get_display ().get_app_launch_context (); + notification.app_info.launch (null, context); + notification.server_id = 0; close_popover (); - } else { - try { - var context = notification_entry.get_display ().get_app_launch_context (); - notification_entry.notification.app_info.launch (null, context); - notification_entry.notification.server_id = 0; - close_popover (); - } catch (Error e) { - warning ("Unable to launch app: %s", e.message); - } + } catch (Error e) { + warning ("Unable to launch app: %s", e.message); } } } From dc33f0a476eb474a9d55e28060b00917203116e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 3 Sep 2026 14:29:54 -0700 Subject: [PATCH 2/8] Fix crash --- src/Widgets/NotificationsList.vala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Widgets/NotificationsList.vala b/src/Widgets/NotificationsList.vala index 502ea7a5..0cbbb4f7 100644 --- a/src/Widgets/NotificationsList.vala +++ b/src/Widgets/NotificationsList.vala @@ -118,17 +118,17 @@ public class Notifications.NotificationsList : Granite.Bin { var app_entry = new AppEntry (); app_entry.clear.connect (clear_app_entry); - var list_item = (Gtk.ListItem) item; + var list_item = (Gtk.ListHeader) item; list_item.child = app_entry; } private void bind_header_factory (Object item) { - var list_item = (Gtk.ListItem) item; + var list_item = (Gtk.ListHeader) item; var notification = (Notification) list_item.item; - var notification_entry = (AppEntry) list_item.child; - notification_entry.app_name = notification.app_name; - notification_entry.app_id = notification.desktop_id; + var app_entry = (AppEntry) list_item.child; + app_entry.app_name = notification.app_name; + app_entry.app_id = notification.desktop_id; } public async void add_entry (Notification notification) { From a425ac44b45e82c0b9080017ce73b9ef64e21f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 3 Sep 2026 14:34:50 -0700 Subject: [PATCH 3/8] Small fixes --- src/Widgets/AppEntry.vala | 2 -- src/Widgets/NotificationsList.vala | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Widgets/AppEntry.vala b/src/Widgets/AppEntry.vala index 5476b56d..a35ab38a 100644 --- a/src/Widgets/AppEntry.vala +++ b/src/Widgets/AppEntry.vala @@ -74,8 +74,6 @@ public class Notifications.AppEntry : Gtk.ListBoxRow { box.append (expander); box.append (clear_btn_entry); - margin_start = 12; - margin_end = 12; margin_bottom = 3; margin_top = 6; can_focus = false; diff --git a/src/Widgets/NotificationsList.vala b/src/Widgets/NotificationsList.vala index 0cbbb4f7..13e98d22 100644 --- a/src/Widgets/NotificationsList.vala +++ b/src/Widgets/NotificationsList.vala @@ -22,9 +22,12 @@ public class Notifications.NotificationsList : Granite.Bin { } } + static construct { + app_datetime = new GLib.HashTable (str_hash, str_equal); + } + construct { app_entries = new Gee.HashMap (); - app_datetime = new GLib.HashTable (str_hash, str_equal); var placeholder = new Gtk.Label (_("No Notifications")) { margin_top = 24, From 7224039bedc449d572ba45323be867de42a1efda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 3 Sep 2026 14:45:09 -0700 Subject: [PATCH 4/8] rest of the owl --- src/Widgets/NotificationsList.vala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Widgets/NotificationsList.vala b/src/Widgets/NotificationsList.vala index 13e98d22..8a2cac60 100644 --- a/src/Widgets/NotificationsList.vala +++ b/src/Widgets/NotificationsList.vala @@ -13,6 +13,7 @@ public class Notifications.NotificationsList : Granite.Bin { private static GLib.HashTable app_datetime; private Gee.HashMap app_entries; + private Gtk.Stack stack; private Gtk.SortListModel sort_list_model; private ListStore list_store; @@ -55,15 +56,16 @@ public class Notifications.NotificationsList : Granite.Bin { header_factory.bind.connect (bind_header_factory); var list_view = new Gtk.ListView (new Gtk.NoSelection (sort_list_model), item_factory) { - header_factory = header_factory + header_factory = header_factory, + single_click_activate = true }; + list_view.remove_css_class (Granite.STYLE_CLASS_VIEW); - // var listbox = new Gtk.ListBox () { - // activate_on_single_click = true, - // }; - // listbox.set_placeholder (placeholder); + stack = new Gtk.Stack (); + stack.add_named (placeholder, "placeholder"); + stack.add_named (list_view, "list"); - child = list_view; + child = stack; insert_action_group (ACTION_GROUP_PREFIX, new NotificationsMonitor ().notifications_action_group); @@ -185,7 +187,10 @@ public class Notifications.NotificationsList : Granite.Bin { private void on_items_changed () { if (list_store.n_items == 0) { + stack.visible_child_name = "placeholder"; Session.get_instance ().clear (); + } else { + stack.visible_child_name = "list"; } items_changed (); From eca7fad5d3507a3226625a3f75c268ec0e535cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 3 Sep 2026 15:00:17 -0700 Subject: [PATCH 5/8] Fix bug --- src/Widgets/NotificationsList.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/NotificationsList.vala b/src/Widgets/NotificationsList.vala index 5455d803..1fa0e48a 100644 --- a/src/Widgets/NotificationsList.vala +++ b/src/Widgets/NotificationsList.vala @@ -190,7 +190,7 @@ public class Notifications.NotificationsList : Granite.Bin { var app_id = notification_entry.notification.desktop_id; uint pos = -1; - if (list_store.find (notification_entry, out pos)) { + if (list_store.find (notification_entry.notification, out pos)) { list_store.remove (pos); Session.get_instance ().remove_notification (notification_entry.notification); } From 9f2fc7f3286630da6cbe66349f4c9e08d3804751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 3 Sep 2026 15:12:01 -0700 Subject: [PATCH 6/8] fix duplicate headers --- src/Widgets/NotificationsList.vala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Widgets/NotificationsList.vala b/src/Widgets/NotificationsList.vala index 1fa0e48a..d3d1ccc3 100644 --- a/src/Widgets/NotificationsList.vala +++ b/src/Widgets/NotificationsList.vala @@ -137,6 +137,7 @@ public class Notifications.NotificationsList : Granite.Bin { unowned GLib.DateTime? time = app_datetime[notification.desktop_id]; if (time == null || time.compare (notification.timestamp) <= 0) { app_datetime[notification.desktop_id] = notification.timestamp; + sort_list_model.section_sorter.changed (DIFFERENT); } list_store.append (notification); From 315895c4b164694668a8b8d8eba98defce3e05a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 3 Sep 2026 17:52:52 -0700 Subject: [PATCH 7/8] Slim up a little --- src/Widgets/NotificationsList.vala | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Widgets/NotificationsList.vala b/src/Widgets/NotificationsList.vala index 63bd9881..632738f1 100644 --- a/src/Widgets/NotificationsList.vala +++ b/src/Widgets/NotificationsList.vala @@ -98,8 +98,7 @@ public class Notifications.NotificationsList : Granite.Bin { var notification_entry = new NotificationEntry (); notification_entry.remove.connect (remove_notification); - var list_item = (Gtk.ListItem) item; - list_item.child = notification_entry; + ((Gtk.ListItem) item).child = notification_entry } private void bind_factory (Object item) { @@ -117,11 +116,10 @@ public class Notifications.NotificationsList : Granite.Bin { } private void setup_header_factory (Object item) { - var app_entry = new AppEntry (); + var app_entry = new AppEntry (); app_entry.clear.connect (clear_app_entry); - var list_item = (Gtk.ListHeader) item; - list_item.child = app_entry; + ((Gtk.ListHeader) item).child = app_entry; } private void bind_header_factory (Object item) { From 4e50066051074a007ab45112c8cc6722c516deae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 3 Sep 2026 17:57:04 -0700 Subject: [PATCH 8/8] Fix typo --- src/Widgets/NotificationsList.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/NotificationsList.vala b/src/Widgets/NotificationsList.vala index 632738f1..1136da55 100644 --- a/src/Widgets/NotificationsList.vala +++ b/src/Widgets/NotificationsList.vala @@ -98,7 +98,7 @@ public class Notifications.NotificationsList : Granite.Bin { var notification_entry = new NotificationEntry (); notification_entry.remove.connect (remove_notification); - ((Gtk.ListItem) item).child = notification_entry + ((Gtk.ListItem) item).child = notification_entry; } private void bind_factory (Object item) {