[libhandy] avatar: Add loading from GLoadableIcon
- From: Adrien Plazas <aplazas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libhandy] avatar: Add loading from GLoadableIcon
- Date: Thu, 11 Feb 2021 09:11:36 +0000 (UTC)
commit f60a2b8cd2f4ed6272fbd6df46acabf0c7ba899f
Author: Julian Sparber <julian sparber net>
Date: Fri Oct 30 16:28:20 2020 +0100
avatar: Add loading from GLoadableIcon
debian/libhandy-1-0.symbols | 2 +
src/hdy-avatar.c | 537 +++++++++++++++++++++++++++++++++++++++-----
src/hdy-avatar.h | 5 +
3 files changed, 494 insertions(+), 50 deletions(-)
---
diff --git a/debian/libhandy-1-0.symbols b/debian/libhandy-1-0.symbols
index cab1fa7e..c4f095ad 100644
--- a/debian/libhandy-1-0.symbols
+++ b/debian/libhandy-1-0.symbols
@@ -23,6 +23,7 @@ libhandy-1.so.0 libhandy-1-0 #MINVER#
hdy_avatar_get_show_initials@LIBHANDY_1_0 0.80.0
hdy_avatar_get_size@LIBHANDY_1_0 0.80.0
hdy_avatar_get_text@LIBHANDY_1_0 0.80.0
+ hdy_avatar_get_loadable_icon@LIBHANDY_1_0 1.1.0
hdy_avatar_get_type@LIBHANDY_1_0 0.80.0
hdy_avatar_new@LIBHANDY_1_0 0.80.0
hdy_avatar_set_icon_name@LIBHANDY_1_0 0.85.0
@@ -30,6 +31,7 @@ libhandy-1.so.0 libhandy-1-0 #MINVER#
hdy_avatar_set_show_initials@LIBHANDY_1_0 0.80.0
hdy_avatar_set_size@LIBHANDY_1_0 0.80.0
hdy_avatar_set_text@LIBHANDY_1_0 0.80.0
+ hdy_avatar_set_loadable_icon@LIBHANDY_1_0 1.1.0
hdy_carousel_get_allow_long_swipes@LIBHANDY_1_0 1.1.0
hdy_carousel_get_allow_mouse_drag@LIBHANDY_1_0 0.80.0
hdy_carousel_get_animation_duration@LIBHANDY_1_0 0.80.0
diff --git a/src/hdy-avatar.c b/src/hdy-avatar.c
index 08a66efb..75166278 100644
--- a/src/hdy-avatar.c
+++ b/src/hdy-avatar.c
@@ -14,10 +14,12 @@
#include <math.h>
#include "hdy-avatar.h"
+#include "hdy-avatar-icon-private.h"
#include "hdy-cairo-private.h"
#include "hdy-css-private.h"
#define NUMBER_OF_COLORS 14
+#define LOAD_BUFFER_SIZE 65536
/**
* SECTION:hdy-avatar
* @short_description: A widget displaying an image, with a generated fallback.
@@ -75,12 +77,13 @@ struct _HdyAvatar
gboolean show_initials;
guint color_class;
gint size;
- cairo_surface_t *round_image;
- gint round_image_size;
+ GdkPixbuf *round_image;
- HdyAvatarImageLoadFunc load_image_func;
- gpointer load_image_func_target;
- GDestroyNotify load_image_func_target_destroy_notify;
+ HdyAvatarIcon *load_func_icon;
+ GLoadableIcon *icon;
+ GCancellable *cancellable;
+ guint currently_loading_size;
+ gboolean loading_error;
};
G_DEFINE_TYPE (HdyAvatar, hdy_avatar, GTK_TYPE_DRAWING_AREA);
@@ -91,13 +94,36 @@ enum {
PROP_TEXT,
PROP_SHOW_INITIALS,
PROP_SIZE,
+ PROP_LOADABLE_ICON,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
-static cairo_surface_t *
-round_image (GdkPixbuf *pixbuf,
- gdouble size)
+static void
+load_icon_async (GLoadableIcon *icon,
+ gint size,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+static inline GLoadableIcon *
+get_icon (HdyAvatar *self)
+{
+ if (self->icon)
+ return self->icon;
+
+ return G_LOADABLE_ICON (self->load_func_icon);
+}
+
+static inline gboolean
+is_scaled (GdkPixbuf *pixbuf)
+{
+ return (pixbuf && g_object_get_data (G_OBJECT (pixbuf), "scaled") != NULL);
+}
+
+static GdkPixbuf *
+make_round_image (GdkPixbuf *pixbuf,
+ gdouble size)
{
g_autoptr (cairo_surface_t) surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size, size);
g_autoptr (cairo_t) cr = cairo_create (surface);
@@ -112,7 +138,7 @@ round_image (GdkPixbuf *pixbuf,
gdk_cairo_set_source_pixbuf (cr, pixbuf, (size - width) / 2, (size - height) / 2);
cairo_paint (cr);
- return g_steal_pointer (&surface);
+ return gdk_pixbuf_get_from_surface (surface, 0, 0, size, size);
}
static gchar *
@@ -143,36 +169,280 @@ extract_initials_from_text (const gchar *text)
return g_string_free (initials, FALSE);
}
+static GdkPixbuf *
+update_custom_image (GLoadableIcon *icon,
+ GdkPixbuf *pixbuf_from_icon,
+ GdkPixbuf *round_image,
+ gint new_size)
+{
+ if (icon == NULL)
+ return NULL;
+
+ if (round_image &&
+ gdk_pixbuf_get_width (round_image) == new_size &&
+ !is_scaled (round_image))
+ return g_object_ref (round_image);
+
+ if (pixbuf_from_icon) {
+ gint pixbuf_from_icon_size = MIN (gdk_pixbuf_get_width (pixbuf_from_icon),
+ gdk_pixbuf_get_height (pixbuf_from_icon));
+ if (pixbuf_from_icon_size == new_size)
+ return make_round_image (pixbuf_from_icon, new_size);
+ }
+
+ if (round_image) {
+ /* Use a scaled image till we get the new image from async loading */
+ GdkPixbuf *pixbuf = gdk_pixbuf_scale_simple (round_image,
+ new_size,
+ new_size,
+ GDK_INTERP_BILINEAR);
+ g_object_set_data (G_OBJECT (pixbuf), "scaled", GINT_TO_POINTER (TRUE));
+
+ return pixbuf;
+ }
+
+ return NULL;
+}
+
+static void
+size_prepared_cb (GdkPixbufLoader *loader,
+ gint width,
+ gint height,
+ gpointer user_data)
+{
+ gint size = GPOINTER_TO_INT (user_data);
+ gdouble ratio = (gdouble) width / (gdouble) height;
+
+ if (width < height) {
+ width = size;
+ height = size / ratio;
+ } else {
+ width = size * ratio;
+ height = size;
+ }
+
+ gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
+/* This function is copied from the gdk-pixbuf project,
+ * from the file gdk-pixbuf/gdk-pixbuf/gdk-pixbuf-io.c.
+ * It was modified to fit libhandy's code style.
+ */
+static void
+load_from_stream_async_cb (GObject *stream,
+ GAsyncResult *res,
+ gpointer data)
+{
+ g_autoptr (GTask) task = data;
+ GdkPixbufLoader *loader = g_task_get_task_data (task);
+ g_autoptr (GBytes) bytes = NULL;
+ GError *error = NULL;
+
+ bytes = g_input_stream_read_bytes_finish (G_INPUT_STREAM (stream), res, &error);
+ if (bytes == NULL) {
+ gdk_pixbuf_loader_close (loader, NULL);
+ g_task_return_error (task, error);
+
+ return;
+ }
+
+ if (g_bytes_get_size (bytes) == 0) {
+ if (!gdk_pixbuf_loader_close (loader, &error)) {
+ g_task_return_error (task, error);
+
+ return;
+ }
+
+ g_task_return_pointer (task,
+ g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader)),
+ g_object_unref);
+
+ return;
+ }
+
+ if (!gdk_pixbuf_loader_write (loader,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ &error)) {
+ gdk_pixbuf_loader_close (loader, NULL);
+ g_task_return_error (task, error);
+
+ return;
+ }
+
+ g_input_stream_read_bytes_async (G_INPUT_STREAM (stream),
+ LOAD_BUFFER_SIZE,
+ G_PRIORITY_DEFAULT,
+ g_task_get_cancellable (task),
+ load_from_stream_async_cb,
+ g_object_ref (task));
+}
+
static void
-update_custom_image (HdyAvatar *self,
- gint width,
- gint height,
- gint scale_factor)
+icon_load_async_cb (GLoadableIcon *icon,
+ GAsyncResult *res,
+ GTask *task)
{
+ g_autoptr (GInputStream) stream = NULL;
+ g_autoptr (GError) error = NULL;
+
+ stream = g_loadable_icon_load_finish (icon, res, NULL, &error);
+ if (stream == NULL) {
+ g_task_return_error (task, g_steal_pointer (&error));
+
+ return;
+ }
+
+ g_input_stream_read_bytes_async (stream,
+ LOAD_BUFFER_SIZE,
+ G_PRIORITY_DEFAULT,
+ g_task_get_cancellable (task),
+ load_from_stream_async_cb,
+ task);
+}
+
+static GdkPixbuf *
+load_from_gicon_async_finish (GAsyncResult *async_result,
+ GError **error)
+{
+ GTask *task = G_TASK (async_result);
+
+ return g_task_propagate_pointer (task, error);
+}
+
+static void
+load_from_gicon_async_for_display_cb (GLoadableIcon *icon,
+ GAsyncResult *res,
+ gpointer *user_data)
+{
+ HdyAvatar *self = HDY_AVATAR (user_data);
+ g_autoptr (GError) error = NULL;
g_autoptr (GdkPixbuf) pixbuf = NULL;
- gint new_size;
- GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (self));
- new_size = MIN (width, height) * scale_factor;
+ pixbuf = load_from_gicon_async_finish (res, &error);
- if (self->round_image_size != new_size && self->round_image != NULL) {
- g_clear_pointer (&self->round_image, cairo_surface_destroy);
- self->round_image_size = -1;
+ if (error != NULL) {
+ if (g_error_matches (error, HDY_AVATAR_ICON_ERROR, HDY_AVATAR_ICON_ERROR_EMPTY)) {
+ self->loading_error = TRUE;
+ } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("Failed to load icon: %s", error->message);
+ self->loading_error = TRUE;
+ }
}
- if (self->load_image_func != NULL && self->round_image == NULL) {
- pixbuf = self->load_image_func (new_size, self->load_image_func_target);
- if (pixbuf != NULL) {
- self->round_image = round_image (pixbuf, (gdouble) new_size);
- cairo_surface_set_device_scale (self->round_image, scale_factor, scale_factor);
- self->round_image_size = new_size;
+ self->currently_loading_size = -1;
+
+ if (pixbuf) {
+ g_autoptr (GdkPixbuf) custom_image = NULL;
+ GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gint width = gtk_widget_get_allocated_width (GTK_WIDGET (self));
+ gint height = gtk_widget_get_allocated_height (GTK_WIDGET (self));
+ gint scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ gint new_size = MIN (width, height) * scale_factor;
+
+ custom_image = update_custom_image (icon,
+ pixbuf,
+ NULL,
+ new_size);
+
+ if (!self->round_image && custom_image)
+ gtk_style_context_add_class (context, "image");
+
+ g_set_object (&self->round_image, custom_image);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+load_icon_async (GLoadableIcon *icon,
+ gint size,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task = g_task_new (icon, cancellable, callback, user_data);
+ GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
+
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (size_prepared_cb),
+ GINT_TO_POINTER (size));
+
+ g_task_set_task_data (task, loader, g_object_unref);
+
+ g_loadable_icon_load_async (icon, size, cancellable,
+ (GAsyncReadyCallback) icon_load_async_cb,
+ task);
+}
+
+/* This function is copied from the gdk-pixbuf project,
+ * from the file gdk-pixbuf/gdk-pixbuf/gdk-pixbuf-io.c.
+ * It was modified to fit libhandy's code style.
+ */
+static GdkPixbuf *
+load_from_stream (GdkPixbufLoader *loader,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ guchar buffer[LOAD_BUFFER_SIZE];
+
+ while (TRUE) {
+ gssize n_read = g_input_stream_read (stream, buffer, sizeof (buffer),
+ cancellable, error);
+
+ if (n_read < 0) {
+ gdk_pixbuf_loader_close (loader, NULL);
+
+ return NULL;
+ }
+
+ if (n_read == 0)
+ break;
+
+ if (!gdk_pixbuf_loader_write (loader, buffer, n_read, error)) {
+ gdk_pixbuf_loader_close (loader, NULL);
+
+ return NULL;
}
}
- if (self->round_image)
- gtk_style_context_add_class (context, "image");
- else
- gtk_style_context_remove_class (context, "image");
+ if (!gdk_pixbuf_loader_close (loader, error))
+ return NULL;
+
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (pixbuf == NULL)
+ return NULL;
+
+ return g_object_ref (pixbuf);
+}
+
+static GdkPixbuf *
+load_icon_sync (GLoadableIcon *icon,
+ gint size)
+{
+ g_autoptr (GError) error = NULL;
+ g_autoptr (GInputStream) stream = g_loadable_icon_load (icon, size, NULL, NULL, &error);
+ g_autoptr (GdkPixbufLoader) loader = gdk_pixbuf_loader_new ();
+ g_autoptr (GdkPixbuf) pixbuf = NULL;
+
+ if (error) {
+ g_warning ("Failed to load icon: %s", error->message);
+ return NULL;
+ }
+
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (size_prepared_cb),
+ GINT_TO_POINTER (size));
+
+ pixbuf = load_from_stream (loader, stream, NULL, &error);
+
+ if (error) {
+ g_warning ("Failed to load pixbuf from GLoadableIcon: %s", error->message);
+ return NULL;
+ }
+
+ return g_steal_pointer (&pixbuf);
}
static void
@@ -291,6 +561,10 @@ hdy_avatar_get_property (GObject *object,
g_value_set_int (value, hdy_avatar_get_size (self));
break;
+ case PROP_LOADABLE_ICON:
+ g_value_set_object (value, hdy_avatar_get_loadable_icon (self));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -322,12 +596,27 @@ hdy_avatar_set_property (GObject *object,
hdy_avatar_set_size (self, g_value_get_int (value));
break;
+ case PROP_LOADABLE_ICON:
+ hdy_avatar_set_loadable_icon (self, g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
+static void
+hdy_avatar_dispose (GObject *object)
+{
+ HdyAvatar *self = HDY_AVATAR (object);
+
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->icon);
+
+ G_OBJECT_CLASS (hdy_avatar_parent_class)->dispose (object);
+}
+
static void
hdy_avatar_finalize (GObject *object)
{
@@ -335,11 +624,9 @@ hdy_avatar_finalize (GObject *object)
g_clear_pointer (&self->icon_name, g_free);
g_clear_pointer (&self->text, g_free);
- g_clear_pointer (&self->round_image, cairo_surface_destroy);
+ g_clear_object (&self->round_image);
g_clear_object (&self->layout);
-
- 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->cancellable);
G_OBJECT_CLASS (hdy_avatar_parent_class)->finalize (object);
}
@@ -347,6 +634,7 @@ hdy_avatar_finalize (GObject *object)
static void
draw_for_size (HdyAvatar *self,
cairo_t *cr,
+ GdkPixbuf *custom_image,
gint width,
gint height,
gint scale_factor)
@@ -364,12 +652,10 @@ draw_for_size (HdyAvatar *self,
set_class_contrasted (self, size);
- update_custom_image (self, width, height, scale_factor);
-
- if (self->round_image) {
- cairo_set_source_surface (cr, self->round_image, x, y);
- cairo_paint (cr);
-
+ if (custom_image) {
+ surface = gdk_cairo_surface_create_from_pixbuf (custom_image, scale_factor,
+ gtk_widget_get_window (GTK_WIDGET (self)));
+ gtk_render_icon_surface (context, cr, surface, x, y);
gtk_render_background (context, cr, x, y, size, size);
gtk_render_frame (context, cr, x, y, size, size);
return;
@@ -423,11 +709,46 @@ static gboolean
hdy_avatar_draw (GtkWidget *widget,
cairo_t *cr)
{
+ HdyAvatar *self = HDY_AVATAR (widget);
+ GdkPixbuf *custom_image = NULL;
+ GtkStyleContext *context = gtk_widget_get_style_context (widget);
gint width = gtk_widget_get_allocated_width (widget);
gint height = gtk_widget_get_allocated_height (widget);
gint scale_factor = gtk_widget_get_scale_factor (widget);
+ gint new_size = MIN (width, height) * scale_factor;
+
+ if (get_icon (self)) {
+ custom_image = update_custom_image (get_icon (self), NULL, self->round_image, new_size);
+
+ if ((!custom_image &&
+ !self->loading_error) ||
+ (self->currently_loading_size != new_size &&
+ is_scaled (custom_image))) {
+ self->currently_loading_size = new_size;
+ g_cancellable_cancel (self->cancellable);
+ g_set_object (&self->cancellable, g_cancellable_new ());
+ load_icon_async (get_icon (self),
+ new_size,
+ self->cancellable,
+ (GAsyncReadyCallback) load_from_gicon_async_for_display_cb,
+ self);
+ }
+
+ /* We don't want to draw a broken custom image, because it may be scaled
+ and we prefer to use the generated one in this case */
+ if (self->loading_error)
+ g_clear_object (&custom_image);
+ }
+
+ if (self->round_image && !custom_image)
+ gtk_style_context_remove_class (context, "image");
+
+ if (!self->round_image && custom_image)
+ gtk_style_context_add_class (context, "image");
+
+ g_set_object (&self->round_image, custom_image);
+ draw_for_size (self, cr, self->round_image, width, height, scale_factor);
- draw_for_size (HDY_AVATAR (widget), cr, width, height, scale_factor);
return FALSE;
}
@@ -523,8 +844,8 @@ hdy_avatar_class_init (HdyAvatarClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ object_class->dispose = hdy_avatar_dispose;
object_class->finalize = hdy_avatar_finalize;
-
object_class->set_property = hdy_avatar_set_property;
object_class->get_property = hdy_avatar_get_property;
@@ -592,6 +913,20 @@ hdy_avatar_class_init (HdyAvatarClass *klass)
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+ /**
+ * HdyAvatar:loadable-icon:
+ *
+ * A #GLoadableIcon used to load the avatar.
+ *
+ * Since: 1.1
+ */
+ props[PROP_LOADABLE_ICON] =
+ g_param_spec_object ("loadable-icon",
+ "Loadable Icon",
+ "The loadable icon used to load the avatar",
+ G_TYPE_LOADABLE_ICON,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
gtk_widget_class_set_css_name (widget_class, "avatar");
@@ -779,19 +1114,40 @@ hdy_avatar_set_image_load_func (HdyAvatar *self,
gpointer user_data,
GDestroyNotify destroy)
{
+ g_autoptr (HdyAvatarIcon) icon = NULL;
+
g_return_if_fail (HDY_IS_AVATAR (self));
g_return_if_fail (user_data != NULL || (user_data == NULL && destroy == NULL));
- if (self->load_image_func_target_destroy_notify != NULL)
- self->load_image_func_target_destroy_notify (self->load_image_func_target);
+ if (load_image != NULL)
+ icon = hdy_avatar_icon_new (load_image, user_data, destroy);
- self->load_image_func = load_image;
- self->load_image_func_target = user_data;
- self->load_image_func_target_destroy_notify = destroy;
+ if (self->load_func_icon && !self->icon) {
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ self->currently_loading_size = -1;
+ self->loading_error = FALSE;
+ }
- g_clear_pointer (&self->round_image, cairo_surface_destroy);
- self->round_image_size = -1;
- gtk_widget_queue_draw (GTK_WIDGET (self));
+ g_set_object (&self->load_func_icon, icon);
+
+ /* Don't update the custom avatar when we have a user set GLoadableIcon */
+ if (self->icon)
+ return;
+
+ if (self->load_func_icon) {
+ gint scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+
+ self->cancellable = g_cancellable_new ();
+ self->currently_loading_size = self->size * scale_factor;
+ load_icon_async (G_LOADABLE_ICON (self->load_func_icon),
+ self->currently_loading_size,
+ self->cancellable,
+ (GAsyncReadyCallback) load_from_gicon_async_for_display_cb,
+ self);
+ } else {
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
}
/**
@@ -852,6 +1208,9 @@ hdy_avatar_draw_to_pixbuf (HdyAvatar *self,
{
g_autoptr (cairo_surface_t) surface = NULL;
g_autoptr (cairo_t) cr = NULL;
+ g_autoptr (GdkPixbuf) custom_image = NULL;
+ g_autoptr (GdkPixbuf) pixbuf_from_icon = NULL;
+ gint scaled_size = size * scale_factor;
GtkStyleContext *context;
GtkAllocation bounds;
@@ -869,9 +1228,87 @@ hdy_avatar_draw_to_pixbuf (HdyAvatar *self,
cr = cairo_create (surface);
cairo_translate (cr, -bounds.x, -bounds.y);
- draw_for_size (self, cr, size, size, scale_factor);
+
+ if (get_icon (self)) {
+ /* Only used the cached round_image if it fits the size and it isn't scaled*/
+ if (!self->round_image ||
+ is_scaled (self->round_image) ||
+ gdk_pixbuf_get_width (self->round_image) != scaled_size) {
+ pixbuf_from_icon = load_icon_sync (get_icon (self), scaled_size);
+ custom_image = update_custom_image (get_icon (self), pixbuf_from_icon, NULL, scaled_size);
+ } else {
+ custom_image = update_custom_image (get_icon (self), NULL, self->round_image, scaled_size);
+ }
+ }
+
+ draw_for_size (self, cr, custom_image, size, size, scale_factor);
return gdk_pixbuf_get_from_surface (surface, 0, 0,
bounds.width * scale_factor,
bounds.height * scale_factor);
}
+
+/**
+ * hdy_avatar_get_loadable_icon:
+ * @self: a #HdyAvatar
+ *
+ * Gets the #GLoadableIcon set via hdy_avatar_set_loadable_icon().
+ *
+ * Returns: (nullable) (transfer none): the #GLoadableIcon
+ *
+ * Since: 1.1
+ */
+GLoadableIcon *
+hdy_avatar_get_loadable_icon (HdyAvatar *self)
+{
+ g_return_val_if_fail (HDY_IS_AVATAR (self), NULL);
+
+ return self->icon;
+}
+
+/**
+ * hdy_avatar_set_loadable_icon:
+ * @self: a #HdyAvatar
+ * @icon: (nullable): a #GLoadableIcon
+ *
+ * Sets the #GLoadableIcon to use as an avatar.
+ * The previous avatar is displayed till the new avatar is loaded,
+ * to immediately remove the custom avatar set the loadable-icon to %NULL.
+ *
+ * The #GLoadableIcon set via this function is prefered over a set #HdyAvatarImageLoadFunc.
+ *
+ * Since: 1.1
+ */
+void
+hdy_avatar_set_loadable_icon (HdyAvatar *self,
+ GLoadableIcon *icon)
+{
+ g_return_if_fail (HDY_IS_AVATAR (self));
+ g_return_if_fail (icon == NULL || G_IS_LOADABLE_ICON (icon));
+
+ if (icon == self->icon)
+ return;
+
+ if (self->icon) {
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ self->currently_loading_size = -1;
+ self->loading_error = FALSE;
+ }
+
+ g_set_object (&self->icon, icon);
+
+ if (self->icon) {
+ gint scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ self->currently_loading_size = self->size * scale_factor;
+ load_icon_async (self->icon,
+ self->currently_loading_size,
+ self->cancellable,
+ (GAsyncReadyCallback) load_from_gicon_async_for_display_cb,
+ self);
+ } else {
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_LOADABLE_ICON]);
+}
diff --git a/src/hdy-avatar.h b/src/hdy-avatar.h
index e820f9a8..4cfccfa9 100644
--- a/src/hdy-avatar.h
+++ b/src/hdy-avatar.h
@@ -70,5 +70,10 @@ HDY_AVAILABLE_IN_ALL
GdkPixbuf *hdy_avatar_draw_to_pixbuf (HdyAvatar *self,
gint size,
gint scale_factor);
+HDY_AVAILABLE_IN_1_1
+GLoadableIcon *hdy_avatar_get_loadable_icon (HdyAvatar *self);
+HDY_AVAILABLE_IN_1_1
+void hdy_avatar_set_loadable_icon (HdyAvatar *self,
+ GLoadableIcon *icon);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]