/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-group.c: A fixed layout container based on ClutterGroup * * Copyright 2010 Florian Müllner * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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 program. If not, see . */ /** * SECTION:st-group * SECTION:clutter-group * @short_description: A fixed layout container * * A #StGroup is an Actor which contains multiple child actors positioned * relative to the #StGroup position. Other operations such as scaling, * rotating and clipping of the group will apply to the child actors. * * A #StGroup's size is defined by the size and position of its children; * it will be the smallest non-negative size that covers the right and bottom * edges of all of its children. * * Setting the size on a Group using #ClutterActor methods like * clutter_actor_set_size() will override the natural size of the Group, * however this will not affect the size of the children and they may still * be painted outside of the allocation of the group. One way to constrain * the visible area of a #StGroup to a specified allocation is to * explicitly set the size of the #StGroup and then use the * #ClutterActor:clip-to-allocation property. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "st-group.h" #include "st-enum-types.h" #include "st-private.h" G_DEFINE_TYPE (StGroup, st_group, ST_TYPE_CONTAINER); static void st_group_paint (ClutterActor *actor) { CLUTTER_ACTOR_CLASS (st_group_parent_class)->paint (actor); clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_paint), NULL); } static void st_group_pick (ClutterActor *actor, const ClutterColor *pick) { /* Chain up so we get a bounding box painted (if we are reactive) */ CLUTTER_ACTOR_CLASS (st_group_parent_class)->pick (actor, pick); clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_paint), NULL); } static void st_group_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StThemeNode *node = st_widget_get_theme_node (ST_WIDGET (actor)); gdouble min_width, natural_width; gint css_width, css_min_width, css_max_width; GList *l, *children; css_width = st_theme_node_get_width (node); css_min_width = st_theme_node_get_min_width (node); css_max_width = st_theme_node_get_max_width (node); /* We will always be at least 0 sized (ie, if all of the actors are * to the left of the origin we won't return a negative size) */ min_width = 0; natural_width = 0; children = st_container_get_children_list (ST_CONTAINER (actor)); for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; gfloat child_x, child_min, child_nat; child_x = clutter_actor_get_x (child); /* for_height is irrelevant to the fixed layout, so it's not used */ _st_actor_get_preferred_width (child, -1, FALSE, &child_min, &child_nat); /* Track the rightmost edge */ if (child_x + child_min > min_width) min_width = child_x + child_min; if (child_x + child_nat > natural_width) natural_width = child_x + child_nat; } /* The size is defined as the distance from the origin to the right-hand * edge of the rightmost actor, unless overridden with min-width */ if (min_width_p) { if (css_min_width != -1) *min_width_p = css_min_width; else *min_width_p = min_width; } if (natural_width_p) { if (css_width != -1) natural_width = css_width; *natural_width_p = MIN (natural_width, css_max_width); } } static void st_group_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StThemeNode *node = st_widget_get_theme_node (ST_WIDGET (actor)); gdouble min_height, natural_height; gint css_height, css_min_height, css_max_height; GList *l, *children; css_height = st_theme_node_get_height (node); css_min_height = st_theme_node_get_min_height (node); css_max_height = st_theme_node_get_max_height (node); /* We will always be at least 0 sized (ie, if all of the actors are * above of the origin we won't return a negative size) */ min_height = 0; natural_height = 0; children = st_container_get_children_list (ST_CONTAINER (actor)); for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; gfloat child_y, child_min, child_nat; child_y = clutter_actor_get_y (child); /* for_width is irrelevant to the fixed layout, so it's not used */ _st_actor_get_preferred_height (child, -1, FALSE, &child_min, &child_nat); /* Track the bottommost edge */ if (child_y + child_min > min_height) min_height = child_y + child_min; if (child_y + child_nat > natural_height) natural_height = child_y + child_nat; } /* The size is defined as the distance from the origin to the right-hand * edge of the rightmost actor, unless overridden with min-height */ if (min_height_p) { if (css_min_height != -1) *min_height_p = css_min_height; else *min_height_p = min_height; } if (natural_height_p) { if (css_height != -1) natural_height = css_height; *natural_height_p = MIN (natural_height, css_max_height); } } static void st_group_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { GList *l, *children; CLUTTER_ACTOR_CLASS (st_group_parent_class)->allocate (actor, box, flags); children = st_container_get_children_list (ST_CONTAINER (actor)); for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; clutter_actor_allocate_preferred_size (child, flags); } } static void st_group_show_all (ClutterActor *actor) { clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_show), NULL); clutter_actor_show (actor); } static void st_group_hide_all (ClutterActor *actor) { clutter_actor_hide (actor); clutter_container_foreach (CLUTTER_CONTAINER (actor), CLUTTER_CALLBACK (clutter_actor_hide), NULL); } /* Based on implementation from clutter-group.c */ static gboolean st_group_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { GList *l, *children; children = st_container_get_children_list (ST_CONTAINER (actor)); CLUTTER_ACTOR_CLASS (st_group_parent_class)->get_paint_volume (actor, volume); for (l = children; l != NULL; l = l->next) { ClutterActor *child = l->data; const ClutterPaintVolume *child_volume; /* This gets the paint volume of the child transformed into the * group's coordinate space... */ child_volume = clutter_actor_get_transformed_paint_volume (child, actor); if (!child_volume) return FALSE; clutter_paint_volume_union (volume, child_volume); } return TRUE; } static void st_group_class_init (StGroupClass *klass) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->get_preferred_width = st_group_get_preferred_width; actor_class->get_preferred_height = st_group_get_preferred_height; actor_class->allocate = st_group_allocate; actor_class->paint = st_group_paint; actor_class->get_paint_volume = st_group_get_paint_volume; actor_class->pick = st_group_pick; actor_class->show_all = st_group_show_all; actor_class->hide_all = st_group_hide_all; } static void st_group_init (StGroup *self) { } /** * st_group_new: * * Create a new #StGroup. * * Return value: the newly created #StGroup actor */ StWidget * st_group_new (void) { return g_object_new (ST_TYPE_GROUP, NULL); }