diff options
-rw-r--r-- | gtk/gtk.symbols | 2 | ||||
-rw-r--r-- | gtk/gtkicontheme.c | 188 | ||||
-rw-r--r-- | gtk/gtkicontheme.h | 9 | ||||
-rw-r--r-- | tests/testicontheme.c | 61 |
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) |