summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk/gtk.symbols2
-rw-r--r--gtk/gtkicontheme.c188
-rw-r--r--gtk/gtkicontheme.h9
-rw-r--r--tests/testicontheme.c61
4 files changed, 260 insertions, 0 deletions
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index d0e219c643..ff0ee486d5 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -1255,6 +1255,8 @@ gtk_icon_info_get_embedded_rect
gtk_icon_info_get_filename
gtk_icon_info_get_type
gtk_icon_info_load_icon
+gtk_icon_info_load_icon_async
+gtk_icon_info_load_icon_finish
gtk_icon_info_load_symbolic
gtk_icon_info_load_symbolic_for_context
gtk_icon_info_load_symbolic_for_style
diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c
index 3b79a55f3e..f2e9f6c1c0 100644
--- a/gtk/gtkicontheme.c
+++ b/gtk/gtkicontheme.c
@@ -350,6 +350,7 @@ static void do_theme_change (GtkIconTheme *icon_theme);
static void blow_themes (GtkIconTheme *icon_themes);
static gboolean rescan_themes (GtkIconTheme *icon_themes);
+static GtkIconData *icon_data_dup (GtkIconData *icon_data);
static void icon_data_free (GtkIconData *icon_data);
static void load_icon_data (IconThemeDir *dir,
const char *path,
@@ -2972,6 +2973,26 @@ icon_data_free (GtkIconData *icon_data)
g_slice_free (GtkIconData, icon_data);
}
+static GtkIconData *
+icon_data_dup (GtkIconData *icon_data)
+{
+ GtkIconData *dup = NULL;
+ if (icon_data)
+ {
+ dup = g_slice_new0 (GtkIconData);
+ *dup = *icon_data;
+ if (dup->n_attach_points > 0)
+ {
+ dup->attach_points = g_memdup (dup->attach_points,
+ sizeof (GdkPoint) * dup->n_attach_points);
+ }
+ dup->display_name = g_strdup (dup->display_name);
+ }
+
+ return dup;
+}
+
+
/*
* GtkIconInfo
*/
@@ -2992,6 +3013,45 @@ icon_info_new (void)
return g_object_new (GTK_TYPE_ICON_INFO, NULL);
}
+/* This only copies whatever is needed to load the pixbuf, so that we can do
+ * a load in a thread without affecting the original IconInfo from the thread.
+ */
+static GtkIconInfo *
+icon_info_dup (GtkIconInfo *icon_info)
+{
+ GtkIconInfo *dup;
+ GSList *l;
+
+ dup = icon_info_new ();
+
+ dup->filename = g_strdup (icon_info->filename);
+ if (icon_info->icon_file)
+ dup->icon_file = g_object_ref (icon_info->icon_file);
+ if (icon_info->loadable)
+ dup->loadable = g_object_ref (icon_info->loadable);
+
+ for (l = icon_info->emblem_infos; l != NULL; l = l->next)
+ {
+ dup->emblem_infos =
+ g_slist_append (dup->emblem_infos,
+ icon_info_dup (l->data));
+ }
+
+ if (icon_info->cache_pixbuf)
+ dup->cache_pixbuf = g_object_ref (icon_info->cache_pixbuf);
+
+ dup->data = icon_data_dup (icon_info->data);
+ dup->dir_type = icon_info->dir_type;
+ dup->dir_size = icon_info->dir_size;
+ dup->threshold = icon_info->threshold;
+ dup->desired_size = icon_info->desired_size;
+ dup->raw_coordinates = icon_info->raw_coordinates;
+ dup->forced_size = icon_info->forced_size;
+ dup->emblems_applied = icon_info->emblems_applied;
+
+ return dup;
+}
+
static GtkIconInfo *
icon_info_new_builtin (BuiltinIcon *icon)
{
@@ -3238,6 +3298,21 @@ apply_emblems (GtkIconInfo *info)
info->emblems_applied = TRUE;
}
+/* If this returns TRUE, its safe to call
+ icon_info_ensure_scale_and_pixbuf without blocking */
+static gboolean
+icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
+{
+ if (icon_info->pixbuf &&
+ (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
+ return TRUE;
+
+ if (icon_info->load_error)
+ return TRUE;
+
+ return FALSE;
+}
+
/* This function contains the complicated logic for deciding
* on the size at which to load the icon and loading it at
* that size.
@@ -3504,6 +3579,119 @@ gtk_icon_info_load_icon (GtkIconInfo *icon_info,
return icon_info->proxy_pixbuf;
}
+static void
+load_icon_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GtkIconInfo *dup = task_data;
+
+ icon_info_ensure_scale_and_pixbuf (dup, FALSE);
+ g_task_return_pointer (task, NULL, NULL);
+}
+
+/**
+ * gtk_icon_info_load_icon_async:
+ * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
+ * @cancellable: (allow-none): optional #GCancellable object,
+ * %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the
+ * request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously load, render and scale an icon previously looked up
+ * from the icon theme using gtk_icon_theme_lookup_icon().
+ *
+ * For more details, see gtk_icon_info_load_icon() which is the synchronous
+ * version of this call.
+ *
+ * Since: 3.8
+ **/
+void
+gtk_icon_info_load_icon_async (GtkIconInfo *icon_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GdkPixbuf *pixbuf;
+ GtkIconInfo *dup;
+ GError *error = NULL;
+
+ task = g_task_new (icon_info, cancellable, callback, user_data);
+
+ if (icon_info_get_pixbuf_ready (icon_info))
+ {
+ pixbuf = gtk_icon_info_load_icon (icon_info, &error);
+ if (pixbuf == NULL)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, pixbuf, g_object_unref);
+ g_object_unref (task);
+ }
+ else
+ {
+ dup = icon_info_dup (icon_info);
+ g_task_set_task_data (task, dup, g_object_unref);
+ g_task_run_in_thread (task, load_icon_thread);
+ g_object_unref (task);
+ }
+}
+
+/**
+ * gtk_icon_info_load_icon_finish:
+ * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
+ * @res: a #GAsyncResult
+ * @error: (allow-none): location to store error information on failure,
+ * or %NULL.
+ *
+ * Finishes an async icon load, see gtk_icon_info_load_icon_async().
+ *
+ * Return value: (transfer full): the rendered icon; this may be a newly
+ * created icon or a new reference to an internal icon, so you must
+ * not modify the icon. Use g_object_unref() to release your reference
+ * to the icon.
+ *
+ * Since: 3.8
+ **/
+GdkPixbuf *
+gtk_icon_info_load_icon_finish (GtkIconInfo *icon_info,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task = G_TASK (result);
+ GtkIconInfo *dup;
+
+ g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
+
+ dup = g_task_get_task_data (task);
+ if (dup == NULL || g_task_had_error (task))
+ return g_task_propagate_pointer (task, error);
+
+ /* We ran the thread and it was not cancelled */
+
+ /* Check if someone else updated the icon_info in between */
+ if (!icon_info_get_pixbuf_ready (icon_info))
+ {
+ /* If not, copy results from dup back to icon_info */
+
+ icon_info->emblems_applied = dup->emblems_applied;
+ icon_info->scale = dup->scale;
+ g_clear_object (&icon_info->pixbuf);
+ if (dup->pixbuf)
+ icon_info->pixbuf = g_object_ref (dup->pixbuf);
+ g_clear_error (&icon_info->load_error);
+ if (dup->load_error)
+ icon_info->load_error = g_error_copy (dup->load_error);
+ }
+
+ g_assert (icon_info_get_pixbuf_ready (icon_info));
+
+ /* This is now guaranteed to not block */
+ return gtk_icon_info_load_icon (icon_info, error);
+}
+
static gchar *
gdk_color_to_css (GdkColor *color)
{
diff --git a/gtk/gtkicontheme.h b/gtk/gtkicontheme.h
index cf284291cb..3888cdab7b 100644
--- a/gtk/gtkicontheme.h
+++ b/gtk/gtkicontheme.h
@@ -203,6 +203,15 @@ const gchar * gtk_icon_info_get_filename (GtkIconInfo *icon_info
GdkPixbuf * gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info);
GdkPixbuf * gtk_icon_info_load_icon (GtkIconInfo *icon_info,
GError **error);
+GDK_AVAILABLE_IN_3_8
+void gtk_icon_info_load_icon_async (GtkIconInfo *icon_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GDK_AVAILABLE_IN_3_8
+GdkPixbuf * gtk_icon_info_load_icon_finish (GtkIconInfo *icon_info,
+ GAsyncResult *res,
+ GError **error);
GdkPixbuf * gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
const GdkRGBA *fg,
const GdkRGBA *success_color,
diff --git a/tests/testicontheme.c b/tests/testicontheme.c
index ad9a459e05..bc0ee6959f 100644
--- a/tests/testicontheme.c
+++ b/tests/testicontheme.c
@@ -34,6 +34,28 @@ usage (void)
);
}
+static void
+icon_loaded_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdkPixbuf *pixbuf;
+ GError *error;
+
+ error = NULL;
+ pixbuf = gtk_icon_info_load_icon_finish (GTK_ICON_INFO (source_object),
+ res, &error);
+
+ if (pixbuf == NULL)
+ {
+ g_print ("%s\n", error->message);
+ exit (1);
+ }
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (user_data), pixbuf);
+ g_object_unref (pixbuf);
+}
+
int
main (int argc, char *argv[])
@@ -103,6 +125,45 @@ main (int argc, char *argv[])
gtk_main ();
}
+ else if (strcmp (argv[1], "display-async") == 0)
+ {
+ GtkWidget *window, *image;
+ GtkIconSize size;
+ GtkIconInfo *info;
+
+ if (argc < 4)
+ {
+ g_object_unref (icon_theme);
+ usage ();
+ return 1;
+ }
+
+ if (argc >= 5)
+ size = atoi (argv[4]);
+ else
+ size = GTK_ICON_SIZE_BUTTON;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ image = gtk_image_new ();
+ gtk_container_add (GTK_CONTAINER (window), image);
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (gtk_main_quit), window);
+ gtk_widget_show_all (window);
+
+ info = gtk_icon_theme_lookup_icon (icon_theme, argv[3], size,
+ GTK_ICON_LOOKUP_USE_BUILTIN);
+
+ if (info == NULL)
+ {
+ g_print ("Icon not found\n");
+ return 1;
+ }
+
+ gtk_icon_info_load_icon_async (info,
+ NULL, icon_loaded_cb, image);
+
+ gtk_main ();
+ }
else if (strcmp (argv[1], "list") == 0)
{
if (argc >= 4)