[libadwaita/wip/exalm/avatar-paintable: 207/209] avatar: Replace image load func with paintables




commit 9660614690c24d2edf81a5a3d0775b25abf1e1b9
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Mon Apr 26 14:49:37 2021 +0500

    avatar: Replace image load func with paintables
    
    Introduce #AdwAvatar:custom-image. The original motivation for having image
    load functions was to make it work with HiDPI which paintables already can
    do.
    
    Since then, libhandy has introduced a GLoadableIcon-based API to allow
    async loading, but paintables are generic enough that it should be possible
    to implement it as well.

 examples/adw-demo-window.c |  42 +++---------
 src/adw-avatar.c           | 156 +++++++++++++++++++--------------------------
 src/adw-avatar.h           |  23 ++-----
 3 files changed, 79 insertions(+), 142 deletions(-)
---
diff --git a/examples/adw-demo-window.c b/examples/adw-demo-window.c
index d0b6d62d..6dcac668 100644
--- a/examples/adw-demo-window.c
+++ b/examples/adw-demo-window.c
@@ -215,37 +215,7 @@ avatar_file_remove_cb (AdwDemoWindow *self)
 
   gtk_label_set_label (self->avatar_file_chooser_label, _("(None)"));
   gtk_widget_set_sensitive (GTK_WIDGET (self->avatar_remove_button), FALSE);
-  adw_avatar_set_image_load_func (self->avatar, NULL, NULL, NULL);
-}
-
-static GdkPixbuf *
-avatar_load_file (int size, AdwDemoWindow *self)
-{
-  g_autoptr (GError) error = NULL;
-  g_autoptr (GdkPixbuf) pixbuf = NULL;
-  g_autoptr (GFile) file = NULL;
-  g_autofree char *filename = NULL;
-  int width, height;
-
-  g_assert (ADW_IS_DEMO_WINDOW (self));
-
-  file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (self->avatar_file_chooser));
-  filename = g_file_get_path (file);
-
-  gdk_pixbuf_get_file_info (filename, &width, &height);
-
-  pixbuf = gdk_pixbuf_new_from_file_at_scale (filename,
-                                              (width <= height) ? size : -1,
-                                              (width >= height) ? size : -1,
-                                              TRUE,
-                                              &error);
-  if (error != NULL) {
-    g_critical ("Failed to create pixbuf from file: %s", error->message);
-
-    return NULL;
-  }
-
-  return g_steal_pointer (&pixbuf);
+  adw_avatar_set_custom_image (self->avatar, NULL);
 }
 
 static void
@@ -254,6 +224,8 @@ avatar_file_chooser_response_cb (AdwDemoWindow *self,
 {
   g_autoptr (GFile) file = NULL;
   g_autoptr (GFileInfo) info = NULL;
+  g_autoptr (GdkTexture) texture = NULL;
+  g_autoptr (GError) error = NULL;
 
   g_assert (ADW_IS_DEMO_WINDOW (self));
 
@@ -272,7 +244,13 @@ avatar_file_chooser_response_cb (AdwDemoWindow *self,
                          g_file_info_get_display_name (info));
 
   gtk_widget_set_sensitive (GTK_WIDGET (self->avatar_remove_button), TRUE);
-  adw_avatar_set_image_load_func (self->avatar, (AdwAvatarImageLoadFunc) avatar_load_file, self, NULL);
+
+  texture = gdk_texture_new_from_file (file, &error);
+
+  if (error)
+    g_critical ("Failed to create texture from file: %s", error->message);
+
+  adw_avatar_set_custom_image (self->avatar, texture ? GDK_PAINTABLE (texture) : NULL);
 }
 
 static void
diff --git a/src/adw-avatar.c b/src/adw-avatar.c
index 2352048a..fd6d815b 100644
--- a/src/adw-avatar.c
+++ b/src/adw-avatar.c
@@ -33,8 +33,7 @@
  * [property@Adw.Avatar:icon-name] or `avatar-default-symbolic` is shown instead
  * of the initials.
  *
- * [method@Adw.Avatar.set_image_load_func] can be used to provide a custom
- * image.
+ * Use [property@Adw.Avatar:custom-image] to set a custom image.
  *
  * ## CSS nodes
  *
@@ -57,12 +56,7 @@ struct _AdwAvatar
   gboolean show_initials;
   guint color_class;
   int size;
-  GdkTexture *texture;
-  int round_image_size;
-
-  AdwAvatarImageLoadFunc load_image_func;
-  gpointer load_image_func_target;
-  GDestroyNotify load_image_func_target_destroy_notify;
+  GdkPaintable *custom_paintable;
 };
 
 G_DEFINE_TYPE (AdwAvatar, adw_avatar, GTK_TYPE_WIDGET);
@@ -72,6 +66,7 @@ enum {
   PROP_ICON_NAME,
   PROP_TEXT,
   PROP_SHOW_INITIALS,
+  PROP_CUSTOM_IMAGE,
   PROP_SIZE,
   PROP_LAST_PROP,
 };
@@ -108,7 +103,7 @@ extract_initials_from_text (const char *text)
 static void
 update_visibility (AdwAvatar *self)
 {
-  gboolean has_custom_image = self->texture != NULL;
+  gboolean has_custom_image = self->custom_paintable != NULL;
   gboolean has_initials = self->show_initials && self->text && strlen (self->text);
 
   gtk_widget_set_visible (GTK_WIDGET (self->label), !has_custom_image && has_initials);
@@ -116,53 +111,6 @@ update_visibility (AdwAvatar *self)
   gtk_widget_set_visible (GTK_WIDGET (self->custom_image), has_custom_image);
 }
 
-static void
-update_custom_image (AdwAvatar *self,
-                     int        width,
-                     int        height,
-                     int        scale_factor)
-{
-  g_autoptr (GdkPixbuf) pixbuf = NULL;
-  int new_size;
-
-  new_size = MIN (width, height) * scale_factor;
-
-  if (self->round_image_size != new_size && self->texture != NULL) {
-    self->round_image_size = -1;
-  }
-  g_clear_object (&self->texture);
-
-  if (self->load_image_func != NULL && self->texture == NULL) {
-    pixbuf = self->load_image_func (new_size, self->load_image_func_target);
-    if (pixbuf != NULL) {
-      if (width != height) {
-        GdkPixbuf *subpixbuf;
-
-        subpixbuf = gdk_pixbuf_new_subpixbuf (pixbuf,
-                                              (width - new_size) / 2,
-                                              (height - new_size) / 2,
-                                              new_size,
-                                              new_size);
-
-        g_object_unref (pixbuf);
-
-        pixbuf = subpixbuf;
-      }
-
-      self->texture = gdk_texture_new_for_pixbuf (pixbuf);
-      self->round_image_size = new_size;
-    }
-  }
-
-  if (self->texture)
-    gtk_widget_add_css_class (self->gizmo, "image");
-  else
-    gtk_widget_remove_css_class (self->gizmo, "image");
-
-  gtk_image_set_from_paintable (self->custom_image, GDK_PAINTABLE (self->texture));
-  update_visibility (self);
-}
-
 static void
 set_class_color (AdwAvatar *self)
 {
@@ -190,7 +138,7 @@ update_initials (AdwAvatar *self)
 {
   g_autofree char *initials = NULL;
 
-  if (self->texture != NULL ||
+  if (self->custom_paintable != NULL ||
       !self->show_initials ||
       self->text == NULL ||
       strlen (self->text) == 0)
@@ -220,7 +168,7 @@ update_font_size (AdwAvatar *self)
   double new_font_size;
   PangoAttrList *attributes;
 
-  if (self->texture != NULL ||
+  if (self->custom_paintable != NULL ||
       !self->show_initials ||
       self->text == NULL ||
       strlen (self->text) == 0)
@@ -269,7 +217,11 @@ adw_avatar_get_property (GObject    *object,
     g_value_set_boolean (value, adw_avatar_get_show_initials (self));
     break;
 
-  case PROP_SIZE:
+  case PROP_CUSTOM_IMAGE:
+    g_value_set_object (value, adw_avatar_get_custom_image (self));
+    break;
+
+    case PROP_SIZE:
     g_value_set_int (value, adw_avatar_get_size (self));
     break;
 
@@ -300,6 +252,10 @@ adw_avatar_set_property (GObject      *object,
     adw_avatar_set_show_initials (self, g_value_get_boolean (value));
     break;
 
+  case PROP_CUSTOM_IMAGE:
+    adw_avatar_set_custom_image (self, g_value_get_object (value));
+    break;
+
   case PROP_SIZE:
     adw_avatar_set_size (self, g_value_get_int (value));
     break;
@@ -331,10 +287,7 @@ adw_avatar_finalize (GObject *object)
 
   g_clear_pointer (&self->icon_name, g_free);
   g_clear_pointer (&self->text, g_free);
-  g_clear_object (&self->texture);
-
-  if (self->load_image_func_target_destroy_notify != NULL)
-    self->load_image_func_target_destroy_notify (self->load_image_func_target);
+  g_clear_object (&self->custom_paintable);
 
   G_OBJECT_CLASS (adw_avatar_parent_class)->finalize (object);
 }
@@ -399,6 +352,20 @@ adw_avatar_class_init (AdwAvatarClass *klass)
                           FALSE,
                           G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
+  /**
+   * AdwAvatar:custom-image: (attributes org.gtk.Property.get=adw_avatar_get_custom_image 
org.gtk.Property.set=adw_avatar_set_custom_image)
+   *
+   * A custom image to use instead of initials or icon.
+   *
+   * Since: 1.0
+   */
+  props[PROP_CUSTOM_IMAGE] =
+    g_param_spec_object ("custom-image",
+                         "Custom image",
+                         "A custom image to use instead of initials or icon",
+                         GDK_TYPE_PAINTABLE,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
   /**
    * AdwAvatar:size: (attributes org.gtk.Property.get=adw_avatar_get_size 
org.gtk.Property.set=adw_avatar_set_size)
    *
@@ -443,7 +410,6 @@ adw_avatar_init (AdwAvatar *self)
   update_icon (self);
   update_visibility (self);
 
-  g_signal_connect (self, "notify::scale-factor", G_CALLBACK (update_custom_image), NULL);
   g_signal_connect (self, "notify::root", G_CALLBACK (update_font_size), NULL);
 }
 
@@ -612,39 +578,53 @@ adw_avatar_set_show_initials (AdwAvatar *self,
 }
 
 /**
- * adw_avatar_set_image_load_func:
+ * adw_avatar_get_custom_image: (attributes org.gtk.Method.get_property=custom-image)
  * @self: a `AdwAvatar`
- * @load_image: (scope notified) (nullable): callback to set a custom image
- * @user_data: (closure load_image) (nullable): user data passed to @load_image
- * @destroy: (destroy user_data) (nullable): destroy notifier for @user_data
  *
- * Sets a function that loads a custom image.
+ * Gets the custom image paintable.
+ *
+ * Returns: (nullable) (transfer none): the custom image
+ *
+ * Since: 1.0
+ */
+GdkPaintable *
+adw_avatar_get_custom_image (AdwAvatar *self)
+{
+  g_return_val_if_fail (ADW_IS_AVATAR (self), NULL);
+
+  return self->custom_paintable;
+}
+
+/**
+ * adw_avatar_set_custom_image: (attributes org.gtk.Method.set_property=custom-image)
+ * @self: a `AdwAvatar`
+ * @custom_image: (nullable) (transfer none): a custom image
  *
- * @load_image will be called when the custom image needs to be reloaded for
- * some reason (e.g. scale-factor changes).
+ * Sets the custom image paintable.
  *
  * Since: 1.0
  */
 void
-adw_avatar_set_image_load_func (AdwAvatar              *self,
-                                AdwAvatarImageLoadFunc  load_image,
-                                gpointer                user_data,
-                                GDestroyNotify          destroy)
+adw_avatar_set_custom_image (AdwAvatar    *self,
+                             GdkPaintable *custom_image)
 {
   g_return_if_fail (ADW_IS_AVATAR (self));
-  g_return_if_fail (user_data != NULL || (user_data == NULL && destroy == NULL));
+  g_return_if_fail (GDK_IS_PAINTABLE (custom_image) || custom_image == NULL);
 
-  if (self->load_image_func_target_destroy_notify != NULL)
-    self->load_image_func_target_destroy_notify (self->load_image_func_target);
+  if (self->custom_paintable == custom_image)
+    return;
 
-  self->load_image_func = load_image;
-  self->load_image_func_target = user_data;
-  self->load_image_func_target_destroy_notify = destroy;
+  g_set_object (&self->custom_paintable, custom_image);
 
-  update_custom_image (self,
-                       gtk_widget_get_width (GTK_WIDGET (self)),
-                       gtk_widget_get_height (GTK_WIDGET (self)),
-                       gtk_widget_get_scale_factor (GTK_WIDGET (self)));
+  if (self->custom_paintable)
+    gtk_widget_add_css_class (self->gizmo, "image");
+  else
+    gtk_widget_remove_css_class (self->gizmo, "image");
+
+  gtk_image_set_from_paintable (self->custom_image, custom_image);
+  update_visibility (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CUSTOM_IMAGE]);
 }
 
 /**
@@ -695,10 +675,6 @@ adw_avatar_set_size (AdwAvatar *self,
     gtk_widget_remove_css_class (self->gizmo, "contrasted");
 
   update_font_size (self);
-  update_custom_image (self,
-                       gtk_widget_get_width (GTK_WIDGET (self)),
-                       gtk_widget_get_height (GTK_WIDGET (self)),
-                       gtk_widget_get_scale_factor (GTK_WIDGET (self)));
 
   gtk_widget_queue_resize (GTK_WIDGET (self));
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SIZE]);
@@ -733,8 +709,6 @@ adw_avatar_draw_to_pixbuf (AdwAvatar *self,
   g_return_val_if_fail (size > 0, NULL);
   g_return_val_if_fail (scale_factor > 0, NULL);
 
-  update_custom_image (self, size, size, scale_factor);
-
   snapshot = gtk_snapshot_new ();
   GTK_WIDGET_GET_CLASS (self)->snapshot (GTK_WIDGET (self), snapshot);
 
diff --git a/src/adw-avatar.h b/src/adw-avatar.h
index e7040b60..ab5682e9 100644
--- a/src/adw-avatar.h
+++ b/src/adw-avatar.h
@@ -22,21 +22,6 @@ G_BEGIN_DECLS
 ADW_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (AdwAvatar, adw_avatar, ADW, AVATAR, GtkWidget)
 
-/**
- * AdwAvatarImageLoadFunc:
- * @size: the required size of the avatar
- * @user_data: (nullable): user data
- *
- * A callback for loading a custom image for [class@Adw.Avatar].
- *
- * The returned [class@GdkPixbuf.Pixbuf] will be cropped to a circle from the
- * center.
- *
- * Returns: (nullable) (transfer full): The pixbuf to use as a custom avatar.
- */
-typedef GdkPixbuf *(* AdwAvatarImageLoadFunc) (int      size,
-                                               gpointer user_data);
-
 ADW_AVAILABLE_IN_ALL
 GtkWidget *adw_avatar_new (int         size,
                            const char *text,
@@ -61,10 +46,10 @@ void     adw_avatar_set_show_initials (AdwAvatar *self,
                                        gboolean   show_initials);
 
 ADW_AVAILABLE_IN_ALL
-void adw_avatar_set_image_load_func (AdwAvatar              *self,
-                                     AdwAvatarImageLoadFunc  load_image,
-                                     gpointer                user_data,
-                                     GDestroyNotify          destroy);
+GdkPaintable *adw_avatar_get_custom_image (AdwAvatar *self);
+ADW_AVAILABLE_IN_ALL
+void          adw_avatar_set_custom_image (AdwAvatar    *self,
+                                           GdkPaintable *custom_image);
 
 ADW_AVAILABLE_IN_ALL
 int  adw_avatar_get_size (AdwAvatar *self);


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