diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 59128c6d8..7cc3ddab3 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -65,6 +65,7 @@ source_h = \ $(srcdir)/clutter-behaviour-rotate.h \ $(srcdir)/clutter-behaviour-scale.h \ $(srcdir)/clutter-binding-pool.h \ + $(srcdir)/clutter-bin-layout.h \ $(srcdir)/clutter-box.h \ $(srcdir)/clutter-cairo-texture.h \ $(srcdir)/clutter-child-meta.h \ @@ -132,6 +133,7 @@ source_c = \ $(srcdir)/clutter-behaviour-scale.c \ $(srcdir)/clutter-bezier.c \ $(srcdir)/clutter-binding-pool.c \ + $(srcdir)/clutter-bin-layout.c \ $(srcdir)/clutter-box.c \ $(srcdir)/clutter-cairo-texture.c \ $(srcdir)/clutter-child-meta.c \ diff --git a/clutter/clutter-bin-layout.c b/clutter/clutter-bin-layout.c new file mode 100644 index 000000000..81a9cef39 --- /dev/null +++ b/clutter/clutter-bin-layout.c @@ -0,0 +1,397 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-actor.h" +#include "clutter-animatable.h" +#include "clutter-bin-layout.h" +#include "clutter-debug.h" +#include "clutter-enum-types.h" +#include "clutter-private.h" + +#define CLUTTER_BIN_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutPrivate)) + +struct _ClutterBinLayoutPrivate +{ + ClutterBinAlignment x_align; + ClutterBinAlignment y_align; + + gdouble x_factor; + gdouble y_factor; +}; + +enum +{ + PROP_0, + + PROP_X_ALIGN, + PROP_Y_ALIGN +}; + +G_DEFINE_TYPE (ClutterBinLayout, + clutter_bin_layout, + CLUTTER_TYPE_LAYOUT_MANAGER); + + +static void +set_x_align (ClutterBinLayout *self, + ClutterBinAlignment alignment) +{ + ClutterBinLayoutPrivate *priv = self->priv; + + if (priv->x_align != alignment) + { + priv->x_align = alignment; + + g_object_notify (G_OBJECT (self), "x-align"); + } +} + +static void +set_y_align (ClutterBinLayout *self, + ClutterBinAlignment alignment) +{ + ClutterBinLayoutPrivate *priv = self->priv; + + if (priv->y_align != alignment) + { + priv->y_align = alignment; + + g_object_notify (G_OBJECT (self), "y-align"); + } +} + +static void +clutter_bin_layout_get_preferred_width (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_height, + gfloat *min_width_p, + gfloat *nat_width_p) +{ + GList *children = clutter_container_get_children (container); + GList *l; + gfloat min_width, nat_width; + + min_width = nat_width = 0.0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat minimum, natural; + + clutter_actor_get_preferred_width (child, for_height, + &minimum, + &natural); + + min_width = MAX (min_width, minimum); + nat_width = MAX (nat_width, natural); + } + + if (min_width_p) + *min_width_p = min_width; + + if (nat_width_p) + *nat_width_p = nat_width; +} + +static void +clutter_bin_layout_get_preferred_height (ClutterLayoutManager *manager, + ClutterContainer *container, + gfloat for_width, + gfloat *min_height_p, + gfloat *nat_height_p) +{ + GList *children = clutter_container_get_children (container); + GList *l; + gfloat min_height, nat_height; + + min_height = nat_height = 0.0; + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + gfloat minimum, natural; + + clutter_actor_get_preferred_height (child, for_width, + &minimum, + &natural); + + min_height = MAX (min_height, minimum); + nat_height = MAX (nat_height, natural); + } + + if (min_height_p) + *min_height_p = min_height; + + if (nat_height_p) + *nat_height_p = nat_height; +} + +static gdouble +get_bin_alignment_factor (ClutterBinAlignment alignment) +{ + switch (alignment) + { + case CLUTTER_BIN_ALIGNMENT_CENTER: + return 0.5; + + case CLUTTER_BIN_ALIGNMENT_START: + return 0.0; + + case CLUTTER_BIN_ALIGNMENT_END: + return 1.0; + + case CLUTTER_BIN_ALIGNMENT_FIXED: + case CLUTTER_BIN_ALIGNMENT_FILL: + return 0.0; + } + + return 0.0; +} + +static void +clutter_bin_layout_allocate (ClutterLayoutManager *manager, + ClutterContainer *container, + const ClutterActorBox *allocation, + ClutterAllocationFlags flags) +{ + ClutterBinLayoutPrivate *priv = CLUTTER_BIN_LAYOUT (manager)->priv; + GList *children = clutter_container_get_children (container); + GList *l; + gfloat available_w, available_h; + + available_w = clutter_actor_box_get_width (allocation); + available_h = clutter_actor_box_get_height (allocation); + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + ClutterActorBox child_alloc = { 0, }; + gfloat child_width, child_height; + ClutterRequestMode request; + + if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FILL) + { + child_alloc.x1 = (int) 0; + child_alloc.x2 = (int) available_w; + } + + if (priv->y_align == CLUTTER_BIN_ALIGNMENT_FILL) + { + child_alloc.y1 = (int) 0; + child_alloc.y2 = (int) available_h; + } + + /* if we are filling horizontally and vertically then we + * can break here because we already have a full allocation + */ + if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FILL && + priv->y_align == CLUTTER_BIN_ALIGNMENT_FILL) + { + clutter_actor_allocate (child, &child_alloc, flags); + break; + } + + request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH; + g_object_get (G_OBJECT (child), "request-mode", &request, NULL); + if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) + { + gfloat min_width, nat_width; + gfloat min_height, nat_height; + + clutter_actor_get_preferred_width (child, available_h, + &min_width, + &nat_width); + child_width = CLAMP (nat_width, min_width, available_w); + + clutter_actor_get_preferred_height (child, child_width, + &min_height, + &nat_height); + child_height = CLAMP (nat_height, min_height, available_h); + } + else if (request == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) + { + gfloat min_width, nat_width; + gfloat min_height, nat_height; + + clutter_actor_get_preferred_height (child, available_w, + &min_height, + &nat_height); + child_height = CLAMP (nat_height, min_height, available_h); + + clutter_actor_get_preferred_width (child, child_height, + &min_width, + &nat_width); + child_width = CLAMP (nat_width, min_width, available_w); + } + + if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FIXED) + { + child_alloc.x1 = (int) clutter_actor_get_x (child); + child_alloc.x2 = (int) child_alloc.x1 + child_width; + } + else + { + gdouble x_align = get_bin_alignment_factor (priv->x_align); + + if (priv->x_align != CLUTTER_BIN_ALIGNMENT_FILL) + { + child_alloc.x1 = (int) ((available_w - child_width) * x_align); + child_alloc.x2 = (int) child_alloc.x1 + child_width; + } + } + + if (priv->y_align == CLUTTER_BIN_ALIGNMENT_FIXED) + { + child_alloc.y1 = (int) clutter_actor_get_y (child); + child_alloc.y2 = (int) child_alloc.y1 + child_height; + } + else + { + gdouble y_align = get_bin_alignment_factor (priv->y_align); + + if (priv->y_align != CLUTTER_BIN_ALIGNMENT_FILL) + { + child_alloc.y1 = (int) ((available_h - child_height) * y_align); + child_alloc.y2 = (int) child_alloc.y1 + child_height; + } + } + + clutter_actor_allocate (child, &child_alloc, flags); + } + + g_list_free (children); +} + +static void +clutter_bin_layout_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_X_ALIGN: + set_x_align (CLUTTER_BIN_LAYOUT (gobject), + g_value_get_enum (value)); + break; + + case PROP_Y_ALIGN: + set_y_align (CLUTTER_BIN_LAYOUT (gobject), + g_value_get_enum (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_bin_layout_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterBinLayoutPrivate *priv; + + priv = CLUTTER_BIN_LAYOUT (gobject)->priv; + + switch (prop_id) + { + case PROP_X_ALIGN: + g_value_set_enum (value, priv->x_align); + break; + + case PROP_Y_ALIGN: + g_value_set_enum (value, priv->y_align); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterLayoutManagerClass *layout_class = + CLUTTER_LAYOUT_MANAGER_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (ClutterBinLayoutPrivate)); + + gobject_class->set_property = clutter_bin_layout_set_property; + gobject_class->get_property = clutter_bin_layout_get_property; + + pspec = g_param_spec_enum ("x-align", + "X Align", + "Horizontal alignment for the actors " + "inside the layout manager", + CLUTTER_TYPE_BIN_ALIGNMENT, + CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec); + + pspec = g_param_spec_enum ("y-align", + "Y Align", + "Vertical alignment for the actors " + "inside the layout manager", + CLUTTER_TYPE_BIN_ALIGNMENT, + CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec); + + layout_class->get_preferred_width = + clutter_bin_layout_get_preferred_width; + layout_class->get_preferred_height = + clutter_bin_layout_get_preferred_height; + layout_class->allocate = + clutter_bin_layout_allocate; +} + +static void +clutter_bin_layout_init (ClutterBinLayout *self) +{ + self->priv = CLUTTER_BIN_LAYOUT_GET_PRIVATE (self); + + self->priv->x_align = CLUTTER_BIN_ALIGNMENT_CENTER; + self->priv->y_align = CLUTTER_BIN_ALIGNMENT_CENTER; +} + +ClutterLayoutManager * +clutter_bin_layout_new (ClutterBinAlignment x_align, + ClutterBinAlignment y_align) +{ + return g_object_new (CLUTTER_TYPE_BIN_LAYOUT, + "x-align", x_align, + "y-align", y_align, + NULL); +} + +void +clutter_bin_layout_set_alignment (ClutterBinLayout *self, + ClutterBinAlignment x_align, + ClutterBinAlignment y_align) +{ + g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + + set_x_align (self, x_align); + set_y_align (self, y_align); +} + +void +clutter_bin_layout_get_alignment (ClutterBinLayout *self, + ClutterBinAlignment *x_align, + ClutterBinAlignment *y_align) +{ + g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self)); + + if (x_align) + *x_align = self->priv->x_align; + + if (y_align) + *y_align = self->priv->y_align; +} diff --git a/clutter/clutter-bin-layout.h b/clutter/clutter-bin-layout.h new file mode 100644 index 000000000..dd360deef --- /dev/null +++ b/clutter/clutter-bin-layout.h @@ -0,0 +1,50 @@ +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_BIN_LAYOUT_H__ +#define __CLUTTER_BIN_LAYOUT_H__ + +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_BIN_LAYOUT (clutter_bin_layout_get_type ()) +#define CLUTTER_BIN_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayout)) +#define CLUTTER_IS_BIN_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYOUT)) +#define CLUTTER_BIN_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutClass)) +#define CLUTTER_IS_BIN_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BIN_LAYOUT)) +#define CLUTTER_BIN_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutClass)) + +typedef struct _ClutterBinLayout ClutterBinLayout; +typedef struct _ClutterBinLayoutPrivate ClutterBinLayoutPrivate; +typedef struct _ClutterBinLayoutClass ClutterBinLayoutClass; + +typedef enum { + CLUTTER_BIN_ALIGNMENT_FIXED, + CLUTTER_BIN_ALIGNMENT_FILL, + CLUTTER_BIN_ALIGNMENT_START, + CLUTTER_BIN_ALIGNMENT_END, + CLUTTER_BIN_ALIGNMENT_CENTER +} ClutterBinAlignment; + +struct _ClutterBinLayout +{ + ClutterLayoutManager parent_instance; + + ClutterBinLayoutPrivate *priv; +}; + +struct _ClutterBinLayoutClass +{ + ClutterLayoutManagerClass parent_class; +}; + +GType clutter_bin_layout_get_type (void) G_GNUC_CONST; + +ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment align_x, + ClutterBinAlignment align_y); + +G_END_DECLS + +#endif /* __CLUTTER_BIN_LAYOUT_H__ */ diff --git a/clutter/clutter.h b/clutter/clutter.h index 5c5edd261..ef042ff85 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -43,6 +43,7 @@ #include "clutter-behaviour-rotate.h" #include "clutter-behaviour-scale.h" #include "clutter-binding-pool.h" +#include "clutter-bin-layout.h" #include "clutter-box.h" #include "clutter-cairo-texture.h" #include "clutter-child-meta.h"