mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-10 10:50:10 +00:00
Add gtk_icon_info_load_icon_async
This lets you asynchronously load icons. We need this for gnome-shell to replace its current non-threadsafe use of GtkIconTheme. https://bugzilla.gnome.org/show_bug.cgi?id=693802
This commit is contained in:
parent
e2d0846386
commit
7690846c3f
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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[])
|
||||
@ -101,6 +123,45 @@ main (int argc, char *argv[])
|
||||
G_CALLBACK (gtk_main_quit), window);
|
||||
gtk_widget_show_all (window);
|
||||
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user