cairo-texture: Add the :auto-resize property

Keeping the backing Cairo surface of a CairoTexture canvas in sync with
the actor's allocation is tedious and prone to mistakes. We can
definitely do better by simply exposing a property that does the surface
resize and invalidation automagically on ::allocate.
This commit is contained in:
Emmanuele Bassi 2011-07-26 14:53:26 +01:00
parent 301551aacf
commit 7f8838d7cc
4 changed files with 129 additions and 5 deletions

View File

@ -67,7 +67,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include <string.h> #include <math.h>
#include <cairo-gobject.h> #include <cairo-gobject.h>
@ -88,6 +88,7 @@ enum
PROP_SURFACE_WIDTH, PROP_SURFACE_WIDTH,
PROP_SURFACE_HEIGHT, PROP_SURFACE_HEIGHT,
PROP_AUTO_RESIZE,
PROP_LAST PROP_LAST
}; };
@ -125,6 +126,8 @@ struct _ClutterCairoTexturePrivate
guint surface_height; guint surface_height;
cairo_t *cr_context; cairo_t *cr_context;
guint auto_resize : 1;
}; };
typedef struct { typedef struct {
@ -173,6 +176,9 @@ clutter_cairo_texture_set_property (GObject *object,
switch (prop_id) switch (prop_id)
{ {
case PROP_SURFACE_WIDTH: case PROP_SURFACE_WIDTH:
/* we perform the resize on notify to coalesce separate
* surface-width/surface-height property set
*/
priv->surface_width = g_value_get_uint (value); priv->surface_width = g_value_get_uint (value);
break; break;
@ -180,6 +186,11 @@ clutter_cairo_texture_set_property (GObject *object,
priv->surface_height = g_value_get_uint (value); priv->surface_height = g_value_get_uint (value);
break; break;
case PROP_AUTO_RESIZE:
clutter_cairo_texture_set_auto_resize (CLUTTER_CAIRO_TEXTURE (object),
g_value_get_boolean (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -206,6 +217,10 @@ clutter_cairo_texture_get_property (GObject *object,
g_value_set_uint (value, priv->surface_height); g_value_set_uint (value, priv->surface_height);
break; break;
case PROP_AUTO_RESIZE:
g_value_set_boolean (value, priv->auto_resize);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -431,6 +446,32 @@ clutter_cairo_texture_get_preferred_height (ClutterActor *actor,
*natural_height = (gfloat) priv->surface_height; *natural_height = (gfloat) priv->surface_height;
} }
static void
clutter_cairo_texture_allocate (ClutterActor *self,
const ClutterActorBox *allocation,
ClutterAllocationFlags flags)
{
ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (self)->priv;
ClutterActorClass *parent_class;
parent_class = CLUTTER_ACTOR_CLASS (clutter_cairo_texture_parent_class);
parent_class->allocate (self, allocation, flags);
if (priv->auto_resize)
{
ClutterCairoTexture *texture = CLUTTER_CAIRO_TEXTURE (self);
gfloat width, height;
clutter_actor_box_get_size (allocation, &width, &height);
priv->surface_width = ceilf (width + 0.5);
priv->surface_height = ceilf (height + 0.5);
clutter_cairo_texture_surface_resize_internal (texture);
clutter_cairo_texture_invalidate (texture);
}
}
static gboolean static gboolean
clutter_cairo_texture_get_paint_volume (ClutterActor *self, clutter_cairo_texture_get_paint_volume (ClutterActor *self,
ClutterPaintVolume *volume) ClutterPaintVolume *volume)
@ -521,6 +562,8 @@ clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
clutter_cairo_texture_get_preferred_width; clutter_cairo_texture_get_preferred_width;
actor_class->get_preferred_height = actor_class->get_preferred_height =
clutter_cairo_texture_get_preferred_height; clutter_cairo_texture_get_preferred_height;
actor_class->allocate =
clutter_cairo_texture_allocate;
klass->create_surface = clutter_cairo_texture_create_surface; klass->create_surface = clutter_cairo_texture_create_surface;
@ -557,9 +600,24 @@ clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
0, 0,
CLUTTER_PARAM_READWRITE); CLUTTER_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, /**
PROP_LAST, * ClutterCairoTexture:auto-resize:
obj_props); *
* Controls whether the #ClutterCairoTexture should automatically
* resize the Cairo surface whenever the actor's allocation changes.
* If :auto-resize is set to %TRUE the surface contents will also
* be invalidated automatically.
*
* Since: 1.8
*/
obj_props[PROP_AUTO_RESIZE] =
g_param_spec_boolean ("auto-resize",
P_("Auto Resize"),
P_("Whether the surface should match the allocation"),
FALSE,
CLUTTER_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
/** /**
* ClutterCairoTexture::create-surface: * ClutterCairoTexture::create-surface:
@ -1058,3 +1116,57 @@ clutter_cairo_texture_clear (ClutterCairoTexture *self)
if (priv->cr_context == NULL) if (priv->cr_context == NULL)
cairo_destroy (cr); cairo_destroy (cr);
} }
/**
* clutter_cairo_texture_set_auto_resize:
* @self: a #ClutterCairoTexture
* @value: %TRUE if the #ClutterCairoTexture should bind the surface
* size to the allocation
*
* Sets whether the #ClutterCairoTexture should ensure that the
* backing Cairo surface used matches the allocation assigned to
* the actor. If the allocation changes, the contents of the
* #ClutterCairoTexture will also be invalidated automatically.
*
* Since: 1.8
*/
void
clutter_cairo_texture_set_auto_resize (ClutterCairoTexture *self,
gboolean value)
{
ClutterCairoTexturePrivate *priv;
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
value = !!value;
priv = self->priv;
if (priv->auto_resize == value)
return;
priv->auto_resize = value;
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_AUTO_RESIZE]);
}
/**
* clutter_cairo_texture_get_auto_resize:
* @self: a #ClutterCairoTexture
*
* Retrieves the value set using clutter_cairo_texture_set_auto_resize().
*
* Return value: %TRUE if the #ClutterCairoTexture should track the
* allocation, and %FALSE otherwise
*
* Since: 1.8
*/
gboolean
clutter_cairo_texture_get_auto_resize (ClutterCairoTexture *self)
{
g_return_val_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self), FALSE);
return self->priv->auto_resize;
}

View File

@ -131,6 +131,9 @@ void clutter_cairo_texture_set_surface_size (ClutterCairoTex
void clutter_cairo_texture_get_surface_size (ClutterCairoTexture *self, void clutter_cairo_texture_get_surface_size (ClutterCairoTexture *self,
guint *width, guint *width,
guint *height); guint *height);
void clutter_cairo_texture_set_auto_resize (ClutterCairoTexture *self,
gboolean value);
gboolean clutter_cairo_texture_get_auto_resize (ClutterCairoTexture *self);
void clutter_cairo_texture_clear (ClutterCairoTexture *self); void clutter_cairo_texture_clear (ClutterCairoTexture *self);

View File

@ -1756,6 +1756,8 @@ ClutterCairoTextureClass
clutter_cairo_texture_new clutter_cairo_texture_new
clutter_cairo_texture_set_surface_size clutter_cairo_texture_set_surface_size
clutter_cairo_texture_get_surface_size clutter_cairo_texture_get_surface_size
clutter_cairo_texture_set_auto_resize
clutter_cairo_texture_get_auto_resize
<SUBSECTION> <SUBSECTION>
clutter_cairo_texture_create clutter_cairo_texture_create

View File

@ -78,10 +78,11 @@ test_cairo_clock_main (int argc, char *argv[])
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return EXIT_FAILURE; return EXIT_FAILURE;
/* create a fixed size stage */ /* create a resizable stage */
stage = clutter_stage_new (); stage = clutter_stage_new ();
clutter_stage_set_title (CLUTTER_STAGE (stage), "2D Clock"); clutter_stage_set_title (CLUTTER_STAGE (stage), "2D Clock");
clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_LightSkyBlue); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_LightSkyBlue);
clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
clutter_actor_set_size (stage, 300, 300); clutter_actor_set_size (stage, 300, 300);
clutter_actor_show (stage); clutter_actor_show (stage);
@ -89,6 +90,12 @@ test_cairo_clock_main (int argc, char *argv[])
canvas = clutter_cairo_texture_new (300, 300); canvas = clutter_cairo_texture_new (300, 300);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), canvas); clutter_container_add_actor (CLUTTER_CONTAINER (stage), canvas);
/* bind the size of the canvas to that of the stage */
clutter_actor_add_constraint (canvas, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, 0));
/* make sure to match allocation to canvas size */
clutter_cairo_texture_set_auto_resize (CLUTTER_CAIRO_TEXTURE (canvas), TRUE);
/* quit on destroy */ /* quit on destroy */
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);