/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 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-layout-manager * @short_description: Layout managers base class * * #ClutterLayoutManager is a base abstract class for layout managers. A * layout manager implements the layouting policy for a composite or a * container actor: it controls the preferred size of the actor to which * it has been paired, and it controls the allocation of its children. * * Any composite or container #ClutterActor subclass can delegate the * layouting of its children to a #ClutterLayoutManager. Clutter provides * a generic container using #ClutterLayoutManager called #ClutterBox. * * Clutter provides some simple #ClutterLayoutManager sub-classes, like * #ClutterFlowLayout and #ClutterBinLayout. * * * Using a Layout Manager inside an Actor * In order to use a #ClutterLayoutManager inside a #ClutterActor * sub-class you should invoke clutter_layout_manager_get_preferred_width() * inside the #ClutterActorClass.get_preferred_width() virtual function and * clutter_layout_manager_get_preferred_height() inside the * #ClutterActorClass.get_preferred_height() virtual functions implementation. * You should also call clutter_layout_manager_allocate() inside the * implementation of the #ClutterActorClass.allocate() virtual function. * In order to receive notifications for changes in the layout * manager policies you should also connect to the * #ClutterLayoutManager::layout-changed signal and queue a relayout * on your actor. The following code should be enough if the actor * does not need to perform specific operations whenever a layout * manager changes: * * g_signal_connect_swapped (layout_manager, * "layout-changed", * G_CALLBACK (clutter_actor_queue_relayout), * actor); * * * * * Implementing a ClutterLayoutManager * The implementation of a layout manager does not differ from * the implementation of the size requisition and allocation bits of * #ClutterActor, so you should read the relative documentation * for subclassing * ClutterActor. * The layout manager implementation can hold a back pointer * to the #ClutterContainer by implementing the * set_container() virtual function. The layout manager * should not hold a real reference (i.e. call g_object_ref()) on the * container actor, to avoid reference cycles. * If a layout manager has properties affecting the layout * policies then it should emit the #ClutterLayoutManager::layout-changed * signal on itself by using the clutter_layout_manager_layout_changed() * function whenever one of these properties changes. * * * * Animating a ClutterLayoutManager * A layout manager is used to let a #ClutterContainer take complete * ownership over the layout (that is: the position and sizing) of its * children; this means that using the Clutter animation API, like * clutter_actor_animate(), to animate the position and sizing of a child of * a layout manager it is not going to work properly, as the animation will * automatically override any setting done by the layout manager * itself. * It is possible for a #ClutterLayoutManager sub-class to animate its * children layout by using the base class animation support. The * #ClutterLayoutManager animation support consists of three virtual * functions: #ClutterLayoutManagerClass.begin_animation(), * #ClutterLayoutManagerClass.get_animation_progress(), and * #ClutterLayoutManagerClass.end_animation(). * * * begin_animation (duration, easing) * This virtual function is invoked when the layout * manager should begin an animation. The implementation should set up * the state for the animation and create the ancillary objects for * animating the layout. The default implementation creates a * #ClutterTimeline for the given duration and a #ClutterAlpha binding * the timeline to the given easing mode. This function returns a * #ClutterAlpha which should be used to control the animation from * the caller perspective. * * * get_animation_progress() * This virtual function should be invoked when animating * a layout manager. It returns the progress of the animation, using the * same semantics as the #ClutterAlpha:alpha value. * * * end_animation() * This virtual function is invoked when the animation of * a layout manager ends, and it is meant to be used for bookkeeping the * objects created in the begin_animation() * function. The default implementation will call it implicitly when the * timeline is complete. * * * The simplest way to animate a layout is to create a #ClutterTimeline * inside the begin_animation() virtual function, along * with a #ClutterAlpha, and for each #ClutterTimeline::new-frame signal * emission call clutter_layout_manager_layout_changed(), which will cause a * relayout. The #ClutterTimeline::completed signal emission should cause * clutter_layout_manager_end_animation() to be called. The default * implementation provided internally by #ClutterLayoutManager does exactly * this, so most sub-classes should either not override any animation-related * virtual function or simply override #ClutterLayoutManagerClass.begin_animation() * and #ClutterLayoutManagerClass.end_animation() to set up ad hoc state, and then * chain up to the parent's implementation. * * Animation of a Layout Manager * The code below shows how a #ClutterLayoutManager sub-class should * provide animating the allocation of its children from within the * #ClutterLayoutManagerClass.allocate() virtual function implementation. The * animation is computed between the last stable allocation performed * before the animation started and the desired final allocation. * The is_animating variable is stored inside the * #ClutterLayoutManager sub-class and it is updated by overriding the * #ClutterLayoutManagerClass.begin_animation() and the * #ClutterLayoutManagerClass.end_animation() virtual functions and chaining up * to the base class implementation. * The last stable allocation is stored within a #ClutterLayoutMeta * sub-class used by the implementation. * * static void * my_layout_manager_allocate (ClutterLayoutManager *manager, * ClutterContainer *container, * const ClutterActorBox *allocation, * ClutterAllocationFlags flags) * { * MyLayoutManager *self = MY_LAYOUT_MANAGER (manager); * ClutterActor *child; * * for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (container)); * child != NULL; * child = clutter_actor_get_next_sibling (child)) * { * ClutterLayoutMeta *meta; * MyLayoutMeta *my_meta; * * /* retrieve the layout meta-object */ * meta = clutter_layout_manager_get_child_meta (manager, * container, * child); * my_meta = MY_LAYOUT_META (meta); * * /* compute the desired allocation for the child */ * compute_allocation (self, my_meta, child, * allocation, flags, * &child_box); * * /* this is the additional code that deals with the animation * * of the layout manager * */ * if (!self->is_animating) * { * /* store the last stable allocation for later use */ * my_meta->last_alloc = clutter_actor_box_copy (&child_box); * } * else * { * ClutterActorBox end = { 0, }; * gdouble p; * * /* get the progress of the animation */ * p = clutter_layout_manager_get_animation_progress (manager); * * if (my_meta->last_alloc != NULL) * { * /* copy the desired allocation as the final state */ * end = child_box; * * /* then interpolate the initial and final state * * depending on the progress of the animation, * * and put the result inside the box we will use * * to allocate the child * */ * clutter_actor_box_interpolate (my_meta->last_alloc, * &end, * p, * &child_box); * } * else * { * /* if there is no stable allocation then the child was * * added while animating; one possible course of action * * is to just bail out and fall through to the allocation * * to position the child directly at its final state * */ * my_meta->last_alloc = * clutter_actor_box_copy (&child_box); * } * } * * /* allocate the child */ * clutter_actor_allocate (child, &child_box, flags); * } * } * * * Sub-classes of #ClutterLayoutManager that support animations of the * layout changes should call clutter_layout_manager_begin_animation() * whenever a layout property changes value, e.g.: * * * if (self->orientation != new_orientation) * { * ClutterLayoutManager *manager; * * self->orientation = new_orientation; * * manager = CLUTTER_LAYOUT_MANAGER (self); * clutter_layout_manager_layout_changed (manager); * clutter_layout_manager_begin_animation (manager, 500, CLUTTER_LINEAR); * * g_object_notify (G_OBJECT (self), "orientation"); * } * * * The code above will animate a change in the * orientation layout property of a layout manager. * * * * Layout Properties * If a layout manager has layout properties, that is properties that * should exist only as the result of the presence of a specific (layout * manager, container actor, child actor) combination, and it wishes to store * those properties inside a #ClutterLayoutMeta, then it should override the * #ClutterLayoutManagerClass.get_child_meta_type() virtual function to return * the #GType of the #ClutterLayoutMeta sub-class used to store the layout * properties; optionally, the #ClutterLayoutManager sub-class might also * override the #ClutterLayoutManagerClass.create_child_meta() virtual function * to control how the #ClutterLayoutMeta instance is created, otherwise the * default implementation will be equivalent to: * * ClutterLayoutManagerClass *klass; * GType meta_type; * * klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); * meta_type = klass->get_child_meta_type (manager); * * return g_object_new (meta_type, * "manager", manager, * "container", container, * "actor", actor, * NULL); * * Where manager is the #ClutterLayoutManager, * container is the #ClutterContainer using the * #ClutterLayoutManager and actor is the #ClutterActor * child of the #ClutterContainer. * * * * Using ClutterLayoutManager with ClutterScript * #ClutterLayoutManager instance can be created in the same way * as other objects in #ClutterScript; properties can be set using the * common syntax. * Layout properties can be set on children of a container with * a #ClutterLayoutManager using the layout:: * modifier on the property name, for instance: * * { * "type" : "ClutterBox", * "layout-manager" : { "type" : "ClutterTableLayout" }, * "children" : [ * { * "type" : "ClutterTexture", * "filename" : "image-00.png", * * "layout::row" : 0, * "layout::column" : 0, * "layout::x-align" : "left", * "layout::y-align" : "center", * "layout::x-expand" : true, * "layout::y-expand" : true * }, * { * "type" : "ClutterTexture", * "filename" : "image-01.png", * * "layout::row" : 0, * "layout::column" : 1, * "layout::x-align" : "right", * "layout::y-align" : "center", * "layout::x-expand" : true, * "layout::y-expand" : true * } * ] * } * * * * #ClutterLayoutManager is available since Clutter 1.2 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-container.h" #include "deprecated/clutter-alpha.h" #include "clutter-debug.h" #include "clutter-layout-manager.h" #include "clutter-layout-meta.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-timeline.h" #define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method) G_STMT_START { \ GObject *_obj = G_OBJECT (m); \ g_warning ("Layout managers of type %s do not implement " \ "the ClutterLayoutManager::%s method", \ G_OBJECT_TYPE_NAME (_obj), \ (method)); } G_STMT_END struct _ClutterLayoutManagerPrivate { gpointer dummy; }; enum { LAYOUT_CHANGED, LAST_SIGNAL }; G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, clutter_layout_manager, G_TYPE_INITIALLY_UNOWNED); static GQuark quark_layout_meta = 0; static GQuark quark_layout_alpha = 0; static guint manager_signals[LAST_SIGNAL] = { 0, }; static void layout_manager_freeze_layout_change (ClutterLayoutManager *manager) { gpointer is_frozen; CLUTTER_NOTE (LAYOUT, "Freezing changes for manager '%s'[%p]", G_OBJECT_TYPE_NAME (manager), manager); is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change"); if (is_frozen == NULL) g_object_set_data (G_OBJECT (manager), "freeze-change", GUINT_TO_POINTER (1)); else { guint level = GPOINTER_TO_UINT (is_frozen) + 1; g_object_set_data (G_OBJECT (manager), "freeze-change", GUINT_TO_POINTER (level)); } } static void layout_manager_thaw_layout_change (ClutterLayoutManager *manager) { gpointer is_frozen; is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change"); if (is_frozen == NULL) g_critical (G_STRLOC ": Mismatched thaw; you have to call " "clutter_layout_manager_freeze_layout_change() prior to " "calling clutter_layout_manager_thaw_layout_change()"); else { guint level = GPOINTER_TO_UINT (is_frozen); g_assert (level > 0); CLUTTER_NOTE (LAYOUT, "Thawing changes for manager '%s'[%p]", G_OBJECT_TYPE_NAME (manager), manager); level -= 1; if (level == 0) g_object_set_data (G_OBJECT (manager), "freeze-change", NULL); else g_object_set_data (G_OBJECT (manager), "freeze-change", GUINT_TO_POINTER (level)); } } static void layout_manager_real_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_width"); if (min_width_p) *min_width_p = 0.0; if (nat_width_p) *nat_width_p = 0.0; } static void layout_manager_real_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_height"); if (min_height_p) *min_height_p = 0.0; if (nat_height_p) *nat_height_p = 0.0; } static void layout_manager_real_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "allocate"); } static void layout_manager_real_set_container (ClutterLayoutManager *manager, ClutterContainer *container) { if (container != NULL) g_object_set_data (G_OBJECT (container), "clutter-layout-manager", manager); } static ClutterLayoutMeta * layout_manager_real_create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { ClutterLayoutManagerClass *klass; GType meta_type; klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); meta_type = klass->get_child_meta_type (manager); /* provide a default implementation to reduce common code */ if (meta_type != G_TYPE_INVALID) { g_assert (g_type_is_a (meta_type, CLUTTER_TYPE_LAYOUT_META)); return g_object_new (meta_type, "manager", manager, "container", container, "actor", actor, NULL); } return NULL; } static GType layout_manager_real_get_child_meta_type (ClutterLayoutManager *manager) { return G_TYPE_INVALID; } /* XXX:2.0 - Remove */ static ClutterAlpha * layout_manager_real_begin_animation (ClutterLayoutManager *manager, guint duration, gulong mode) { ClutterTimeline *timeline; ClutterAlpha *alpha; alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha != NULL) { clutter_alpha_set_mode (alpha, mode); timeline = clutter_alpha_get_timeline (alpha); clutter_timeline_set_duration (timeline, duration); clutter_timeline_rewind (timeline); return alpha; }; timeline = clutter_timeline_new (duration); alpha = clutter_alpha_new_full (timeline, mode); /* let the alpha take ownership of the timeline */ g_object_unref (timeline); g_signal_connect_swapped (timeline, "completed", G_CALLBACK (clutter_layout_manager_end_animation), manager); g_signal_connect_swapped (timeline, "new-frame", G_CALLBACK (clutter_layout_manager_layout_changed), manager); g_object_set_qdata_full (G_OBJECT (manager), quark_layout_alpha, alpha, (GDestroyNotify) g_object_unref); clutter_timeline_start (timeline); return alpha; } /* XXX:2.0 - Remove */ static gdouble layout_manager_real_get_animation_progress (ClutterLayoutManager *manager) { ClutterAlpha *alpha; alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha == NULL) return 1.0; return clutter_alpha_get_alpha (alpha); } /* XXX:2.0 - Remove */ static void layout_manager_real_end_animation (ClutterLayoutManager *manager) { ClutterTimeline *timeline; ClutterAlpha *alpha; alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha == NULL) return; timeline = clutter_alpha_get_timeline (alpha); g_assert (timeline != NULL); if (clutter_timeline_is_playing (timeline)) clutter_timeline_stop (timeline); g_signal_handlers_disconnect_by_func (timeline, G_CALLBACK (clutter_layout_manager_end_animation), manager); g_signal_handlers_disconnect_by_func (timeline, G_CALLBACK (clutter_layout_manager_layout_changed), manager); g_object_set_qdata (G_OBJECT (manager), quark_layout_alpha, NULL); clutter_layout_manager_layout_changed (manager); } static void clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass) { quark_layout_meta = g_quark_from_static_string ("clutter-layout-manager-child-meta"); /* XXX:2.0 - Remove */ quark_layout_alpha = g_quark_from_static_string ("clutter-layout-manager-alpha"); g_type_class_add_private (klass, sizeof (ClutterLayoutManagerPrivate)); klass->get_preferred_width = layout_manager_real_get_preferred_width; klass->get_preferred_height = layout_manager_real_get_preferred_height; klass->allocate = layout_manager_real_allocate; klass->create_child_meta = layout_manager_real_create_child_meta; klass->get_child_meta_type = layout_manager_real_get_child_meta_type; /* XXX:2.0 - Remove */ klass->begin_animation = layout_manager_real_begin_animation; klass->get_animation_progress = layout_manager_real_get_animation_progress; klass->end_animation = layout_manager_real_end_animation; klass->set_container = layout_manager_real_set_container; /** * ClutterLayoutManager::layout-changed: * @manager: the #ClutterLayoutManager that emitted the signal * * The ::layout-changed signal is emitted each time a layout manager * has been changed. Every #ClutterActor using the @manager instance * as a layout manager should connect a handler to the ::layout-changed * signal and queue a relayout on themselves: * * |[ * static void layout_changed (ClutterLayoutManager *manager, * ClutterActor *self) * { * clutter_actor_queue_relayout (self); * } * ... * self->manager = g_object_ref_sink (manager); * g_signal_connect (self->manager, "layout-changed", * G_CALLBACK (layout_changed), * self); * ]| * * Sub-classes of #ClutterLayoutManager that implement a layout that * can be controlled or changed using parameters should emit the * ::layout-changed signal whenever one of the parameters changes, * by using clutter_layout_manager_layout_changed(). * * Since: 1.2 */ manager_signals[LAYOUT_CHANGED] = g_signal_new (I_("layout-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterLayoutManagerClass, layout_changed), NULL, NULL, _clutter_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void clutter_layout_manager_init (ClutterLayoutManager *manager) { manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManagerPrivate); } /** * clutter_layout_manager_get_preferred_width: * @manager: a #ClutterLayoutManager * @container: the #ClutterContainer using @manager * @for_height: the height for which the width should be computed, or -1 * @min_width_p: (out) (allow-none): return location for the minimum width * of the layout, or %NULL * @nat_width_p: (out) (allow-none): return location for the natural width * of the layout, or %NULL * * Computes the minimum and natural widths of the @container according * to @manager. * * See also clutter_actor_get_preferred_width() * * Since: 1.2 */ void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterLayoutManagerClass *klass; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); klass->get_preferred_width (manager, container, for_height, min_width_p, nat_width_p); } /** * clutter_layout_manager_get_preferred_height: * @manager: a #ClutterLayoutManager * @container: the #ClutterContainer using @manager * @for_width: the width for which the height should be computed, or -1 * @min_height_p: (out) (allow-none): return location for the minimum height * of the layout, or %NULL * @nat_height_p: (out) (allow-none): return location for the natural height * of the layout, or %NULL * * Computes the minimum and natural heights of the @container according * to @manager. * * See also clutter_actor_get_preferred_height() * * Since: 1.2 */ void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterLayoutManagerClass *klass; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); klass->get_preferred_height (manager, container, for_width, min_height_p, nat_height_p); } /** * clutter_layout_manager_allocate: * @manager: a #ClutterLayoutManager * @container: the #ClutterContainer using @manager * @allocation: the #ClutterActorBox containing the allocated area * of @container * @flags: the allocation flags * * Allocates the children of @container given an area * * See also clutter_actor_allocate() * * Since: 1.2 */ void clutter_layout_manager_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterLayoutManagerClass *klass; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (allocation != NULL); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); klass->allocate (manager, container, allocation, flags); } /** * clutter_layout_manager_layout_changed: * @manager: a #ClutterLayoutManager * * Emits the #ClutterLayoutManager::layout-changed signal on @manager * * This function should only be called by implementations of the * #ClutterLayoutManager class * * Since: 1.2 */ void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager) { gpointer is_frozen; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change"); if (is_frozen == NULL) g_signal_emit (manager, manager_signals[LAYOUT_CHANGED], 0); else CLUTTER_NOTE (LAYOUT, "Layout manager '%s'[%p] has been frozen", G_OBJECT_TYPE_NAME (manager), manager); } /** * clutter_layout_manager_set_container: * @manager: a #ClutterLayoutManager * @container: (allow-none): a #ClutterContainer using @manager * * If the #ClutterLayoutManager sub-class allows it, allow * adding a weak reference of the @container using @manager * from within the layout manager * * The layout manager should not increase the reference * count of the @container * * Since: 1.2 */ void clutter_layout_manager_set_container (ClutterLayoutManager *manager, ClutterContainer *container) { ClutterLayoutManagerClass *klass; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (container == NULL || CLUTTER_IS_CONTAINER (container)); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); if (klass->set_container) klass->set_container (manager, container); } GType _clutter_layout_manager_get_child_meta_type (ClutterLayoutManager *manager) { return CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->get_child_meta_type (manager); } static inline ClutterLayoutMeta * create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { ClutterLayoutManagerClass *klass; ClutterLayoutMeta *meta = NULL; layout_manager_freeze_layout_change (manager); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); if (klass->get_child_meta_type (manager) != G_TYPE_INVALID) meta = klass->create_child_meta (manager, container, actor); layout_manager_thaw_layout_change (manager); return meta; } static inline ClutterLayoutMeta * get_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { ClutterLayoutMeta *layout = NULL; layout = g_object_get_qdata (G_OBJECT (actor), quark_layout_meta); if (layout != NULL) { ClutterChildMeta *child = CLUTTER_CHILD_META (layout); if (layout->manager == manager && child->container == container && child->actor == actor) return layout; /* if the LayoutMeta referenced is not attached to the * layout manager then we simply ask the layout manager * to replace it with the right one */ } layout = create_child_meta (manager, container, actor); if (layout != NULL) { g_assert (CLUTTER_IS_LAYOUT_META (layout)); g_object_set_qdata_full (G_OBJECT (actor), quark_layout_meta, layout, (GDestroyNotify) g_object_unref); return layout; } return NULL; } /** * clutter_layout_manager_get_child_meta: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * * Retrieves the #ClutterLayoutMeta that the layout @manager associated * to the @actor child of @container, eventually by creating one if the * #ClutterLayoutManager supports layout properties * * Return value: (transfer none): a #ClutterLayoutMeta, or %NULL if the * #ClutterLayoutManager does not have layout properties. The returned * layout meta instance is owned by the #ClutterLayoutManager and it * should not be unreferenced * * Since: 1.0 */ ClutterLayoutMeta * clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL); g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); return get_child_meta (manager, container, actor); } static inline gboolean layout_set_property_internal (ClutterLayoutManager *manager, GObject *gobject, GParamSpec *pspec, const GValue *value) { if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { g_warning ("%s: Child property '%s' of the layout manager of " "type '%s' is constructor-only", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); return FALSE; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: Child property '%s' of the layout manager of " "type '%s' is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); return FALSE; } g_object_set_property (gobject, pspec->name, value); return TRUE; } static inline gboolean layout_get_property_internal (ClutterLayoutManager *manager, GObject *gobject, GParamSpec *pspec, GValue *value) { if (!(pspec->flags & G_PARAM_READABLE)) { g_warning ("%s: Child property '%s' of the layout manager of " "type '%s' is not readable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); return FALSE; } g_object_get_property (gobject, pspec->name, value); return TRUE; } /** * clutter_layout_manager_child_set: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @first_property: the first property name * @...: a list of property name and value pairs * * Sets a list of properties and their values on the #ClutterLayoutMeta * associated by @manager to a child of @container * * Languages bindings should use clutter_layout_manager_child_set_property() * instead * * Since: 1.2 */ void clutter_layout_manager_child_set (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *first_property, ...) { ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; va_list var_args; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (first_property != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type '%s' do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); va_start (var_args, first_property); pname = first_property; while (pname) { GValue value = G_VALUE_INIT; GParamSpec *pspec; gchar *error; gboolean res; pspec = g_object_class_find_property (klass, pname); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname); break; } G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } res = layout_set_property_internal (manager, G_OBJECT (meta), pspec, &value); g_value_unset (&value); if (!res) break; pname = va_arg (var_args, gchar*); } va_end (var_args); } /** * clutter_layout_manager_child_set_property: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @property_name: the name of the property to set * @value: a #GValue with the value of the property to set * * Sets a property on the #ClutterLayoutMeta created by @manager and * attached to a child of @container * * Since: 1.2 */ void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *property_name, const GValue *value) { ClutterLayoutMeta *meta; GObjectClass *klass; GParamSpec *pspec; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (property_name != NULL); g_return_if_fail (value != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type '%s' do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); pspec = g_object_class_find_property (klass, property_name); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name); return; } layout_set_property_internal (manager, G_OBJECT (meta), pspec, value); } /** * clutter_layout_manager_child_get: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @first_property: the name of the first property * @...: a list of property name and return location for the value pairs * * Retrieves the values for a list of properties out of the * #ClutterLayoutMeta created by @manager and attached to the * child of a @container * * Since: 1.2 */ void clutter_layout_manager_child_get (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *first_property, ...) { ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; va_list var_args; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (first_property != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type '%s' do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); va_start (var_args, first_property); pname = first_property; while (pname) { GValue value = G_VALUE_INIT; GParamSpec *pspec; gchar *error; gboolean res; pspec = g_object_class_find_property (klass, pname); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname); break; } g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); res = layout_get_property_internal (manager, G_OBJECT (meta), pspec, &value); if (!res) { g_value_unset (&value); break; } G_VALUE_LCOPY (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); break; } g_value_unset (&value); pname = va_arg (var_args, gchar*); } va_end (var_args); } /** * clutter_layout_manager_child_get_property: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @property_name: the name of the property to get * @value: a #GValue with the value of the property to get * * Gets a property on the #ClutterLayoutMeta created by @manager and * attached to a child of @container * * The #GValue must already be initialized to the type of the property * and has to be unset with g_value_unset() after extracting the real * value out of it * * Since: 1.2 */ void clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *property_name, GValue *value) { ClutterLayoutMeta *meta; GObjectClass *klass; GParamSpec *pspec; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (property_name != NULL); g_return_if_fail (value != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type %s do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); pspec = g_object_class_find_property (klass, property_name); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name); return; } layout_get_property_internal (manager, G_OBJECT (meta), pspec, value); } /** * clutter_layout_manager_find_child_property: * @manager: a #ClutterLayoutManager * @name: the name of the property * * Retrieves the #GParamSpec for the layout property @name inside * the #ClutterLayoutMeta sub-class used by @manager * * Return value: (transfer none): a #GParamSpec describing the property, * or %NULL if no property with that name exists. The returned * #GParamSpec is owned by the layout manager and should not be * modified or freed * * Since: 1.2 */ GParamSpec * clutter_layout_manager_find_child_property (ClutterLayoutManager *manager, const gchar *name) { ClutterLayoutManagerClass *klass; GObjectClass *meta_klass; GParamSpec *pspec; GType meta_type; klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); meta_type = klass->get_child_meta_type (manager); if (meta_type == G_TYPE_INVALID) return NULL; meta_klass = g_type_class_ref (meta_type); pspec = g_object_class_find_property (meta_klass, name); g_type_class_unref (meta_klass); return pspec; } /** * clutter_layout_manager_list_child_properties: * @manager: a #ClutterLayoutManager * @n_pspecs: (out): return location for the number of returned * #GParamSpecs * * Retrieves all the #GParamSpecs for the layout properties * stored inside the #ClutterLayoutMeta sub-class used by @manager * * Return value: (transfer full) (array length=n_pspecs): the newly-allocated, * %NULL-terminated array of #GParamSpecs. Use g_free() to free the * resources allocated for the array * * Since: 1.2 */ GParamSpec ** clutter_layout_manager_list_child_properties (ClutterLayoutManager *manager, guint *n_pspecs) { ClutterLayoutManagerClass *klass; GObjectClass *meta_klass; GParamSpec **pspecs; GType meta_type; klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); meta_type = klass->get_child_meta_type (manager); if (meta_type == G_TYPE_INVALID) return NULL; meta_klass = g_type_class_ref (meta_type); pspecs = g_object_class_list_properties (meta_klass, n_pspecs); g_type_class_unref (meta_klass); return pspecs; }