diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 6c22c42ad..9df651b85 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -74,6 +74,7 @@ source_h = \ $(srcdir)/clutter-colorize-effect.h \ $(srcdir)/clutter-constraint.h \ $(srcdir)/clutter-container.h \ + $(srcdir)/clutter-content.h \ $(srcdir)/clutter-deform-effect.h \ $(srcdir)/clutter-deprecated.h \ $(srcdir)/clutter-desaturate-effect.h \ @@ -151,6 +152,7 @@ source_c = \ $(srcdir)/clutter-colorize-effect.c \ $(srcdir)/clutter-constraint.c \ $(srcdir)/clutter-container.c \ + $(srcdir)/clutter-content.c \ $(srcdir)/clutter-deform-effect.c \ $(srcdir)/clutter-desaturate-effect.c \ $(srcdir)/clutter-device-manager.c \ @@ -209,6 +211,7 @@ source_h_priv = \ $(srcdir)/clutter-actor-private.h \ $(srcdir)/clutter-backend-private.h \ $(srcdir)/clutter-bezier.h \ + $(srcdir)/clutter-content-private.h \ $(srcdir)/clutter-debug.h \ $(srcdir)/clutter-device-manager-private.h \ $(srcdir)/clutter-easing.h \ diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 6336dc4c7..8f9da94de 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -380,6 +380,7 @@ #include "clutter-color.h" #include "clutter-constraint.h" #include "clutter-container.h" +#include "clutter-content-private.h" #include "clutter-debug.h" #include "clutter-effect-private.h" #include "clutter-enum-types.h" @@ -506,6 +507,11 @@ struct _ClutterActorPrivate /* delegate object used to allocate the children of this actor */ ClutterLayoutManager *layout_manager; + /* delegate object used to paint the contents of this actor */ + ClutterContent *content; + + ClutterContentGravity content_gravity; + /* used when painting, to update the paint volume */ ClutterEffect *current_effect; @@ -668,6 +674,10 @@ enum PROP_FIRST_CHILD, PROP_LAST_CHILD, + PROP_CONTENT, + PROP_CONTENT_GRAVITY, + PROP_CONTENT_BOX, + PROP_LAST }; @@ -2045,6 +2055,10 @@ clutter_actor_set_allocation_internal (ClutterActor *self, g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]); + /* if the allocation changes, so does the content box */ + if (priv->content != NULL) + g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]); + retval = TRUE; } else @@ -3109,6 +3123,9 @@ clutter_actor_paint_node (ClutterActor *actor, clutter_paint_node_unref (node); } + if (priv->content != NULL) + _clutter_content_paint_content (priv->content, actor, root); + if (CLUTTER_ACTOR_GET_CLASS (actor)->paint_node != NULL) CLUTTER_ACTOR_GET_CLASS (actor)->paint_node (actor, root); @@ -4353,6 +4370,14 @@ clutter_actor_set_property (GObject *object, clutter_actor_set_background_color (actor, g_value_get_boxed (value)); break; + case PROP_CONTENT: + clutter_actor_set_content (actor, g_value_get_object (value)); + break; + + case PROP_CONTENT_GRAVITY: + clutter_actor_set_content_gravity (actor, g_value_get_enum (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -4751,6 +4776,23 @@ clutter_actor_get_property (GObject *object, g_value_set_object (value, priv->last_child); break; + case PROP_CONTENT: + g_value_set_object (value, priv->content); + break; + + case PROP_CONTENT_GRAVITY: + g_value_set_enum (value, priv->content_gravity); + break; + + case PROP_CONTENT_BOX: + { + ClutterActorBox box = { 0, }; + + clutter_actor_get_content_box (actor, &box); + g_value_set_boxed (value, &box); + } + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -4807,8 +4849,13 @@ clutter_actor_dispose (GObject *object) if (priv->layout_manager != NULL) { clutter_layout_manager_set_container (priv->layout_manager, NULL); - g_object_unref (priv->layout_manager); - priv->layout_manager = NULL; + g_clear_object (&priv->layout_manager); + } + + if (priv->content != NULL) + { + _clutter_content_detached (priv->content, self); + g_clear_object (&priv->content); } G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); @@ -6152,6 +6199,66 @@ clutter_actor_class_init (ClutterActorClass *klass) CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READABLE); + /** + * ClutterActor:content: + * + * The #ClutterContent implementation that controls the content + * of the actor. + * + * Since: 1.10 + */ + obj_props[PROP_CONTENT] = + g_param_spec_object ("content", + P_("Content"), + P_("Delegate object for painting the actor's content"), + CLUTTER_TYPE_CONTENT, + CLUTTER_PARAM_READWRITE); + + /** + * ClutterActor:content-gravity: + * + * The alignment that should be honoured by the #ClutterContent + * set with the #ClutterActor:content property. + * + * Changing the value of this property will change the bounding box of + * the content; you can use the #ClutterActor:content-box property to + * get the position and size of the content within the actor's + * allocation. + * + * This property is meaningful only for #ClutterContent implementations + * that have a preferred size, and if the preferred size is smaller than + * the actor's allocation. + * + * Since: 1.10 + */ + obj_props[PROP_CONTENT_GRAVITY] = + g_param_spec_enum ("content-gravity", + P_("Content Gravity"), + P_("Alignment of the actor's content"), + CLUTTER_TYPE_CONTENT_GRAVITY, + CLUTTER_CONTENT_GRAVITY_RESIZE_FILL, + CLUTTER_PARAM_READWRITE); + + /** + * ClutterActor:content-box: + * + * The bounding box for the #ClutterContent used by the actor. + * + * The value of this property is controlled by the #ClutterActor:allocation + * and #ClutterActor:content-gravity properties of #ClutterActor. + * + * The bounding box for the content is guaranteed to never exceed the + * allocation's of the actor. + * + * Since: 1.10 + */ + obj_props[PROP_CONTENT_BOX] = + g_param_spec_boxed ("content-box", + P_("Content Box"), + P_("The bounding box of the actor's content"), + CLUTTER_TYPE_ACTOR_BOX, + CLUTTER_PARAM_READABLE); + g_object_class_install_properties (object_class, PROP_LAST, obj_props); /** @@ -6731,6 +6838,12 @@ clutter_actor_init (ClutterActor *self) priv->last_paint_volume_valid = TRUE; priv->transform_valid = FALSE; + + /* the default is to stretch the content, to match the + * current behaviour of basically all actors. also, it's + * the easiest thing to compute. + */ + priv->content_gravity = CLUTTER_CONTENT_GRAVITY_RESIZE_FILL; } /** @@ -17236,3 +17349,303 @@ clutter_actor_restore_easing_state (ClutterActor *self) g_array_remove_index (info->states, info->states->len - 1); info->cur_state = &g_array_index (info->states, AState, info->states->len - 1); } + +/** + * clutter_actor_set_content: + * @self: a #ClutterActor + * @content: (allow-none): a #ClutterContent, or %NULL + * + * Sets the contents of a #ClutterActor. + * + * Since: 1.10 + */ +void +clutter_actor_set_content (ClutterActor *self, + ClutterContent *content) +{ + ClutterActorPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + g_return_if_fail (content == NULL || CLUTTER_IS_CONTENT (content)); + + priv = self->priv; + + if (priv->content != NULL) + { + _clutter_content_detached (priv->content, self); + g_object_unref (priv->content); + } + + priv->content = content; + + if (priv->content != NULL) + { + g_object_ref (priv->content); + _clutter_content_attached (priv->content, self); + } + + /* given that the content is always painted within the allocation, + * we only need to queue a redraw here + */ + clutter_actor_queue_redraw (self); + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT]); + + /* if the content gravity is not resize-fill, and the new content has a + * different preferred size than the previous one, then the content box + * may have been changed. since we compute that lazily, we just notify + * here, and let whomever watches :content-box do whatever they need to + * do. + */ + if (priv->content_gravity != CLUTTER_CONTENT_GRAVITY_RESIZE_FILL) + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]); +} + +/** + * clutter_actor_get_content: + * @self: a #ClutterActor + * + * Retrieves the contents of @self. + * + * Return value: (transfer none): a pointer to the #ClutterContent instance, + * or %NULL if none was set + * + * Since: 1.10 + */ +ClutterContent * +clutter_actor_get_content (ClutterActor *self) +{ + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); + + return self->priv->content; +} + +/** + * clutter_actor_set_content_gravity: + * @self: a #ClutterActor + * @gravity: the #ClutterContentGravity + * + * Sets the gravity of the #ClutterContent used by @self. + * + * See the description of the #ClutterActor:content-gravity property for + * more information. + * + * Since: 1.10 + */ +void +clutter_actor_set_content_gravity (ClutterActor *self, + ClutterContentGravity gravity) +{ + ClutterActorPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + priv = self->priv; + + if (priv->content_gravity == gravity) + return; + + priv->content_gravity = gravity; + + clutter_actor_queue_redraw (self); + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]); + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]); +} + +/** + * clutter_actor_get_content_gravity: + * @self: a #ClutterActor + * + * Retrieves the content gravity as set using + * clutter_actor_get_content_gravity(). + * + * Return value: the content gravity + * + * Since: 1.10 + */ +ClutterContentGravity +clutter_actor_get_content_gravity (ClutterActor *self) +{ + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), + CLUTTER_CONTENT_GRAVITY_RESIZE_FILL); + + return self->priv->content_gravity; +} + +/** + * clutter_actor_get_content_box: + * @self: a #ClutterActor + * @box: (out caller-allocates): the return location for the bounding + * box for the #ClutterContent + * + * Retrieves the bounding box for the #ClutterContent of @self. + * + * If no #ClutterContent is set for @self, or if @self has not been + * allocated yet, then the result is undefined. + * + * The content box is guaranteed to be, at most, as big as the allocation + * of the #ClutterActor. + * + * If the #ClutterContent used by the actor has a preferred size, then + * it is possible to modify the content box by using the + * #ClutterActor:content-gravity property. + * + * Since: 1.10 + */ +void +clutter_actor_get_content_box (ClutterActor *self, + ClutterActorBox *box) +{ + ClutterActorPrivate *priv; + gfloat content_w, content_h; + gfloat alloc_w, alloc_h; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + g_return_if_fail (box != NULL); + + priv = self->priv; + + if (!clutter_actor_has_allocation (self)) + return; + + if (priv->content == NULL) + return; + + *box = priv->allocation; + + /* no need to do any more work */ + if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL) + return; + + /* if the content does not have a preferred size then there is + * no point in computing the content box + */ + if (!_clutter_content_get_preferred_size (priv->content, + &content_w, + &content_h)) + return; + + clutter_actor_box_get_size (&priv->allocation, &alloc_w, &alloc_h); + + switch (priv->content_gravity) + { + case CLUTTER_CONTENT_GRAVITY_TOP_LEFT: + box->x2 = box->x1 + MIN (content_w, alloc_w); + box->y2 = box->y1 + MIN (content_h, alloc_h); + break; + + case CLUTTER_CONTENT_GRAVITY_TOP: + if (alloc_w > content_w) + { + box->x1 += ceilf ((alloc_w - content_w) / 2.0); + box->x2 = box->x1 + content_w; + } + box->y2 = box->y1 + MIN (content_h, alloc_h); + break; + + case CLUTTER_CONTENT_GRAVITY_TOP_RIGHT: + if (alloc_w > content_w) + { + box->x1 += (alloc_w - content_w); + box->x2 = box->x1 + content_w; + } + box->y2 = box->y1 + MIN (content_h, alloc_h); + break; + + case CLUTTER_CONTENT_GRAVITY_LEFT: + box->x2 = box->x1 + MIN (content_w, alloc_w); + if (alloc_h > content_h) + { + box->y1 += ceilf ((alloc_h - content_h) / 2.0); + box->y2 = box->y1 + content_h; + } + break; + + case CLUTTER_CONTENT_GRAVITY_CENTER: + if (alloc_w > content_w) + { + box->x1 += ceilf ((alloc_w - content_w) / 2.0); + box->x2 = box->x1 + content_w; + } + if (alloc_h > content_h) + { + box->y1 += ceilf ((alloc_h - content_h) / 2.0); + box->y2 = box->y1 + content_h; + } + break; + + case CLUTTER_CONTENT_GRAVITY_RIGHT: + if (alloc_w > content_w) + { + box->x1 += (alloc_w - content_w); + box->x2 = box->x1 + content_w; + } + if (alloc_h > content_h) + { + box->y1 += ceilf ((alloc_h - content_h) / 2.0); + box->y2 = box->y1 + content_h; + } + break; + + case CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT: + box->x2 = box->x1 + MIN (content_w, alloc_w); + if (alloc_h > content_h) + { + box->y1 += (alloc_h - content_h); + box->y2 = box->y1 + content_h; + } + break; + + case CLUTTER_CONTENT_GRAVITY_BOTTOM: + if (alloc_w > content_w) + { + box->x1 += ceilf ((alloc_w - content_w) / 2.0); + box->x2 = box->x1 + content_w; + } + if (alloc_h > content_h) + { + box->y1 += (alloc_h - content_h); + box->y2 = box->y1 + content_h; + } + break; + + case CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT: + if (alloc_w > content_w) + { + box->x1 += (alloc_w - content_w); + box->x2 = box->x1 + content_w; + } + if (alloc_h > content_h) + { + box->y1 += (alloc_h - content_h); + box->y2 = box->y1 + content_h; + } + break; + + case CLUTTER_CONTENT_GRAVITY_RESIZE_FILL: + g_assert_not_reached (); + break; + + case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT: + if (content_w >= content_h && content_h > 0) + { + double ratio = content_w / content_h; + + box->x2 = box->x1 + alloc_w; + + box->y1 += ceilf ((alloc_h - (alloc_h / ratio)) / 2.0); + box->y2 = box->y1 + (alloc_h / ratio); + } + else if (content_h > content_w && content_w > 0) + { + double ratio = content_h / content_w; + + box->x1 += ceilf ((alloc_w - (alloc_w / ratio)) / 2.0); + box->x2 = box->x2 + (alloc_w / ratio); + + box->y2 = box->x1 + alloc_h; + } + break; + } +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 81fa7948f..42f0fcea7 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -467,6 +467,21 @@ gboolean clutter_actor_is_in_clone_paint gboolean clutter_actor_get_paint_box (ClutterActor *self, ClutterActorBox *box); gboolean clutter_actor_has_overlaps (ClutterActor *self); + +/* Content */ +CLUTTER_AVAILABLE_IN_1_10 +void clutter_actor_set_content (ClutterActor *self, + ClutterContent *content); +CLUTTER_AVAILABLE_IN_1_10 +ClutterContent * clutter_actor_get_content (ClutterActor *self); +CLUTTER_AVAILABLE_IN_1_10 +void clutter_actor_set_content_gravity (ClutterActor *self, + ClutterContentGravity gravity); +CLUTTER_AVAILABLE_IN_1_10 +ClutterContentGravity clutter_actor_get_content_gravity (ClutterActor *self); +CLUTTER_AVAILABLE_IN_1_10 +void clutter_actor_get_content_box (ClutterActor *self, + ClutterActorBox *box); CLUTTER_AVAILABLE_IN_1_10 void clutter_actor_set_background_color (ClutterActor *self, const ClutterColor *color); diff --git a/clutter/clutter-content-private.h b/clutter/clutter-content-private.h new file mode 100644 index 000000000..56c3f78c0 --- /dev/null +++ b/clutter/clutter-content-private.h @@ -0,0 +1,47 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2011 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifndef __CLUTTER_CONTENT_PRIVATE_H__ +#define __CLUTTER_CONTENT_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +gboolean _clutter_content_get_preferred_size (ClutterContent *content, + gfloat *width, + gfloat *height); + +void _clutter_content_attached (ClutterContent *content, + ClutterActor *actor); +void _clutter_content_detached (ClutterContent *content, + ClutterActor *actor); + +void _clutter_content_paint_content (ClutterContent *content, + ClutterActor *actor, + ClutterPaintNode *node); + +G_END_DECLS + +#endif /* __CLUTTER_CONTENT_PRIVATE_H__ */ diff --git a/clutter/clutter-content.c b/clutter/clutter-content.c new file mode 100644 index 000000000..d14a5ce63 --- /dev/null +++ b/clutter/clutter-content.c @@ -0,0 +1,248 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2011 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:clutter-content + * @Title: ClutterContent + * @short_desc: Delegate for painting the content of an actor + * + * #ClutterContent is an interface to implement types responsible for + * painting the content of a #ClutterActor. + * + * Multiple actors can use the same #ClutterContent instance, in order + * to share the resources associated with painting the same content. + * + * #ClutterContent is available since Clutter 1.10. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-content-private.h" + +#include "clutter-debug.h" +#include "clutter-marshal.h" +#include "clutter-private.h" + +typedef struct _ClutterContentIface ClutterContentInterface; + +static GQuark quark_content_actors = 0; + +G_DEFINE_INTERFACE (ClutterContent, clutter_content, G_TYPE_OBJECT) + +static gboolean +clutter_content_real_get_preferred_size (ClutterContent *content, + gfloat *width, + gfloat *height) +{ + if (width != NULL) + *width = 0.f; + + if (height != NULL) + *height = 0.f; + + return FALSE; +} + +static void +clutter_content_real_attached (ClutterContent *content, + ClutterActor *actor) +{ + GObject *obj = G_OBJECT (content); + GHashTable *actors; + + actors = g_object_get_qdata (obj, quark_content_actors); + if (actors == NULL) + { + actors = g_hash_table_new (NULL, NULL); + g_object_set_qdata_full (obj, quark_content_actors, + actors, + (GDestroyNotify) g_hash_table_unref); + } + + g_hash_table_insert (actors, actor, actor); +} + +static void +clutter_content_real_detached (ClutterContent *content, + ClutterActor *actor) +{ + GObject *obj = G_OBJECT (content); + GHashTable *actors; + + actors = g_object_get_qdata (obj, quark_content_actors); + g_assert (actors != NULL); + + g_hash_table_remove (actors, actor); + + if (g_hash_table_size (actors) == 0) + g_object_set_qdata (obj, quark_content_actors, NULL); +} + +static void +clutter_content_real_invalidate (ClutterContent *content) +{ + GHashTable *actors; + GHashTableIter iter; + gpointer key_p, value_p; + + actors = g_object_get_qdata (G_OBJECT (content), quark_content_actors); + if (actors == NULL) + return; + + g_hash_table_iter_init (&iter, actors); + while (g_hash_table_iter_next (&iter, &key_p, &value_p)) + { + ClutterActor *actor = key_p; + + g_assert (actor != NULL); + + clutter_actor_queue_redraw (actor); + } +} + +static void +clutter_content_real_paint_content (ClutterContent *content, + ClutterActor *actor, + ClutterPaintNode *context) +{ +} + +static void +clutter_content_default_init (ClutterContentInterface *iface) +{ + quark_content_actors = g_quark_from_static_string ("-clutter-content-actors"); + + iface->get_preferred_size = clutter_content_real_get_preferred_size; + iface->paint_content = clutter_content_real_paint_content; + iface->attached = clutter_content_real_attached; + iface->detached = clutter_content_real_detached; + iface->invalidate = clutter_content_real_invalidate; +} + +/** + * clutter_content_invalidate: + * @content: a #ClutterContent + * + * Invalidates a #ClutterContent. + * + * This function should be called by #ClutterContent implementations when + * they change the way a the content should be painted regardless of the + * actor state. + * + * Since: 1.10 + */ +void +clutter_content_invalidate (ClutterContent *content) +{ + g_return_if_fail (CLUTTER_IS_CONTENT (content)); + + CLUTTER_CONTENT_GET_IFACE (content)->invalidate (content); +} + +/*< private > + * _clutter_content_attached: + * @content: a #ClutterContent + * @actor: a #ClutterActor + * + * Attaches @actor to the @content. + * + * This function should be used internally every time a #ClutterActor + * is associated to a #ClutterContent, to set up a backpointer from + * the @content to the @actor. + * + * This function will invoke the #ClutterContentIface.attached() virtual + * function. + */ +void +_clutter_content_attached (ClutterContent *content, + ClutterActor *actor) +{ + CLUTTER_CONTENT_GET_IFACE (content)->attached (content, actor); +} + +/*< private > + * _clutter_content_detached: + * @content: a #ClutterContent + * @actor: a #ClutterActor + * + * Detaches @actor from @content. + * + * This function should be used internally every time a #ClutterActor + * removes the association with a #ClutterContent. + * + * This function will invoke the #ClutterContentIface.detached() virtual + * function. + */ +void +_clutter_content_detached (ClutterContent *content, + ClutterActor *actor) +{ + CLUTTER_CONTENT_GET_IFACE (content)->detached (content, actor); +} + +/*< private > + * _clutter_content_paint_content: + * @content: a #ClutterContent + * @actor: a #ClutterActor + * @context: a #ClutterPaintNode + * + * Creates the render tree for the @content and @actor. + * + * This function will invoke the #ClutterContentIface.paint_content() + * virtual function. + */ +void +_clutter_content_paint_content (ClutterContent *content, + ClutterActor *actor, + ClutterPaintNode *node) +{ + CLUTTER_CONTENT_GET_IFACE (content)->paint_content (content, actor, node); +} + +/*< private > + * _clutter_content_get_preferred_size: + * @content: a #ClutterContent + * @width: (out): return location for the natural width of the content + * @height: (out): return location for the natural height of the content + * + * Retrieves the natural size of the @content, if any. + * + * The natural size of a #ClutterContent is defined as the size the content + * would have regardless of the allocation of the actor that is painting it, + * for instance the size of an image data. + * + * Return value: %TRUE if the content has a preferred size, and %FALSE + * otherwise + */ +gboolean +_clutter_content_get_preferred_size (ClutterContent *content, + gfloat *width, + gfloat *height) +{ + return CLUTTER_CONTENT_GET_IFACE (content)->get_preferred_size (content, + width, + height); +} diff --git a/clutter/clutter-content.h b/clutter/clutter-content.h new file mode 100644 index 000000000..0f91fa242 --- /dev/null +++ b/clutter/clutter-content.h @@ -0,0 +1,100 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2011 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_CONTENT_H__ +#define __CLUTTER_CONTENT_H__ + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_CONTENT (clutter_content_get_type ()) +#define CLUTTER_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CONTENT, ClutterContent)) +#define CLUTTER_IS_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CONTENT)) +#define CLUTTER_CONTENT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_CONTENT, ClutterContentIface)) + +typedef struct _ClutterContentIface ClutterContentIface; + +/** + * ClutterContent: + * + * The ClutterContent structure is an opaque type + * whose members cannot be acccessed directly. + * + * Since: 1.10 + */ + +/** + * ClutterContentIface: + * @get_preferred_size: virtual function; should be overridden by subclasses + * of #ClutterContent that have a natural size + * @paint_content: virtual function; called each time the content needs to + * paint itself + * @attached: virtual function; called each time a #ClutterContent is attached + * to a #ClutterActor. If overridden, the subclass must chain up to the + * parent class implementation. + * @detached: virtual function; called each time a #ClutterContent is detached + * from a #ClutterActor. If overridden, the subclass must chain up to the + * parent class implementation. + * @invalidate: virtual function; called each time a #ClutterContent state + * is changed. If overridden, the subclass must chain up to the parent + * class implementation. + * + * The ClutterContentIface structure contains only + * private data. + * + * Since: 1.10 + */ +struct _ClutterContentIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + gboolean (* get_preferred_size) (ClutterContent *content, + gfloat *width, + gfloat *height); + void (* paint_content) (ClutterContent *content, + ClutterActor *actor, + ClutterPaintNode *node); + + void (* attached) (ClutterContent *content, + ClutterActor *actor); + void (* detached) (ClutterContent *content, + ClutterActor *actor); + + void (* invalidate) (ClutterContent *content); +}; + +GType clutter_content_get_type (void) G_GNUC_CONST; + +void clutter_content_invalidate (ClutterContent *content); + +G_END_DECLS + +#endif /* __CLUTTER_CONTENT_H__ */ diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h index 0588780bb..8e68015b6 100644 --- a/clutter/clutter-enums.h +++ b/clutter/clutter-enums.h @@ -1113,6 +1113,42 @@ typedef enum { CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD = 1 << 2 } ClutterRepaintFlags; +/** + * ClutterContentGravity: + * @CLUTTER_CONTENT_GRAVITY_TOP_LEFT: Align the content to the top left corner + * @CLUTTER_CONTENT_GRAVITY_TOP: Align the content to the top edge + * @CLUTTER_CONTENT_GRAVITY_TOP_RIGHT: Align the content to the top right corner + * @CLUTTER_CONTENT_GRAVITY_LEFT: Align the content to the left edge + * @CLUTTER_CONTENT_GRAVITY_CENTER: Align the content to the center + * @CLUTTER_CONTENT_GRAVITY_RIGHT: Align the content to the right edge + * @CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT: Align the content to the bottom left corner + * @CLUTTER_CONTENT_GRAVITY_BOTTOM: Align the content to the bottom edge + * @CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT: Align the content to the bottom right corner + * @CLUTTER_CONTENT_GRAVITY_RESIZE_FILL: Resize the content to fill the allocation + * @CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT: Resize the content to remain within the + * allocation, while maintaining the aspect ratio + * + * Controls the alignment of the #ClutterContent inside a #ClutterActor. + * + * Since: 1.10 + */ +typedef enum { + CLUTTER_CONTENT_GRAVITY_TOP_LEFT, + CLUTTER_CONTENT_GRAVITY_TOP, + CLUTTER_CONTENT_GRAVITY_TOP_RIGHT, + + CLUTTER_CONTENT_GRAVITY_LEFT, + CLUTTER_CONTENT_GRAVITY_CENTER, + CLUTTER_CONTENT_GRAVITY_RIGHT, + + CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT, + CLUTTER_CONTENT_GRAVITY_BOTTOM, + CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT, + + CLUTTER_CONTENT_GRAVITY_RESIZE_FILL, + CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT +} ClutterContentGravity; + G_END_DECLS #endif /* __CLUTTER_ENUMS_H__ */ diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index 3fd93350e..8b41f633a 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -54,6 +54,7 @@ typedef struct _ClutterActorMeta ClutterActorMeta; typedef struct _ClutterLayoutManager ClutterLayoutManager; typedef struct _ClutterActorIter ClutterActorIter; typedef struct _ClutterPaintNode ClutterPaintNode; +typedef struct _ClutterContent ClutterContent; /* dummy */ typedef struct _ClutterAlpha ClutterAlpha; typedef struct _ClutterAnimatable ClutterAnimatable; /* dummy */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 52d443fa8..070691bc7 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -55,6 +55,7 @@ #include "clutter-colorize-effect.h" #include "clutter-constraint.h" #include "clutter-container.h" +#include "clutter-content.h" #include "clutter-deform-effect.h" #include "clutter-desaturate-effect.h" #include "clutter-device-manager.h" diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index a31e87c59..b0523e5c1 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -56,7 +56,8 @@ UNIT_TESTS = \ test-drop.c \ test-devices.c \ test-actor.c \ - test-transitions.c + test-transitions.c \ + test-content.c if X11_TESTS UNIT_TESTS += test-pixmap.c diff --git a/tests/interactive/test-content.c b/tests/interactive/test-content.c new file mode 100644 index 000000000..f8d007a40 --- /dev/null +++ b/tests/interactive/test-content.c @@ -0,0 +1,223 @@ +#include +#include +#include + +typedef struct _ColorContent { + GObject parent_instance; + + double red; + double green; + double blue; + double alpha; + + float padding; +} ColorContent; + +typedef struct _ColorContentClass { + GObjectClass parent_class; +} ColorContentClass; + +static void clutter_content_iface_init (ClutterContentIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ColorContent, color_content, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, + clutter_content_iface_init)) + +static void +color_content_paint_content (ClutterContent *content, + ClutterActor *actor, + ClutterPaintNode *root) +{ + ColorContent *self = (ColorContent *) content; + ClutterActorBox box, content_box; + ClutterColor color; + PangoLayout *layout; + PangoRectangle logical; + ClutterPaintNode *node; + +#if 0 + g_debug ("Painting content [%p] " + "{ r:%.2f, g:%.2f, b:%.2f, a:%.2f } " + "for actor [%p] (context: [%p])", + content, + self->red, + self->green, + self->blue, + self->alpha, + actor, context); +#endif + + clutter_actor_get_content_box (actor, &content_box); + + box = content_box; + box.x1 += self->padding; + box.y1 += self->padding; + box.x2 -= self->padding; + box.y2 -= self->padding; + + color.alpha = self->alpha * 255; + + color.red = self->red * 255; + color.green = self->green * 255; + color.blue = self->blue * 255; + + node = clutter_color_node_new (&color); + clutter_paint_node_add_rectangle (node, &box); + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); + + color.red = (1.0 - self->red) * 255; + color.green = (1.0 - self->green) * 255; + color.blue = (1.0 - self->blue) * 255; + + layout = clutter_actor_create_pango_layout (actor, "A"); + pango_layout_get_pixel_extents (layout, NULL, &logical); + + node = clutter_text_node_new (layout, &color); + + /* top-left */ + box.x1 = clutter_actor_box_get_x (&content_box); + box.y1 = clutter_actor_box_get_y (&content_box); + clutter_paint_node_add_rectangle (node, &box); + + /* top-right */ + box.x1 = clutter_actor_box_get_x (&content_box) + + clutter_actor_box_get_width (&content_box) + - logical.width; + box.y1 = clutter_actor_box_get_y (&content_box); + clutter_paint_node_add_rectangle (node, &box); + + /* bottom-right */ + box.x1 = clutter_actor_box_get_x (&content_box) + + clutter_actor_box_get_width (&content_box) + - logical.width; + box.y1 = clutter_actor_box_get_y (&content_box) + + clutter_actor_box_get_height (&content_box) + - logical.height; + clutter_paint_node_add_rectangle (node, &box); + + /* bottom-left */ + box.x1 = clutter_actor_box_get_x (&content_box); + box.y1 = clutter_actor_box_get_y (&content_box) + + clutter_actor_box_get_height (&content_box) + - logical.height; + clutter_paint_node_add_rectangle (node, &box); + + /* center */ + box.x1 = clutter_actor_box_get_x (&content_box) + + (clutter_actor_box_get_width (&content_box) - logical.width) / 2.0; + box.y1 = clutter_actor_box_get_y (&content_box) + + (clutter_actor_box_get_height (&content_box) - logical.height) / 2.0; + clutter_paint_node_add_rectangle (node, &box); + + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); + + g_object_unref (layout); +} + +static void +clutter_content_iface_init (ClutterContentIface *iface) +{ + iface->paint_content = color_content_paint_content; +} + +static void +color_content_class_init (ColorContentClass *klass) +{ +} + +static void +color_content_init (ColorContent *self) +{ +} + +static ClutterContent * +color_content_new (double red, + double green, + double blue, + double alpha, + float padding) +{ + ColorContent *self = g_object_new (color_content_get_type (), NULL); + + self->red = red; + self->green = green; + self->blue = blue; + self->alpha = alpha; + self->padding = padding; + + return (ClutterContent *) self; +} + +G_MODULE_EXPORT int +test_content_main (int argc, char *argv[]) +{ + ClutterActor *stage, *grid; + ClutterContent *content; + int i, n_rects; + + if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) + return EXIT_FAILURE; + + stage = clutter_stage_new (); + clutter_actor_set_name (stage, "Stage"); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Content"); + clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + clutter_actor_show (stage); + + grid = clutter_actor_new (); + clutter_actor_set_name (grid, "Grid"); + clutter_actor_set_margin_top (grid, 12); + clutter_actor_set_margin_right (grid, 12); + clutter_actor_set_margin_bottom (grid, 12); + clutter_actor_set_margin_left (grid, 12); + clutter_actor_set_layout_manager (grid, clutter_flow_layout_new (CLUTTER_FLOW_HORIZONTAL)); + clutter_actor_add_constraint (grid, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, 0.0)); + clutter_actor_add_child (stage, grid); + + content = color_content_new (g_random_double_range (0.0, 1.0), + g_random_double_range (0.0, 1.0), + g_random_double_range (0.0, 1.0), + 1.0, + 2.0); + + n_rects = g_random_int_range (12, 24); + for (i = 0; i < n_rects; i++) + { + ClutterActor *box = clutter_actor_new (); + ClutterColor bg_color = { + g_random_int_range (0, 255), + g_random_int_range (0, 255), + g_random_int_range (0, 255), + 255 + }; + char *name, *color; + + color = clutter_color_to_string (&bg_color); + name = g_strconcat ("Box <", color, ">", NULL); + clutter_actor_set_name (box, name); + + g_free (name); + g_free (color); + + clutter_actor_set_background_color (box, &bg_color); + clutter_actor_set_content (box, content); + clutter_actor_set_size (box, 64, 64); + + clutter_actor_add_child (grid, box); + } + + clutter_main (); + + g_object_unref (content); + + return EXIT_SUCCESS; +} + +G_MODULE_EXPORT const char * +test_content_describe (void) +{ + return "A simple test for ClutterContent"; +}