[gnome-builder] libide-gui: port notifications visualizers to GTK 4



commit 7def41e907b9ef63fee19c87a25c03222027b745
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jul 11 21:51:30 2022 -0700

    libide-gui: port notifications visualizers to GTK 4

 src/libide/gui/ide-notification-list-box-row.c  |  47 +++----
 src/libide/gui/ide-notification-list-box-row.ui |  51 ++++---
 src/libide/gui/ide-notification-stack-private.h |   3 +-
 src/libide/gui/ide-notification-stack.c         | 176 +++++++++++++-----------
 src/libide/gui/ide-notification-view-private.h  |   5 +-
 src/libide/gui/ide-notification-view.c          |  31 ++---
 src/libide/gui/ide-notification-view.ui         |  38 +----
 src/libide/gui/ide-notifications-button.c       | 134 +++++++++---------
 src/libide/gui/ide-notifications-button.h       |  10 +-
 src/libide/gui/ide-notifications-button.ui      |  70 ++++++----
 10 files changed, 274 insertions(+), 291 deletions(-)
---
diff --git a/src/libide/gui/ide-notification-list-box-row.c b/src/libide/gui/ide-notification-list-box-row.c
index 86be210a0..ce7ba22eb 100644
--- a/src/libide/gui/ide-notification-list-box-row.c
+++ b/src/libide/gui/ide-notification-list-box-row.c
@@ -22,9 +22,8 @@
 
 #include "config.h"
 
-#include <dazzle.h>
+#include <libide-gtk.h>
 
-#include "ide-gui-private.h"
 #include "ide-notification-list-box-row-private.h"
 
 struct _IdeNotificationListBoxRow
@@ -87,14 +86,12 @@ setup_buttons_locked (IdeNotificationListBoxRow *self)
           if (label != NULL && (!self->compact || icon == NULL))
             child = g_object_new (GTK_TYPE_LABEL,
                                   "label", label,
-                                  "visible", TRUE,
                                   "use-underline", TRUE,
                                   NULL);
           else if (icon != NULL)
             child = g_object_new (GTK_TYPE_IMAGE,
-                                  "icon-size", GTK_ICON_SIZE_MENU,
+                                  "pixel-size", 16,
                                   "gicon", icon,
-                                  "visible", TRUE,
                                   NULL);
 
           g_assert (GTK_IS_WIDGET (child));
@@ -103,23 +100,20 @@ setup_buttons_locked (IdeNotificationListBoxRow *self)
                                  "child", child,
                                  "action-name", action,
                                  "action-target", target,
-                                 "visible", TRUE,
                                  NULL);
 
 
           if (!self->compact)
             {
               g_object_set (button, "width-request", 100, NULL);
-              dzl_gtk_widget_add_style_class (GTK_WIDGET (button), "suggested-action");
+              gtk_widget_add_css_class (GTK_WIDGET (button), "suggested-action");
             }
           else
-            dzl_gtk_widget_add_style_class (GTK_WIDGET (button), "circular");
+            gtk_widget_add_css_class (GTK_WIDGET (button), "circular");
 
           g_assert (GTK_IS_WIDGET (button));
 
-          gtk_container_add_with_properties (GTK_CONTAINER (self->buttons), GTK_WIDGET (button),
-                                             "pack-type", GTK_PACK_END,
-                                             NULL);
+          gtk_box_append (self->buttons, GTK_WIDGET (button));
         }
     }
 
@@ -138,8 +132,6 @@ setup_buttons_locked (IdeNotificationListBoxRow *self)
  * Create a new #IdeNotificationListBoxRow.
  *
  * Returns: (transfer full): a newly created #IdeNotificationListBoxRow
- *
- * Since: 3.32
  */
 GtkWidget *
 ide_notification_list_box_row_new (IdeNotification *notification)
@@ -184,7 +176,7 @@ ide_notification_list_box_row_constructed (GObject *object)
                           (self->compact && ide_notification_get_n_buttons (self->notification)));
 
   if (ide_notification_get_urgent (self->notification))
-    dzl_gtk_widget_add_style_class (GTK_WIDGET (self), "needs-attention");
+    gtk_widget_add_css_class (GTK_WIDGET (self), "needs-attention");
 
   gtk_widget_set_visible (GTK_WIDGET (self->progress),
                           ide_notification_get_has_progress (self->notification));
@@ -195,7 +187,7 @@ ide_notification_list_box_row_constructed (GObject *object)
   setup_buttons_locked (self);
 
   if (ide_notification_get_progress_is_imprecise (self->notification))
-    _ide_gtk_progress_bar_start_pulsing (self->progress);
+    ide_gtk_progress_bar_start_pulsing (self->progress);
 
   ide_object_unlock (IDE_OBJECT (self->notification));
 
@@ -204,16 +196,16 @@ chain_up:
 }
 
 static void
-ide_notification_list_box_row_destroy (GtkWidget *widget)
+ide_notification_list_box_row_dispose (GObject *object)
 {
-  IdeNotificationListBoxRow *self = (IdeNotificationListBoxRow *)widget;
+  IdeNotificationListBoxRow *self = (IdeNotificationListBoxRow *)object;
 
   if (self->progress != NULL)
-    _ide_gtk_progress_bar_stop_pulsing (self->progress);
+    ide_gtk_progress_bar_stop_pulsing (self->progress);
 
   g_clear_object (&self->notification);
 
-  GTK_WIDGET_CLASS (ide_notification_list_box_row_parent_class)->destroy (widget);
+  G_OBJECT_CLASS (ide_notification_list_box_row_parent_class)->dispose (object);
 }
 
 static void
@@ -269,11 +261,10 @@ ide_notification_list_box_row_class_init (IdeNotificationListBoxRowClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->constructed = ide_notification_list_box_row_constructed;
+  object_class->dispose = ide_notification_list_box_row_dispose;
   object_class->get_property = ide_notification_list_box_row_get_property;
   object_class->set_property = ide_notification_list_box_row_set_property;
 
-  widget_class->destroy = ide_notification_list_box_row_destroy;
-
   properties [PROP_COMPACT] =
     g_param_spec_boolean ("compact",
                           "Compact",
@@ -310,8 +301,6 @@ ide_notification_list_box_row_init (IdeNotificationListBoxRow *self)
  * @self: a #IdeNotificationListBoxRow
  *
  * Returns: (transfer none) (nullable): an #IdeNotification
- *
- * Since: 3.32
  */
 IdeNotification *
 ide_notification_list_box_row_get_notification (IdeNotificationListBoxRow *self)
@@ -333,6 +322,7 @@ void
 ide_notification_list_box_row_set_compact (IdeNotificationListBoxRow *self,
                                            gboolean                   compact)
 {
+  GtkWidget *child;
   GtkBox *parent;
 
   g_return_if_fail (IDE_IS_NOTIFICATION_LIST_BOX_ROW (self));
@@ -343,12 +333,11 @@ ide_notification_list_box_row_set_compact (IdeNotificationListBoxRow *self,
 
       g_object_ref (self->buttons);
 
-      gtk_container_foreach (GTK_CONTAINER (self->buttons),
-                             (GtkCallback)gtk_widget_destroy,
-                             NULL);
+      while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->buttons))))
+        gtk_box_remove (self->buttons, child);
 
       parent = GTK_BOX (gtk_widget_get_parent (GTK_WIDGET (self->buttons)));
-      gtk_container_remove (GTK_CONTAINER (parent), GTK_WIDGET (self->buttons));
+      gtk_box_remove (parent, GTK_WIDGET (self->buttons));
       gtk_widget_hide (GTK_WIDGET (parent));
 
       if (compact)
@@ -356,9 +345,7 @@ ide_notification_list_box_row_set_compact (IdeNotificationListBoxRow *self,
       else
         parent = self->lower_button_area;
 
-      gtk_container_add_with_properties (GTK_CONTAINER (parent), GTK_WIDGET (self->buttons),
-                                         "pack-type", GTK_PACK_END,
-                                         NULL);
+      gtk_box_append (parent, GTK_WIDGET (self->buttons));
 
       g_object_unref (self->buttons);
 
diff --git a/src/libide/gui/ide-notification-list-box-row.ui b/src/libide/gui/ide-notification-list-box-row.ui
index b9317d3cf..eb13f4a6d 100644
--- a/src/libide/gui/ide-notification-list-box-row.ui
+++ b/src/libide/gui/ide-notification-list-box-row.ui
@@ -22,11 +22,11 @@
             <property name="can_focus">False</property>
             <property name="valign">baseline</property>
             <property name="hexpand">True</property>
+            <layout>
+              <property name="column">0</property>
+              <property name="row">1</property>
+            </layout>
           </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">1</property>
-          </packing>
         </child>
         <child>
           <object class="GtkLabel" id="title">
@@ -34,13 +34,13 @@
             <property name="can_focus">False</property>
             <property name="xalign">0</property>
             <style>
-              <class name="title"/>
+              <class name="heading"/>
             </style>
+            <layout>
+              <property name="column">0</property>
+              <property name="row">0</property>
+            </layout>
           </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">0</property>
-          </packing>
         </child>
         <child>
           <object class="GtkLabel" id="body">
@@ -56,11 +56,11 @@
             <attributes>
               <attribute name="font-features" value="tnum"/>
             </attributes>
+            <layout>
+              <property name="column">0</property>
+              <property name="row">2</property>
+            </layout>
           </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">2</property>
-          </packing>
         </child>
         <child>
           <object class="GtkBox" id="lower_button_area">
@@ -78,18 +78,13 @@
                   <placeholder/>
                 </child>
               </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="position">0</property>
-              </packing>
             </child>
+            <layout>
+              <property name="column">0</property>
+              <property name="row">3</property>
+              <property name="column-span">2</property>
+            </layout>
           </object>
-          <packing>
-            <property name="left_attach">0</property>
-            <property name="top_attach">3</property>
-            <property name="width">2</property>
-          </packing>
         </child>
         <child>
           <object class="GtkBox" id="side_button_area">
@@ -99,12 +94,12 @@
             <child>
               <placeholder/>
             </child>
+            <layout>
+              <property name="column">1</property>
+              <property name="row">0</property>
+              <property name="row-span">3</property>
+            </layout>
           </object>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="top_attach">0</property>
-            <property name="height">3</property>
-          </packing>
         </child>
       </object>
     </child>
diff --git a/src/libide/gui/ide-notification-stack-private.h b/src/libide/gui/ide-notification-stack-private.h
index df9f2e0ca..c9d810284 100644
--- a/src/libide/gui/ide-notification-stack-private.h
+++ b/src/libide/gui/ide-notification-stack-private.h
@@ -21,13 +21,14 @@
 #pragma once
 
 #include <gtk/gtk.h>
+
 #include <libide-core.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_NOTIFICATION_STACK (ide_notification_stack_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeNotificationStack, ide_notification_stack, IDE, NOTIFICATION_STACK, GtkStack)
+G_DECLARE_FINAL_TYPE (IdeNotificationStack, ide_notification_stack, IDE, NOTIFICATION_STACK, GtkWidget)
 
 GtkWidget       *ide_notification_stack_new           (void);
 void             ide_notification_stack_bind_model    (IdeNotificationStack *self,
diff --git a/src/libide/gui/ide-notification-stack.c b/src/libide/gui/ide-notification-stack.c
index 7f08023fb..1653c86ba 100644
--- a/src/libide/gui/ide-notification-stack.c
+++ b/src/libide/gui/ide-notification-stack.c
@@ -1,6 +1,6 @@
 /* ide-notification-stack.c
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2022 Christian Hergert <chergert redhat com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,8 +22,6 @@
 
 #include "config.h"
 
-#include <dazzle.h>
-
 #include "ide-notification-stack-private.h"
 #include "ide-notification-view-private.h"
 
@@ -32,9 +30,11 @@
 
 struct _IdeNotificationStack
 {
-  GtkStack         parent_instance;
-  DzlSignalGroup  *signals;
-  DzlBindingGroup *bindings;
+  GtkWidget        parent_instance;
+  GtkStack        *stack;
+  GPtrArray       *pages;
+  IdeSignalGroup  *signals;
+  IdeBindingGroup *bindings;
   GListModel      *model;
   gdouble          progress;
   guint            carousel_source;
@@ -52,7 +52,7 @@ enum {
   N_SIGNALS
 };
 
-G_DEFINE_FINAL_TYPE (IdeNotificationStack, ide_notification_stack, GTK_TYPE_STACK)
+G_DEFINE_FINAL_TYPE (IdeNotificationStack, ide_notification_stack, GTK_TYPE_WIDGET)
 
 static guint signals [N_SIGNALS];
 static GParamSpec *properties [N_PROPS];
@@ -117,22 +117,19 @@ ide_notification_stack_items_changed_cb (IdeNotificationStack *self,
                                          GListModel           *model)
 {
   GtkWidget *urgent = NULL;
-  GList *children;
-  GList *iter;
 
   g_assert (IDE_IS_NOTIFICATION_STACK (self));
 
-  children = gtk_container_get_children (GTK_CONTAINER (self));
-  iter = g_list_nth (children, position);
+  if (self->pages == NULL)
+    return;
 
-  for (guint i = 0; i < removed; i++, iter = iter->next)
+  for (guint i = 0; i < removed; i++)
     {
-      GtkWidget *child = iter->data;
-      gtk_widget_destroy (child);
+      GtkStackPage *page = g_ptr_array_index (self->pages, position);
+      g_ptr_array_remove_index (self->pages, position);
+      gtk_stack_remove (self->stack, gtk_stack_page_get_child (page));
     }
 
-  g_list_free (children);
-
   for (guint i = 0; i < added; i++)
     {
       g_autoptr(IdeNotification) notif = g_list_model_get_item (model, position + i);
@@ -140,10 +137,9 @@ ide_notification_stack_items_changed_cb (IdeNotificationStack *self,
                                       "notification", notif,
                                       "visible", TRUE,
                                       NULL);
+      GtkStackPage *page = gtk_stack_add_child (self->stack, view);
 
-      gtk_container_add_with_properties (GTK_CONTAINER (self), view,
-                                         "position", position + i,
-                                         NULL);
+      g_ptr_array_insert (self->pages, position + i, page);
 
       if (!urgent && ide_notification_get_urgent (notif))
         urgent = view;
@@ -151,7 +147,7 @@ ide_notification_stack_items_changed_cb (IdeNotificationStack *self,
 
   if (urgent != NULL)
     {
-      gtk_stack_set_visible_child (GTK_STACK (self), urgent);
+      gtk_stack_set_visible_child (self->stack, urgent);
       g_clear_handle_id (&self->carousel_source, g_source_remove);
     }
 
@@ -164,48 +160,58 @@ ide_notification_stack_items_changed_cb (IdeNotificationStack *self,
 }
 
 static void
-ide_notification_stack_notify_visible_child (IdeNotificationStack *self)
+ide_notification_stack_notify_visible_child (IdeNotificationStack *self,
+                                             GParamSpec           *pspec,
+                                             GtkStack             *stack)
 {
   g_assert (IDE_IS_NOTIFICATION_STACK (self));
+  g_assert (GTK_IS_STACK (stack));
 
   self->progress = 0.0;
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
 
-  dzl_binding_group_set_source (self->bindings,
+  ide_binding_group_set_source (self->bindings,
                                 ide_notification_stack_get_visible (self));
 
   g_signal_emit (self, signals [CHANGED], 0);
 }
 
 static void
-ide_notification_stack_destroy (GtkWidget *widget)
+ide_notification_stack_dispose (GObject *object)
 {
-  IdeNotificationStack *self = (IdeNotificationStack *)widget;
+  IdeNotificationStack *self = (IdeNotificationStack *)object;
+
+  g_clear_pointer (&self->pages, g_ptr_array_unref);
 
   if (self->signals != NULL)
-    dzl_signal_group_set_target (self->signals, NULL);
+    {
+      ide_signal_group_set_target (self->signals, NULL);
+      g_clear_object (&self->signals);
+    }
 
   if (self->bindings != NULL)
-    dzl_binding_group_set_source (self->bindings, NULL);
+    {
+      ide_binding_group_set_source (self->bindings, NULL);
+      g_clear_object (&self->bindings);
+    }
 
-  g_clear_object (&self->bindings);
-  g_clear_object (&self->signals);
   g_clear_handle_id (&self->carousel_source, g_source_remove);
 
-  GTK_WIDGET_CLASS (ide_notification_stack_parent_class)->destroy (widget);
+  g_clear_pointer ((GtkWidget **)&self->stack, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (ide_notification_stack_parent_class)->dispose (object);
 }
 
 static void
 ide_notification_stack_class_init (IdeNotificationStackClass *klass)
 {
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+  object_class->dispose = ide_notification_stack_dispose;
   object_class->get_property = ide_notification_stack_get_property;
   object_class->set_property = ide_notification_stack_set_property;
 
-  widget_class->destroy = ide_notification_stack_destroy;
-
   properties [PROP_PROGRESS] =
     g_param_spec_double ("progress",
                          "Progress",
@@ -224,31 +230,37 @@ ide_notification_stack_class_init (IdeNotificationStackClass *klass)
                   G_TYPE_NONE, 0);
 
   gtk_widget_class_set_css_name (widget_class, "notificationstack");
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
 }
 
 static void
 ide_notification_stack_init (IdeNotificationStack *self)
 {
-  self->signals = dzl_signal_group_new (G_TYPE_LIST_MODEL);
+  self->pages = g_ptr_array_new ();
 
-  dzl_signal_group_connect_object (self->signals,
+  self->signals = ide_signal_group_new (G_TYPE_LIST_MODEL);
+  ide_signal_group_connect_object (self->signals,
                                    "items-changed",
                                    G_CALLBACK (ide_notification_stack_items_changed_cb),
                                    self,
                                    G_CONNECT_SWAPPED);
 
-  self->bindings = dzl_binding_group_new ();
-
-  dzl_binding_group_bind (self->bindings, "progress", self, "progress",
+  self->bindings = ide_binding_group_new ();
+  ide_binding_group_bind (self->bindings, "progress",
+                          self, "progress",
                           G_BINDING_SYNC_CREATE);
 
-  gtk_stack_set_transition_duration (GTK_STACK (self), TRANSITION_DURATION);
-  gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
-
-  g_signal_connect (self,
-                    "notify::visible-child",
-                    G_CALLBACK (ide_notification_stack_notify_visible_child),
-                    NULL);
+  self->stack = g_object_new (GTK_TYPE_STACK,
+                              "transition-duration", TRANSITION_DURATION,
+                              "transition-type", GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN,
+                              NULL);
+  gtk_widget_set_parent (GTK_WIDGET (self->stack), GTK_WIDGET (self));
+
+  g_signal_connect_object (self->stack,
+                           "notify::visible-child",
+                           G_CALLBACK (ide_notification_stack_notify_visible_child),
+                           self,
+                           G_CONNECT_SWAPPED);
 }
 
 void
@@ -267,8 +279,14 @@ ide_notification_stack_bind_model (IdeNotificationStack *self,
       if (model != NULL)
         n_items = g_list_model_get_n_items (model);
 
-      gtk_container_foreach (GTK_CONTAINER (self), (GtkCallback)gtk_widget_destroy, NULL);
-      dzl_signal_group_set_target (self->signals, model);
+      while (self->pages->len > 0)
+        {
+          GtkStackPage *page = g_ptr_array_index (self->pages, 0);
+          g_ptr_array_remove_index (self->pages, 0);
+          gtk_stack_remove (self->stack, gtk_stack_page_get_child (page));
+        }
+
+      ide_signal_group_set_target (self->signals, model);
 
       if (n_items > 0)
         ide_notification_stack_items_changed_cb (self, 0, 0, n_items, model);
@@ -290,25 +308,27 @@ void
 ide_notification_stack_move_next (IdeNotificationStack *self)
 {
   GtkWidget *child;
-  gint position;
 
   g_return_if_fail (IDE_IS_NOTIFICATION_STACK (self));
 
-  if ((child = gtk_stack_get_visible_child (GTK_STACK (self))))
+  if ((child = gtk_stack_get_visible_child (self->stack)))
     {
-      GList *children;
+      for (guint i = 0; i < self->pages->len; i++)
+        {
+          GtkStackPage *page = g_ptr_array_index (self->pages, i);
+
+          if (child == gtk_stack_page_get_child (page) && i + 1 < self->pages->len)
+            {
+              page = g_ptr_array_index (self->pages, i + 1);
+              child = gtk_stack_page_get_child (page);
 
-      gtk_container_child_get (GTK_CONTAINER (self), child,
-                               "position", &position,
-                               NULL);
-      children = gtk_container_get_children (GTK_CONTAINER (self));
-      if (!(child = g_list_nth_data (children, position + 1)))
-        child = children->data;
-      g_list_free (children);
+              gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
+              gtk_stack_set_visible_child (self->stack, child);
+              gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
 
-      gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
-      gtk_stack_set_visible_child (GTK_STACK (self), child);
-      gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+              break;
+            }
+        }
 
       if (!self->in_carousel)
         g_clear_handle_id (&self->carousel_source, g_source_remove);
@@ -319,27 +339,27 @@ void
 ide_notification_stack_move_previous (IdeNotificationStack *self)
 {
   GtkWidget *child;
-  gint position;
 
   g_return_if_fail (IDE_IS_NOTIFICATION_STACK (self));
 
-  if ((child = gtk_stack_get_visible_child (GTK_STACK (self))))
+  if ((child = gtk_stack_get_visible_child (self->stack)))
     {
-      GList *children;
-
-      gtk_container_child_get (GTK_CONTAINER (self), child,
-                               "position", &position,
-                               NULL);
-      children = gtk_container_get_children (GTK_CONTAINER (self));
-      if (position == 0)
-        child = g_list_last (children)->data;
-      else
-        child = g_list_nth_data (children, position - 1);
-      g_list_free (children);
-
-      gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
-      gtk_stack_set_visible_child (GTK_STACK (self), child);
-      gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+      for (guint i = 0; i < self->pages->len; i++)
+        {
+          GtkStackPage *page = g_ptr_array_index (self->pages, i);
+
+          if (child == gtk_stack_page_get_child (page) && i > 0)
+            {
+              page = g_ptr_array_index (self->pages, i - 1);
+              child = gtk_stack_page_get_child (page);
+
+              gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
+              gtk_stack_set_visible_child (self->stack, child);
+              gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+
+              break;
+            }
+        }
 
       if (!self->in_carousel)
         g_clear_handle_id (&self->carousel_source, g_source_remove);
@@ -353,8 +373,6 @@ ide_notification_stack_move_previous (IdeNotificationStack *self)
  * Gets the visible notification in the stack.
  *
  * Returns: (transfer none) (nullable): an #IdeNotification or %NULL
- *
- * Since: 3.32
  */
 IdeNotification *
 ide_notification_stack_get_visible (IdeNotificationStack *self)
@@ -363,7 +381,7 @@ ide_notification_stack_get_visible (IdeNotificationStack *self)
 
   g_return_val_if_fail (IDE_IS_NOTIFICATION_STACK (self), NULL);
 
-  if ((child = gtk_stack_get_visible_child (GTK_STACK (self))))
+  if ((child = gtk_stack_get_visible_child (self->stack)))
     {
       if (IDE_IS_NOTIFICATION_VIEW (child))
         return ide_notification_view_get_notification (IDE_NOTIFICATION_VIEW (child));
diff --git a/src/libide/gui/ide-notification-view-private.h b/src/libide/gui/ide-notification-view-private.h
index 017a83fa5..e7df97e01 100644
--- a/src/libide/gui/ide-notification-view-private.h
+++ b/src/libide/gui/ide-notification-view-private.h
@@ -20,14 +20,15 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
+#include <adwaita.h>
+
 #include <libide-core.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_NOTIFICATION_VIEW (ide_notification_view_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeNotificationView, ide_notification_view, IDE, NOTIFICATION_VIEW, GtkBin)
+G_DECLARE_FINAL_TYPE (IdeNotificationView, ide_notification_view, IDE, NOTIFICATION_VIEW, AdwBin)
 
 GtkWidget       *ide_notification_view_new              (void);
 IdeNotification *ide_notification_view_get_notification (IdeNotificationView *self);
diff --git a/src/libide/gui/ide-notification-view.c b/src/libide/gui/ide-notification-view.c
index e6cfb8415..241227bbe 100644
--- a/src/libide/gui/ide-notification-view.c
+++ b/src/libide/gui/ide-notification-view.c
@@ -22,16 +22,14 @@
 
 #include "config.h"
 
-#include <dazzle.h>
-
 #include "ide-notification-view-private.h"
 
 struct _IdeNotificationView
 {
-  GtkBin           parent_instance;
+  AdwBin           parent_instance;
 
   IdeNotification *notification;
-  DzlBindingGroup *bindings;
+  IdeBindingGroup *bindings;
 
   GtkLabel        *label;
   GtkBox          *buttons;
@@ -39,7 +37,7 @@ struct _IdeNotificationView
   GtkImage        *default_button_image;
 };
 
-G_DEFINE_FINAL_TYPE (IdeNotificationView, ide_notification_view, GTK_TYPE_BIN)
+G_DEFINE_FINAL_TYPE (IdeNotificationView, ide_notification_view, ADW_TYPE_BIN)
 
 enum {
   PROP_0,
@@ -61,7 +59,8 @@ ide_notification_view_notify_icon (IdeNotificationView *self,
   g_assert (IDE_IS_NOTIFICATION (notif));
 
   icon = ide_notification_ref_icon (notif);
-  gtk_image_set_from_gicon (self->default_button_image, icon, GTK_ICON_SIZE_MENU);
+  gtk_image_set_pixel_size (self->default_button_image, 16);
+  gtk_image_set_from_gicon (self->default_button_image, icon);
   gtk_widget_set_visible (GTK_WIDGET (self->default_button), icon != NULL);
 }
 
@@ -71,12 +70,14 @@ connect_notification (IdeNotificationView *self,
 {
   g_autofree gchar *action_name = NULL;
   g_autoptr(GVariant) target_value = NULL;
+  GtkWidget *child;
   guint n_buttons;
 
   g_assert (IDE_IS_NOTIFICATION_VIEW (self));
   g_assert (!notification || IDE_IS_NOTIFICATION (notification));
 
-  gtk_container_foreach (GTK_CONTAINER (self->buttons), (GtkCallback)gtk_widget_destroy, NULL);
+  while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->buttons))))
+    gtk_box_remove (self->buttons, child);
 
   if (notification == NULL)
     {
@@ -128,15 +129,13 @@ connect_notification (IdeNotificationView *self,
           button = g_object_new (GTK_TYPE_BUTTON,
                                  "child", g_object_new (GTK_TYPE_IMAGE,
                                                         "gicon", button_icon,
-                                                        "visible", TRUE,
                                                         NULL),
                                  "action-name", action,
                                  "action-target", target,
                                  "has-tooltip", TRUE,
                                  "tooltip-text", label,
-                                 "visible", TRUE,
                                  NULL);
-          gtk_container_add (GTK_CONTAINER (self->buttons), GTK_WIDGET (button));
+          gtk_box_append (self->buttons, GTK_WIDGET (button));
         }
     }
 
@@ -208,8 +207,6 @@ ide_notification_view_class_init (IdeNotificationViewClass *klass)
    * IdeNotificationView:notification:
    *
    * The "notification" property is the #IdeNotification to be displayed.
-   *
-   * Since: 3.32
    */
   properties [PROP_NOTIFICATION] =
     g_param_spec_object ("notification",
@@ -233,9 +230,9 @@ ide_notification_view_init (IdeNotificationView *self)
 {
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  self->bindings = dzl_binding_group_new ();
+  self->bindings = ide_binding_group_new ();
 
-  dzl_binding_group_bind (self->bindings, "title", self->label, "label", G_BINDING_SYNC_CREATE);
+  ide_binding_group_bind (self->bindings, "title", self->label, "label", G_BINDING_SYNC_CREATE);
 }
 
 /**
@@ -245,8 +242,6 @@ ide_notification_view_init (IdeNotificationView *self)
  * the #IdeOmniBar.
  *
  * Returns: (transfer full): a newly created #IdeNotificationView
- *
- * Since: 3.32
  */
 GtkWidget *
 ide_notification_view_new (void)
@@ -261,8 +256,6 @@ ide_notification_view_new (void)
  * Gets the #IdeNotification that is being viewed.
  *
  * Returns: (transfer none) (nullable): an #IdeNotification or %NULL
- *
- * Since: 3.32
  */
 IdeNotification *
 ide_notification_view_get_notification (IdeNotificationView *self)
@@ -283,7 +276,7 @@ ide_notification_view_set_notification (IdeNotificationView *self,
 
   if (g_set_object (&self->notification, notification))
     {
-      dzl_binding_group_set_source (self->bindings, notification);
+      ide_binding_group_set_source (self->bindings, notification);
       connect_notification (self, notification);
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NOTIFICATION]);
     }
diff --git a/src/libide/gui/ide-notification-view.ui b/src/libide/gui/ide-notification-view.ui
index bc7b177cf..5ea7de55a 100644
--- a/src/libide/gui/ide-notification-view.ui
+++ b/src/libide/gui/ide-notification-view.ui
@@ -1,60 +1,34 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.0 -->
 <interface>
-  <requires lib="gtk+" version="3.24"/>
-  <template class="IdeNotificationView" parent="GtkBin">
-    <property name="can_focus">False</property>
+  <requires lib="gtk" version="4.0"/>
+  <requires lib="Adw" version="1.0"/>
+  <template class="IdeNotificationView" parent="AdwBin">
     <child>
       <object class="GtkBox">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
         <property name="spacing">6</property>
         <child>
           <object class="GtkLabel" id="label">
             <property name="ellipsize">end</property>
             <property name="margin-start">6</property>
-            <property name="visible">True</property>
-            <property name="width_chars">5</property>
-            <property name="can_focus">False</property>
+            <property name="width-chars">5</property>
           </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="position">0</property>
-          </packing>
         </child>
         <child>
           <object class="GtkButton" id="default_button">
-            <property name="can_focus">True</property>
-            <property name="focus_on_click">False</property>
-            <property name="receives_default">True</property>
+            <property name="focus-on-click">False</property>
             <child>
               <object class="GtkImage" id="default_button_image">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="stock">gtk-missing-image</property>
+                <property name="icon-name">gtk-missing-image</property>
               </object>
             </child>
           </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
         </child>
         <child>
           <object class="GtkBox" id="buttons">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
             <property name="halign">end</property>
             <property name="hexpand">True</property>
             <property name="spacing">6</property>
           </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="position">2</property>
-          </packing>
         </child>
       </object>
     </child>
diff --git a/src/libide/gui/ide-notifications-button.c b/src/libide/gui/ide-notifications-button.c
index 522bc4b8f..15f2a45d0 100644
--- a/src/libide/gui/ide-notifications-button.c
+++ b/src/libide/gui/ide-notifications-button.c
@@ -22,9 +22,11 @@
 
 #include "config.h"
 
+#include <libide-gtk.h>
+
 #include "ide-notifications-button.h"
+#include "ide-notification-list-box-row-private.h"
 #include "ide-gui-global.h"
-#include "ide-gui-private.h"
 
 /**
  * SECTION:ide-notifications-button:
@@ -37,23 +39,26 @@
  *
  * The button itself will show a "combined" progress of all the active
  * notifications.
- *
- * Since: 3.32
  */
 
 struct _IdeNotificationsButton
 {
-  DzlProgressMenuButton  parent_instance;
+  GtkWidget              parent_instance;
 
   GListModel            *model;
-  DzlListModelFilter    *filter;
+  GtkFilterListModel    *filter;
 
   /* Template widgets */
+  GtkStack              *stack;
+  GtkImage              *icon;
+  IdeProgressIcon       *progress;
+  GtkMenuButton         *menu_button;
   GtkPopover            *popover;
   GtkListBox            *list_box;
+  GtkRevealer           *revealer;
 };
 
-G_DEFINE_FINAL_TYPE (IdeNotificationsButton, ide_notifications_button, DZL_TYPE_PROGRESS_MENU_BUTTON)
+G_DEFINE_FINAL_TYPE (IdeNotificationsButton, ide_notifications_button, GTK_TYPE_WIDGET)
 
 static GtkWidget *
 create_notification_row (gpointer item,
@@ -76,10 +81,10 @@ create_notification_row (gpointer item,
 }
 
 static gboolean
-filter_by_has_progress (GObject  *object,
-                        gpointer  user_data)
+filter_by_has_progress (gpointer item,
+                        gpointer user_data)
 {
-  IdeNotification *notif = (IdeNotification *)object;
+  IdeNotification *notif = item;
 
   g_assert (IDE_IS_NOTIFICATION (notif));
   g_assert (user_data == NULL);
@@ -91,18 +96,20 @@ static void
 ide_notifications_button_bind_model (IdeNotificationsButton *self,
                                      GListModel             *model)
 {
+  static GtkCustomFilter *custom;
+
   g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
   g_assert (G_IS_LIST_MODEL (model));
 
+  if (custom == NULL)
+    custom = gtk_custom_filter_new (filter_by_has_progress, NULL, NULL);
+
   if (g_set_object (&self->model, model))
     {
       g_clear_object (&self->filter);
 
-      self->filter = dzl_list_model_filter_new (model);
-      dzl_list_model_filter_set_filter_func (self->filter,
-                                             filter_by_has_progress,
-                                             NULL, NULL);
-
+      self->filter = gtk_filter_list_model_new (g_object_ref (model),
+                                                g_object_ref (GTK_FILTER (custom)));
       gtk_list_box_bind_model (self->list_box,
                                G_LIST_MODEL (self->filter),
                                create_notification_row,
@@ -115,48 +122,34 @@ ide_notifications_button_notify_has_progress_cb (IdeNotificationsButton *self,
                                                  GParamSpec             *pspec,
                                                  IdeNotifications       *notifications)
 {
-  GtkWidget *parent;
-
   g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
   g_assert (IDE_IS_NOTIFICATIONS (notifications));
 
-  parent = gtk_widget_get_parent (GTK_WIDGET (self));
-
-  /* If we are in a revealer, just toggle the revealer
-   * instead of falling back to using fading widgetry.
-   */
-  if (GTK_IS_REVEALER (parent))
-    {
-      if (ide_notifications_get_has_progress (notifications))
-        {
-          gtk_revealer_set_reveal_child (GTK_REVEALER (parent), TRUE);
-        }
-      else
-        {
-          GtkPopover *popover = gtk_menu_button_get_popover (GTK_MENU_BUTTON (self));
-
-          if (gtk_widget_get_visible (GTK_WIDGET (popover)))
-            gtk_widget_hide (GTK_WIDGET (popover));
-
-          gtk_revealer_set_reveal_child (GTK_REVEALER (parent), FALSE);
-        }
-
-      return;
-    }
-
-  /* Fallback to using widget opacity to hide/show from/to view. */
   if (ide_notifications_get_has_progress (notifications))
     {
-      if (!gtk_widget_get_visible (GTK_WIDGET (self)))
-        dzl_gtk_widget_show_with_fade (GTK_WIDGET (self));
+      gtk_revealer_set_reveal_child (self->revealer, TRUE);
     }
   else
     {
-      if (gtk_widget_get_visible (GTK_WIDGET (self)))
-        dzl_gtk_widget_hide_with_fade (GTK_WIDGET (self));
+      gtk_menu_button_popdown (self->menu_button);
+      gtk_revealer_set_reveal_child (self->revealer, FALSE);
     }
 }
 
+static void
+ide_notifications_button_notify_progress_is_imprecise_cb (IdeNotificationsButton *self,
+                                                          GParamSpec             *pspec,
+                                                          IdeNotifications       *notifications)
+{
+  g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
+  g_assert (IDE_IS_NOTIFICATIONS (notifications));
+
+  if (ide_notifications_get_progress_is_imprecise (notifications))
+    gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->icon));
+  else
+    gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->progress));
+}
+
 static void
 ide_notifications_button_context_set_cb (GtkWidget  *widget,
                                          IdeContext *context)
@@ -170,16 +163,21 @@ ide_notifications_button_context_set_cb (GtkWidget  *widget,
   notifications = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_NOTIFICATIONS);
   ide_notifications_button_bind_model (self, G_LIST_MODEL (notifications));
 
-  g_object_bind_property (notifications, "progress", self, "progress",
+  g_object_bind_property (notifications, "progress",
+                          self->progress, "progress",
                           G_BINDING_SYNC_CREATE);
   g_signal_connect_object (notifications,
                            "notify::has-progress",
                            G_CALLBACK (ide_notifications_button_notify_has_progress_cb),
                            self,
                            G_CONNECT_SWAPPED);
-  g_object_bind_property (notifications, "progress-is-imprecise", self, "show-progress",
-                          G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
+  g_signal_connect_object (notifications,
+                           "notify::has-progress",
+                           G_CALLBACK (ide_notifications_button_notify_progress_is_imprecise_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
 
+  ide_notifications_button_notify_progress_is_imprecise_cb (self, NULL, notifications);
   ide_notifications_button_notify_has_progress_cb (self, NULL, notifications);
 }
 
@@ -188,8 +186,8 @@ ide_notifications_button_row_activated (IdeNotificationsButton    *self,
                                         IdeNotificationListBoxRow *row,
                                         GtkListBox                *list_box)
 {
-  g_autofree gchar *default_action = NULL;
   g_autoptr(GVariant) default_target = NULL;
+  g_autofree char *default_action = NULL;
   IdeNotification *notif;
 
   g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
@@ -199,49 +197,43 @@ ide_notifications_button_row_activated (IdeNotificationsButton    *self,
   notif = ide_notification_list_box_row_get_notification (row);
 
   if (ide_notification_get_default_action (notif, &default_action, &default_target))
-    {
-      gchar *name = strchr (default_action, '.');
-      gchar *group = default_action;
-
-      if (name != NULL)
-        {
-          *name = '\0';
-          name++;
-        }
-      else
-        {
-          group = NULL;
-          name = default_action;
-        }
-
-      dzl_gtk_widget_action (GTK_WIDGET (list_box), group, name, default_target);
-    }
+    gtk_widget_activate_action_variant (GTK_WIDGET (row), default_action, default_target);
 }
 
 static void
-ide_notifications_button_destroy (GtkWidget *widget)
+ide_notifications_button_dispose (GObject *object)
 {
-  IdeNotificationsButton *self = (IdeNotificationsButton *)widget;
+  IdeNotificationsButton *self = (IdeNotificationsButton *)object;
 
   g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
 
   g_clear_object (&self->filter);
   g_clear_object (&self->model);
+  g_clear_pointer ((GtkWidget **)&self->revealer, gtk_widget_unparent);
 
-  GTK_WIDGET_CLASS (ide_notifications_button_parent_class)->destroy (widget);
+  G_OBJECT_CLASS (ide_notifications_button_parent_class)->dispose (object);
 }
 
 static void
 ide_notifications_button_class_init (IdeNotificationsButtonClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  widget_class->destroy = ide_notifications_button_destroy;
+  object_class->dispose = ide_notifications_button_dispose;
 
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-gui/ui/ide-notifications-button.ui");
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+  gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, icon);
   gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, list_box);
+  gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, menu_button);
   gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, popover);
+  gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, progress);
+  gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, revealer);
+  gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, stack);
   gtk_widget_class_bind_template_callback (widget_class, ide_notifications_button_row_activated);
+
+  g_type_ensure (IDE_TYPE_ANIMATION);
 }
 
 static void
@@ -259,8 +251,6 @@ ide_notifications_button_init (IdeNotificationsButton *self)
  * Create a new #IdeNotificationsButton.
  *
  * Returns: (transfer full): a newly created #IdeNotificationsButton
- *
- * Since: 3.32
  */
 GtkWidget *
 ide_notifications_button_new (void)
diff --git a/src/libide/gui/ide-notifications-button.h b/src/libide/gui/ide-notifications-button.h
index 703d63f4b..ba304c992 100644
--- a/src/libide/gui/ide-notifications-button.h
+++ b/src/libide/gui/ide-notifications-button.h
@@ -1,6 +1,6 @@
 /* ide-notifications-button.h
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2022 Christian Hergert <chergert redhat com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,16 +25,16 @@
 #endif
 
 #include <libide-core.h>
-#include <dazzle.h>
+#include <libide-gtk.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_NOTIFICATIONS_BUTTON (ide_notifications_button_get_type())
 
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdeNotificationsButton, ide_notifications_button, IDE, NOTIFICATIONS_BUTTON, 
DzlProgressMenuButton)
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeNotificationsButton, ide_notifications_button, IDE, NOTIFICATIONS_BUTTON, GtkWidget)
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 GtkWidget *ide_notifications_button_new (void);
 
 G_END_DECLS
diff --git a/src/libide/gui/ide-notifications-button.ui b/src/libide/gui/ide-notifications-button.ui
index ebf209a45..42933650e 100644
--- a/src/libide/gui/ide-notifications-button.ui
+++ b/src/libide/gui/ide-notifications-button.ui
@@ -1,32 +1,56 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="IdeNotificationsButton" parent="DzlProgressMenuButton">
-    <property name="show-progress">false</property>
-    <property name="popover">popover</property>
-  </template>
-  <object class="GtkPopover" id="popover">
-    <style>
-      <class name="notificationsbutton"/>
-    </style>
+  <template class="IdeNotificationsButton" parent="GtkWidget">
     <child>
-      <object class="GtkScrolledWindow">
-        <property name="visible">true</property>
-        <property name="max-content-width">400</property>
-        <property name="min-content-width">400</property>
-        <property name="hscrollbar-policy">never</property>
-        <property name="propagate-natural-width">false</property>
-        <property name="propagate-natural-height">true</property>
+      <object class="GtkRevealer" id="revealer">
+        <property name="transition-type">slide-left</property>
+        <property name="transition-duration">300</property>
+        <property name="reveal-child">false</property>
         <child>
-          <object class="GtkListBox" id="list_box">
-            <signal name="row-activated" handler="ide_notifications_button_row_activated" swapped="true" 
object="IdeNotificationsButton"/>
-            <property name="selection-mode">none</property>
-            <property name="visible">true</property>
+          <object class="GtkMenuButton" id="menu_button">
+            <style>
+              <class name="flat"/>
+            </style>
+            <property name="popover">
+              <object class="GtkPopover" id="popover">
+                <style>
+                  <class name="notificationsbutton"/>
+                </style>
+                <child>
+                  <object class="GtkScrolledWindow">
+                    <property name="visible">true</property>
+                    <property name="max-content-width">400</property>
+                    <property name="min-content-width">400</property>
+                    <property name="hscrollbar-policy">never</property>
+                    <property name="propagate-natural-width">false</property>
+                    <property name="propagate-natural-height">true</property>
+                    <child>
+                      <object class="GtkListBox" id="list_box">
+                        <signal name="row-activated" handler="ide_notifications_button_row_activated" 
swapped="true" object="IdeNotificationsButton"/>
+                        <property name="selection-mode">none</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </property>
+            <child>
+              <object class="GtkStack" id="stack">
+                <child>
+                  <object class="GtkImage" id="icon">
+                    <property name="icon-name">content-loading-symbolic</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="IdeProgressIcon" id="progress">
+                  </object>
+                </child>
+              </object>
+            </child>
           </object>
         </child>
       </object>
     </child>
-  </object>
+  </template>
 </interface>
-
-
-


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]