[libadwaita] toast-overlay: Allow add_toast() be called multiple times



commit ec85752f269fa0e4c8cb6d2e614317a2bf540c2c
Author: Jonathan Blandford <jrb gnome org>
Date:   Tue May 3 22:50:49 2022 -0700

    toast-overlay: Allow add_toast() be called multiple times
    
    If the toast is already shown, extend its timeout. Otherwise, bump it
    forward in the queue.
    
    Do that in the undo toast demo and the corresponding docs example.
    
    Update tests.

 demo/pages/toasts/adw-demo-page-toasts.c |  3 ++
 src/adw-toast-overlay.c                  | 76 ++++++++++++++++++++++++++++++--
 src/adw-toast-private.h                  |  7 +--
 src/adw-toast-widget-private.h           |  2 +
 src/adw-toast-widget.c                   |  9 ++++
 src/adw-toast.c                          | 44 +++++++++---------
 tests/test-toast-overlay.c               |  2 +
 7 files changed, 116 insertions(+), 27 deletions(-)
---
diff --git a/demo/pages/toasts/adw-demo-page-toasts.c b/demo/pages/toasts/adw-demo-page-toasts.c
index e1182f39..39826bcc 100644
--- a/demo/pages/toasts/adw-demo-page-toasts.c
+++ b/demo/pages/toasts/adw-demo-page-toasts.c
@@ -54,6 +54,9 @@ toast_add_with_button_cb (AdwDemoPageToasts *self)
 
     adw_toast_set_title (self->undo_toast, title);
 
+    /* Bump the toast timeout */
+    add_toast (self, g_object_ref (self->undo_toast));
+
     g_free (title);
   } else {
     self->undo_toast = adw_toast_new_format (_("ā€˜%sā€™ deleted"), "Lorem Ipsum");
diff --git a/src/adw-toast-overlay.c b/src/adw-toast-overlay.c
index b1c5dd7b..28845a3b 100644
--- a/src/adw-toast-overlay.c
+++ b/src/adw-toast-overlay.c
@@ -220,6 +220,7 @@ dismissed_cb (ToastInfo *info)
   } else {
     g_queue_remove (self->queue, info);
 
+    adw_toast_set_overlay (ADW_TOAST (info->toast), NULL);
     if (!info->hide_animation)
       free_toast_info (info);
   }
@@ -259,6 +260,51 @@ show_toast (AdwToastOverlay *self,
   adw_animation_play (info->show_animation);
 }
 
+static int
+bump_sort_func (ToastInfo *compare,
+                AdwToast  *toast,
+                gpointer   user_data)
+{
+  if (adw_toast_get_priority (compare->toast) == ADW_TOAST_PRIORITY_HIGH)
+    return -1;
+
+  return 1;
+}
+
+static int
+find_toast_func (ToastInfo *info,
+                 AdwToast  *toast)
+{
+  if (info && info->toast == toast)
+    return 0;
+
+  return 1;
+}
+
+static void
+bump_toast (AdwToastOverlay *self,
+            AdwToast        *toast)
+{
+  GList *link;
+  ToastInfo *info;
+
+  /* Remove it from the queue, then reinsert in the right location */
+  link = g_queue_find_custom (self->queue, toast,
+                              (GCompareFunc) find_toast_func);
+
+  g_assert (link);
+
+  info = link->data;
+  g_queue_remove (self->queue, info);
+
+  if (adw_toast_get_priority (toast) == ADW_TOAST_PRIORITY_HIGH)
+    g_queue_push_head (self->queue, info);
+  else
+    g_queue_insert_sorted (self->queue, info,
+                           (GCompareDataFunc) bump_sort_func,
+                           NULL);
+}
+
 static gboolean
 dismiss_cb (AdwToastOverlay *self,
             GVariant        *args)
@@ -570,6 +616,11 @@ adw_toast_overlay_set_child (AdwToastOverlay *self,
  * either @toast or the original toast will be placed in a queue, depending on
  * the priority of @toast. See [property@Toast:priority].
  *
+ * If called on a toast that's already displayed, its timeout will be reset.
+ *
+ * If called on a toast currently in the queue, the toast will be bumped
+ * forward to be shown as soon as possible.
+ *
  * Since: 1.0
  */
 void
@@ -577,18 +628,35 @@ adw_toast_overlay_add_toast (AdwToastOverlay *self,
                              AdwToast        *toast)
 {
   ToastInfo *info;
+  AdwToastOverlay *overlay;
 
   g_return_if_fail (ADW_IS_TOAST_OVERLAY (self));
   g_return_if_fail (ADW_IS_TOAST (toast));
 
-  if (adw_toast_get_added (toast)) {
-    g_critical ("Adding toast '%s', but it has already been added to an "
-                "AdwToastOverlay", adw_toast_get_title (toast));
+  overlay = adw_toast_get_overlay (toast);
+
+  /* If the toast has been added already and is being shown, reset its
+   * timeout. Otherwise, bump it forward in the queue. */
+  if (overlay == self) {
+    if (self->current_toast && self->current_toast->toast == toast)
+      adw_toast_widget_reset_timeout (ADW_TOAST_WIDGET (self->current_toast->widget));
+    else
+      bump_toast (self, toast);
+
+    /* Unref, as we don't actually take the ownership */
+    g_object_unref (toast);
+    return;
+  }
+
+  if (overlay) {
+    g_critical ("Adding toast '%s', but it has already been added to a "
+                "different AdwToastOverlay", adw_toast_get_title (toast));
 
+    g_object_unref (toast);
     return;
   }
 
-  adw_toast_set_added (toast, TRUE);
+  adw_toast_set_overlay (toast, self);
 
   info = g_new0 (ToastInfo, 1);
   info->overlay = self;
diff --git a/src/adw-toast-private.h b/src/adw-toast-private.h
index b9fa3907..4dc78b9b 100644
--- a/src/adw-toast-private.h
+++ b/src/adw-toast-private.h
@@ -13,11 +13,12 @@
 #endif
 
 #include "adw-toast.h"
+#include "adw-toast-overlay.h"
 
 G_BEGIN_DECLS
 
-gboolean adw_toast_get_added (AdwToast *self);
-void     adw_toast_set_added (AdwToast *self,
-                              gboolean  added);
+AdwToastOverlay *adw_toast_get_overlay (AdwToast        *self);
+void             adw_toast_set_overlay (AdwToast        *self,
+                                        AdwToastOverlay *overlay);
 
 G_END_DECLS
diff --git a/src/adw-toast-widget-private.h b/src/adw-toast-widget-private.h
index ff523f71..6362899d 100644
--- a/src/adw-toast-widget-private.h
+++ b/src/adw-toast-widget-private.h
@@ -23,4 +23,6 @@ G_DECLARE_FINAL_TYPE (AdwToastWidget, adw_toast_widget, ADW, TOAST_WIDGET, GtkWi
 
 GtkWidget *adw_toast_widget_new (AdwToast *toast) G_GNUC_WARN_UNUSED_RESULT;
 
+void adw_toast_widget_reset_timeout (AdwToastWidget *self);
+
 G_END_DECLS
diff --git a/src/adw-toast-widget.c b/src/adw-toast-widget.c
index 2e9b832a..698edcbf 100644
--- a/src/adw-toast-widget.c
+++ b/src/adw-toast-widget.c
@@ -263,3 +263,12 @@ adw_toast_widget_new (AdwToast *toast)
                        "toast", toast,
                        NULL);
 }
+
+void
+adw_toast_widget_reset_timeout (AdwToastWidget *self)
+{
+  g_assert (ADW_IS_TOAST_WIDGET (self));
+
+  end_timeout (self);
+  start_timeout (self);
+}
diff --git a/src/adw-toast.c b/src/adw-toast.c
index 3af4ac6d..431d7f4b 100644
--- a/src/adw-toast.c
+++ b/src/adw-toast.c
@@ -127,6 +127,9 @@
  *                                n_items), n_items);
  *
  *   adw_toast_set_title (self->undo_toast, title);
+ *
+ *   // Bump the toast timeout
+ *   adw_toast_overlay_add_toast (self->toast_overlay, g_object_ref (self->undo_toast));
  * }
  *
  * static void
@@ -157,7 +160,7 @@ struct _AdwToast {
   guint timeout;
   GtkWidget *custom_title;
 
-  gboolean added;
+  AdwToastOverlay *overlay;
 };
 
 enum {
@@ -186,7 +189,7 @@ G_DEFINE_FINAL_TYPE (AdwToast, adw_toast, G_TYPE_OBJECT)
 static void
 dismissed_cb (AdwToast *self)
 {
-  self->added = FALSE;
+  adw_toast_set_overlay (self, NULL);
 }
 
 static void
@@ -882,29 +885,12 @@ adw_toast_dismiss (AdwToast *self)
 {
   g_return_if_fail (ADW_IS_TOAST (self));
 
-  if (!self->added)
+  if (!self->overlay)
     return;
 
   g_signal_emit (self, signals[SIGNAL_DISMISSED], 0, NULL);
 }
 
-gboolean
-adw_toast_get_added (AdwToast *self)
-{
-  g_return_val_if_fail (ADW_IS_TOAST (self), FALSE);
-
-  return self->added;
-}
-
-void
-adw_toast_set_added (AdwToast *self,
-                     gboolean  added)
-{
-  g_return_if_fail (ADW_IS_TOAST (self));
-
-  self->added = !!added;
-}
-
 /**
  * adw_toast_set_custom_title: (attributes org.gtk.Method.set_property=custom-title)
  * @self: a toast
@@ -952,3 +938,21 @@ adw_toast_get_custom_title (AdwToast *self)
 
   return self->custom_title;
 }
+
+AdwToastOverlay *
+adw_toast_get_overlay (AdwToast *self)
+{
+  g_return_val_if_fail (ADW_IS_TOAST (self), NULL);
+
+  return self->overlay;
+}
+
+void
+adw_toast_set_overlay (AdwToast        *self,
+                       AdwToastOverlay *overlay)
+{
+  g_return_if_fail (ADW_IS_TOAST (self));
+  g_return_if_fail (overlay == NULL || ADW_IS_TOAST_OVERLAY (overlay));
+
+  self->overlay = overlay;
+}
diff --git a/tests/test-toast-overlay.c b/tests/test-toast-overlay.c
index 45b92047..e66eabae 100644
--- a/tests/test-toast-overlay.c
+++ b/tests/test-toast-overlay.c
@@ -52,6 +52,8 @@ test_adw_toast_overlay_add_toast (void)
   g_assert_nonnull (toast_overlay);
   g_assert_nonnull (toast);
 
+  adw_toast_overlay_add_toast (toast_overlay, g_object_ref (toast));
+  adw_toast_overlay_add_toast (toast_overlay, g_object_ref (toast));
   adw_toast_overlay_add_toast (toast_overlay, g_object_ref (toast));
 
   g_assert_finalize_object (toast_overlay);


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