image: Implement support for paintables

This includes adding support to GtkImageDefintion and GtkIconHelper.

Only GtkImage handles support for signals from the paintable.
This commit is contained in:
Benjamin Otte 2018-02-16 10:50:49 +01:00
parent c59948169d
commit 9fa1e68151
8 changed files with 270 additions and 13 deletions

View File

@ -297,6 +297,9 @@ notify_storage_type (GtkCellRendererPixbuf *cellpixbuf,
case GTK_IMAGE_TEXTURE:
g_object_notify (G_OBJECT (cellpixbuf), "texture");
break;
case GTK_IMAGE_PAINTABLE:
g_object_notify (G_OBJECT (cellpixbuf), "paintable");
break;
case GTK_IMAGE_ICON_NAME:
g_object_notify (G_OBJECT (cellpixbuf), "icon-name");
break;

View File

@ -6556,6 +6556,7 @@ gtk_entry_clear_icon (GtkEntry *entry,
break;
case GTK_IMAGE_SURFACE:
case GTK_IMAGE_PAINTABLE:
case GTK_IMAGE_EMPTY:
default:
g_assert_not_reached ();

View File

@ -268,6 +268,16 @@ ensure_paintable_from_texture (GtkIconHelper *self,
return g_object_ref (GDK_PAINTABLE (texture));
}
static GdkPaintable *
ensure_paintable_from_paintable (GtkIconHelper *self,
GdkPaintable *paintable,
int *scale)
{
*scale = 1;
return g_object_ref (paintable);
}
static GdkPaintable *
ensure_paintable_for_gicon (GtkIconHelper *self,
GtkCssStyle *style,
@ -326,6 +336,11 @@ gtk_icon_helper_load_paintable (GtkIconHelper *self,
symbolic = FALSE;
break;
case GTK_IMAGE_PAINTABLE:
paintable = ensure_paintable_from_paintable (self, gtk_image_definition_get_paintable (self->def), &scale);
symbolic = FALSE;
break;
case GTK_IMAGE_ICON_NAME:
scale = gtk_widget_get_scale_factor (self->owner);
if (self->use_fallback)
@ -379,6 +394,43 @@ gtk_icon_helper_ensure_paintable (GtkIconHelper *self)
self->texture_is_symbolic = symbolic;
}
static void
get_size_for_paintable (GtkIconHelper *self,
GdkPaintable *paintable,
int *width_out,
int *height_out)
{
int width = gdk_paintable_get_intrinsic_width (paintable);
int height = gdk_paintable_get_intrinsic_height (paintable);
if (width == 0)
{
if (height != 0)
{
double ar = gdk_paintable_get_intrinsic_aspect_ratio (paintable);
if (ar > 0)
width = ceil (height * ar);
}
}
else
{
if (height == 0)
{
double ar = gdk_paintable_get_intrinsic_aspect_ratio (paintable);
if (ar > 0)
height = ceil (width / ar);
}
}
if (width == 0 || height == 0)
ensure_icon_size (self, &width, &height);
*width_out = width;
*height_out = height;
}
void
_gtk_icon_helper_get_size (GtkIconHelper *self,
gint *width_out,
@ -413,6 +465,14 @@ _gtk_icon_helper_get_size (GtkIconHelper *self,
}
break;
case GTK_IMAGE_PAINTABLE:
{
GdkPaintable *paintable = gtk_image_definition_get_paintable (self->def);
get_size_for_paintable (self, paintable, &width, &height);
}
break;
case GTK_IMAGE_EMPTY:
default:
break;
@ -425,17 +485,7 @@ _gtk_icon_helper_get_size (GtkIconHelper *self,
if (self->paintable != NULL)
{
width = gdk_paintable_get_intrinsic_width (self->paintable);
height = gdk_paintable_get_intrinsic_height (self->paintable);
if (width == 0 || height == 0)
{
ensure_icon_size (self, &width, &height);
}
else
{
width = (width + self->texture_scale - 1) / self->texture_scale;
height = (height + self->texture_scale - 1) / self->texture_scale;
}
get_size_for_paintable (self, self->paintable, &width, &height);
}
else
{
@ -487,6 +537,13 @@ _gtk_icon_helper_set_texture (GtkIconHelper *self,
gtk_icon_helper_take_definition (self, gtk_image_definition_new_texture (texture));
}
void
_gtk_icon_helper_set_paintable (GtkIconHelper *self,
GdkPaintable *paintable)
{
gtk_icon_helper_take_definition (self, gtk_image_definition_new_paintable (paintable));
}
gboolean
_gtk_icon_helper_set_pixel_size (GtkIconHelper *self,
gint pixel_size)
@ -555,6 +612,12 @@ _gtk_icon_helper_peek_texture (GtkIconHelper *self)
return gtk_image_definition_get_texture (self->def);
}
GdkPaintable *
_gtk_icon_helper_peek_paintable (GtkIconHelper *self)
{
return gtk_image_definition_get_paintable (self->def);
}
const gchar *
_gtk_icon_helper_get_icon_name (GtkIconHelper *self)
{

View File

@ -69,6 +69,8 @@ void _gtk_icon_helper_set_surface (GtkIconHelper *self,
cairo_surface_t *surface);
void _gtk_icon_helper_set_texture (GtkIconHelper *self,
GdkTexture *texture);
void _gtk_icon_helper_set_paintable (GtkIconHelper *self,
GdkPaintable *paintable);
gboolean _gtk_icon_helper_set_pixel_size (GtkIconHelper *self,
gint pixel_size);
@ -82,6 +84,7 @@ gboolean _gtk_icon_helper_get_use_fallback (GtkIconHelper *self);
GIcon *_gtk_icon_helper_peek_gicon (GtkIconHelper *self);
cairo_surface_t *_gtk_icon_helper_peek_surface (GtkIconHelper *self);
GdkTexture *_gtk_icon_helper_peek_texture (GtkIconHelper *self);
GdkPaintable *_gtk_icon_helper_peek_paintable (GtkIconHelper *self);
GtkImageDefinition *gtk_icon_helper_get_definition (GtkIconHelper *self);
const gchar *_gtk_icon_helper_get_icon_name (GtkIconHelper *self);

View File

@ -119,6 +119,7 @@ enum
{
PROP_0,
PROP_SURFACE,
PROP_PAINTABLE,
PROP_TEXTURE,
PROP_FILE,
PROP_ICON_SIZE,
@ -161,6 +162,13 @@ gtk_image_class_init (GtkImageClass *class)
CAIRO_GOBJECT_TYPE_SURFACE,
GTK_PARAM_READWRITE);
image_props[PROP_PAINTABLE] =
g_param_spec_object ("paintable",
P_("Paintable"),
P_("A GdkPaintable to display"),
GDK_TYPE_PAINTABLE,
GTK_PARAM_READWRITE);
image_props[PROP_TEXTURE] =
g_param_spec_object ("texture",
P_("Texture"),
@ -284,6 +292,8 @@ gtk_image_finalize (GObject *object)
GtkImage *image = GTK_IMAGE (object);
GtkImagePrivate *priv = gtk_image_get_instance_private (image);
gtk_image_clear (image);
gtk_icon_helper_destroy (&priv->icon_helper);
g_free (priv->filename);
@ -306,6 +316,9 @@ gtk_image_set_property (GObject *object,
case PROP_SURFACE:
gtk_image_set_from_surface (image, g_value_get_boxed (value));
break;
case PROP_PAINTABLE:
gtk_image_set_from_paintable (image, g_value_get_object (value));
break;
case PROP_TEXTURE:
gtk_image_set_from_texture (image, g_value_get_object (value));
break;
@ -353,6 +366,9 @@ gtk_image_get_property (GObject *object,
case PROP_SURFACE:
g_value_set_boxed (value, _gtk_icon_helper_peek_surface (&priv->icon_helper));
break;
case PROP_PAINTABLE:
g_value_set_object (value, _gtk_icon_helper_peek_paintable (&priv->icon_helper));
break;
case PROP_TEXTURE:
g_value_set_object (value, _gtk_icon_helper_peek_texture (&priv->icon_helper));
break;
@ -479,6 +495,32 @@ gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf)
return GTK_WIDGET (image);
}
/**
* gtk_image_new_from_paintable:
* @paintable: (allow-none): a #GdkPaintable, or %NULL
*
* Creates a new #GtkImage displaying @paintable.
* The #GtkImage does not assume a reference to the
* paintable; you still need to unref it if you own references.
* #GtkImage will add its own reference rather than adopting yours.
*
* The #GtkImage will track changes to the @paintable and update
* its size and contents in response to it.
*
* Returns: a new #GtkImage
**/
GtkWidget*
gtk_image_new_from_paintable (GdkPaintable *paintable)
{
GtkImage *image;
image = g_object_new (GTK_TYPE_IMAGE, NULL);
gtk_image_set_from_paintable (image, paintable);
return GTK_WIDGET (image);
}
/**
* gtk_image_new_from_texture:
* @texture: (allow-none): a #GdkTexture, or %NULL
@ -945,6 +987,64 @@ gtk_image_set_from_surface (GtkImage *image,
g_object_thaw_notify (G_OBJECT (image));
}
static void
gtk_image_paintable_invalidate_contents (GdkPaintable *paintable,
GtkImage *image)
{
gtk_widget_queue_draw (GTK_WIDGET (image));
}
static void
gtk_image_paintable_invalidate_size (GdkPaintable *paintable,
GtkImage *image)
{
GtkImagePrivate *priv = gtk_image_get_instance_private (image);
gtk_icon_helper_invalidate (&priv->icon_helper);
}
/**
* gtk_image_set_from_paintable:
* @image: a #GtkImage
* @paintable: (nullable): a #GdkPaintable or %NULL
*
* See gtk_image_new_from_paintable() for details.
**/
void
gtk_image_set_from_paintable (GtkImage *image,
GdkPaintable *paintable)
{
GtkImagePrivate *priv = gtk_image_get_instance_private (image);
g_return_if_fail (GTK_IS_IMAGE (image));
g_return_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable));
g_object_freeze_notify (G_OBJECT (image));
if (paintable)
g_object_ref (paintable);
gtk_image_clear (image);
if (paintable)
{
_gtk_icon_helper_set_paintable (&priv->icon_helper, paintable);
g_signal_connect (paintable,
"invalidate-contents",
G_CALLBACK (gtk_image_paintable_invalidate_contents),
image);
g_signal_connect (paintable,
"invalidate-size",
G_CALLBACK (gtk_image_paintable_invalidate_size),
image);
g_object_unref (paintable);
}
g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_PAINTABLE]);
g_object_thaw_notify (G_OBJECT (image));
}
/**
* gtk_image_set_from_texture:
* @image: a #GtkImage
@ -1022,6 +1122,29 @@ gtk_image_get_surface (GtkImage *image)
return _gtk_icon_helper_peek_surface (&priv->icon_helper);
}
/**
* gtk_image_get_paintable:
* @image: a #GtkImage
*
* Gets the image #GdkPaintable being displayed by the #GtkImage.
* The storage type of the image must be %GTK_IMAGE_EMPTY or
* %GTK_IMAGE_PAINTABLE (see gtk_image_get_storage_type()).
* The caller of this function does not own a reference to the
* returned paintable.
*
* Returns: (nullable) (transfer none): the displayed paintable, or %NULL if
* the image is empty
**/
GdkPaintable *
gtk_image_get_paintable (GtkImage *image)
{
GtkImagePrivate *priv = gtk_image_get_instance_private (image);
g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
return _gtk_icon_helper_peek_paintable (&priv->icon_helper);
}
/**
* gtk_image_get_texture:
* @image: a #GtkImage
@ -1205,6 +1328,9 @@ gtk_image_notify_for_storage_type (GtkImage *image,
case GTK_IMAGE_TEXTURE:
g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_TEXTURE]);
break;
case GTK_IMAGE_PAINTABLE:
g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_PAINTABLE]);
break;
case GTK_IMAGE_EMPTY:
default:
break;
@ -1277,6 +1403,17 @@ gtk_image_clear (GtkImage *image)
g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_RESOURCE]);
}
if (storage_type == GTK_IMAGE_PAINTABLE)
{
GdkPaintable *paintable = _gtk_icon_helper_peek_paintable (&priv->icon_helper);
g_signal_handlers_disconnect_by_func (paintable,
gtk_image_paintable_invalidate_contents,
image);
g_signal_handlers_disconnect_by_func (paintable,
gtk_image_paintable_invalidate_size,
image);
}
_gtk_icon_helper_clear (&priv->icon_helper);
g_object_thaw_notify (G_OBJECT (image));

View File

@ -59,6 +59,8 @@ typedef struct _GtkImageClass GtkImageClass;
* This image type was added in GTK+ 3.10
* @GTK_IMAGE_TEXTURE: the widget contains a #GdkTexture.
* This image type was added in GTK+ 3.94
* @GTK_IMAGE_PAINTABLE: the widget contains a #GdkPaintable.
* This image type was added in GTK+ 3.96
*
* Describes the image data representation used by a #GtkImage. If you
* want to get the image from the widget, you can only get the
@ -74,7 +76,8 @@ typedef enum
GTK_IMAGE_ICON_NAME,
GTK_IMAGE_GICON,
GTK_IMAGE_SURFACE,
GTK_IMAGE_TEXTURE
GTK_IMAGE_TEXTURE,
GTK_IMAGE_PAINTABLE
} GtkImageType;
/**
@ -113,6 +116,8 @@ GtkWidget* gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf);
GDK_AVAILABLE_IN_ALL
GtkWidget* gtk_image_new_from_texture (GdkTexture *texture);
GDK_AVAILABLE_IN_ALL
GtkWidget* gtk_image_new_from_paintable (GdkPaintable *paintable);
GDK_AVAILABLE_IN_ALL
GtkWidget* gtk_image_new_from_icon_name (const gchar *icon_name);
GDK_AVAILABLE_IN_ALL
GtkWidget* gtk_image_new_from_gicon (GIcon *icon);
@ -134,6 +139,9 @@ GDK_AVAILABLE_IN_ALL
void gtk_image_set_from_texture (GtkImage *image,
GdkTexture *texture);
GDK_AVAILABLE_IN_ALL
void gtk_image_set_from_paintable (GtkImage *image,
GdkPaintable *paintable);
GDK_AVAILABLE_IN_ALL
void gtk_image_set_from_icon_name (GtkImage *image,
const gchar *icon_name);
GDK_AVAILABLE_IN_ALL
@ -156,6 +164,8 @@ GDK_AVAILABLE_IN_ALL
cairo_surface_t *gtk_image_get_surface (GtkImage *image);
GDK_AVAILABLE_IN_ALL
GdkTexture *gtk_image_get_texture (GtkImage *image);
GDK_AVAILABLE_IN_ALL
GdkPaintable *gtk_image_get_paintable (GtkImage *image);
GDK_AVAILABLE_IN_ALL
const char *gtk_image_get_icon_name (GtkImage *image);

View File

@ -24,6 +24,7 @@ typedef struct _GtkImageDefinitionIconName GtkImageDefinitionIconName;
typedef struct _GtkImageDefinitionGIcon GtkImageDefinitionGIcon;
typedef struct _GtkImageDefinitionSurface GtkImageDefinitionSurface;
typedef struct _GtkImageDefinitionTexture GtkImageDefinitionTexture;
typedef struct _GtkImageDefinitionPaintable GtkImageDefinitionPaintable;
struct _GtkImageDefinitionEmpty {
GtkImageType type;
@ -58,6 +59,13 @@ struct _GtkImageDefinitionTexture {
GdkTexture *texture;
};
struct _GtkImageDefinitionPaintable {
GtkImageType type;
gint ref_count;
GdkPaintable *paintable;
};
union _GtkImageDefinition
{
GtkImageType type;
@ -66,6 +74,7 @@ union _GtkImageDefinition
GtkImageDefinitionGIcon gicon;
GtkImageDefinitionSurface surface;
GtkImageDefinitionTexture texture;
GtkImageDefinitionPaintable paintable;
};
GtkImageDefinition *
@ -84,7 +93,8 @@ gtk_image_definition_alloc (GtkImageType type)
sizeof (GtkImageDefinitionIconName),
sizeof (GtkImageDefinitionGIcon),
sizeof (GtkImageDefinitionSurface),
sizeof (GtkImageDefinitionTexture)
sizeof (GtkImageDefinitionTexture),
sizeof (GtkImageDefinitionPaintable)
};
GtkImageDefinition *def;
@ -153,6 +163,20 @@ gtk_image_definition_new_texture (GdkTexture *texture)
return def;
}
GtkImageDefinition *
gtk_image_definition_new_paintable (GdkPaintable *paintable)
{
GtkImageDefinition *def;
if (paintable == NULL)
return NULL;
def = gtk_image_definition_alloc (GTK_IMAGE_PAINTABLE);
def->paintable.paintable = g_object_ref (paintable);
return def;
}
GtkImageDefinition *
gtk_image_definition_ref (GtkImageDefinition *def)
{
@ -181,6 +205,9 @@ gtk_image_definition_unref (GtkImageDefinition *def)
case GTK_IMAGE_TEXTURE:
g_object_unref (def->texture.texture);
break;
case GTK_IMAGE_PAINTABLE:
g_object_unref (def->paintable.paintable);
break;
case GTK_IMAGE_ICON_NAME:
g_free (def->icon_name.icon_name);
break;
@ -208,6 +235,7 @@ gtk_image_definition_get_scale (const GtkImageDefinition *def)
case GTK_IMAGE_EMPTY:
case GTK_IMAGE_SURFACE:
case GTK_IMAGE_TEXTURE:
case GTK_IMAGE_PAINTABLE:
case GTK_IMAGE_ICON_NAME:
case GTK_IMAGE_GICON:
return 1;
@ -249,3 +277,13 @@ gtk_image_definition_get_texture (const GtkImageDefinition *def)
return def->texture.texture;
}
GdkPaintable *
gtk_image_definition_get_paintable (const GtkImageDefinition *def)
{
if (def->type != GTK_IMAGE_PAINTABLE)
return NULL;
return def->paintable.paintable;
}

View File

@ -30,6 +30,7 @@ GtkImageDefinition * gtk_image_definition_new_icon_name (const char
GtkImageDefinition * gtk_image_definition_new_gicon (GIcon *gicon);
GtkImageDefinition * gtk_image_definition_new_surface (cairo_surface_t *surface);
GtkImageDefinition * gtk_image_definition_new_texture (GdkTexture *texture);
GtkImageDefinition * gtk_image_definition_new_paintable (GdkPaintable *paintable);
GtkImageDefinition * gtk_image_definition_ref (GtkImageDefinition *def);
void gtk_image_definition_unref (GtkImageDefinition *def);
@ -40,6 +41,7 @@ const gchar * gtk_image_definition_get_icon_name (const GtkImageD
GIcon * gtk_image_definition_get_gicon (const GtkImageDefinition *def);
cairo_surface_t * gtk_image_definition_get_surface (const GtkImageDefinition *def);
GdkTexture * gtk_image_definition_get_texture (const GtkImageDefinition *def);
GdkPaintable * gtk_image_definition_get_paintable (const GtkImageDefinition *def);
G_END_DECLS