diff --git a/src/Makefile-st.am b/src/Makefile-st.am index c069c7199..d0f4877e9 100644 --- a/src/Makefile-st.am +++ b/src/Makefile-st.am @@ -67,6 +67,8 @@ st-enum-types.c: stamp-st-enum-types.h st/st-enum-types.c.in st_source_h = \ st/st-adjustment.h \ st/st-bin.h \ + st/st-box-layout.h \ + st/st-box-layout-child.h \ st/st-button.h \ st/st-private.h \ st/st-stylable.h \ @@ -86,6 +88,8 @@ st_source_h = \ st_source_c = \ st/st-adjustment.c \ st/st-bin.c \ + st/st-box-layout.c \ + st/st-box-layout-child.c \ st/st-button.c \ st/st-private.c \ st/st-scrollable.c \ diff --git a/src/st/st-box-layout-child.c b/src/st/st-box-layout-child.c new file mode 100644 index 000000000..f1d995321 --- /dev/null +++ b/src/st/st-box-layout-child.c @@ -0,0 +1,189 @@ +/* + * st-box-layout-child.c: box layout child actor + * + * Copyright 2009 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, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Written by: Thomas Wood + */ + +/** + * SECTION:st-box-layout-child + * @short_description: meta data associated with a #StBoxLayout child. + * + * #StBoxLayoutChild is a #ClutterChildMeta implementation that stores the + * child properties for children inside a #StBoxLayout. + */ + +#include "st-box-layout-child.h" +#include "st-private.h" + +G_DEFINE_TYPE (StBoxLayoutChild, st_box_layout_child, CLUTTER_TYPE_CHILD_META) + +#define BOX_LAYOUT_CHILD_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildPrivate)) + + +enum +{ + PROP_0, + + PROP_EXPAND, + PROP_X_FILL, + PROP_Y_FILL, + PROP_X_ALIGN, + PROP_Y_ALIGN +}; + +static void +st_box_layout_child_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object); + + switch (property_id) + { + case PROP_EXPAND: + g_value_set_boolean (value, child->expand); + break; + case PROP_X_FILL: + g_value_set_boolean (value, child->x_fill); + break; + case PROP_Y_FILL: + g_value_set_boolean (value, child->y_fill); + break; + case PROP_X_ALIGN: + g_value_set_enum (value, child->x_align); + break; + case PROP_Y_ALIGN: + g_value_set_enum (value, child->y_align); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +st_box_layout_child_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object); + StBoxLayout *box = ST_BOX_LAYOUT (CLUTTER_CHILD_META (object)->container); + + switch (property_id) + { + case PROP_EXPAND: + child->expand = g_value_get_boolean (value); + break; + case PROP_X_FILL: + child->x_fill = g_value_get_boolean (value); + break; + case PROP_Y_FILL: + child->y_fill = g_value_get_boolean (value); + break; + case PROP_X_ALIGN: + child->x_align = g_value_get_enum (value); + break; + case PROP_Y_ALIGN: + child->y_align = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } + + clutter_actor_queue_relayout ((ClutterActor*) box); +} + +static void +st_box_layout_child_dispose (GObject *object) +{ + G_OBJECT_CLASS (st_box_layout_child_parent_class)->dispose (object); +} + +static void +st_box_layout_child_finalize (GObject *object) +{ + G_OBJECT_CLASS (st_box_layout_child_parent_class)->finalize (object); +} + +static void +st_box_layout_child_class_init (StBoxLayoutChildClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->get_property = st_box_layout_child_get_property; + object_class->set_property = st_box_layout_child_set_property; + object_class->dispose = st_box_layout_child_dispose; + object_class->finalize = st_box_layout_child_finalize; + + + pspec = g_param_spec_boolean ("expand", "Expand", + "Allocate the child extra space", + FALSE, + ST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_EXPAND, pspec); + + pspec = g_param_spec_boolean ("x-fill", "x-fill", + "Whether the child should receive priority " + "when the container is allocating spare space " + "on the horizontal axis", + TRUE, + ST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_X_FILL, pspec); + + pspec = g_param_spec_boolean ("y-fill", "y-fill", + "Whether the child should receive priority " + "when the container is allocating spare space " + "on the vertical axis", + TRUE, + ST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_Y_FILL, pspec); + + pspec = g_param_spec_enum ("x-align", + "X Alignment", + "X alignment of the widget within the cell", + ST_TYPE_ALIGN, + ST_ALIGN_MIDDLE, + ST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_X_ALIGN, pspec); + + pspec = g_param_spec_enum ("y-align", + "Y Alignment", + "Y alignment of the widget within the cell", + ST_TYPE_ALIGN, + ST_ALIGN_MIDDLE, + ST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_Y_ALIGN, pspec); +} + +static void +st_box_layout_child_init (StBoxLayoutChild *self) +{ + self->expand = FALSE; + + self->x_fill = TRUE; + self->y_fill = TRUE; + + self->x_align = ST_ALIGN_MIDDLE; + self->y_align = ST_ALIGN_MIDDLE; +} + diff --git a/src/st/st-box-layout-child.h b/src/st/st-box-layout-child.h new file mode 100644 index 000000000..ec1d19967 --- /dev/null +++ b/src/st/st-box-layout-child.h @@ -0,0 +1,84 @@ +/* + * st-box-layout-child.h: box layout child actor + * + * Copyright 2009 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, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Written by: Thomas Wood + */ + +#ifndef _ST_BOX_LAYOUT_CHILD_H +#define _ST_BOX_LAYOUT_CHILD_H + +#include +#include "st-enum-types.h" +#include "st-box-layout.h" + +G_BEGIN_DECLS + +#define ST_TYPE_BOX_LAYOUT_CHILD st_box_layout_child_get_type() + +#define ST_BOX_LAYOUT_CHILD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChild)) + +#define ST_BOX_LAYOUT_CHILD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildClass)) + +#define ST_IS_BOX_LAYOUT_CHILD(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + ST_TYPE_BOX_LAYOUT_CHILD)) + +#define ST_IS_BOX_LAYOUT_CHILD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + ST_TYPE_BOX_LAYOUT_CHILD)) + +#define ST_BOX_LAYOUT_CHILD_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildClass)) + +typedef struct _StBoxLayoutChild StBoxLayoutChild; +typedef struct _StBoxLayoutChildClass StBoxLayoutChildClass; +typedef struct _StBoxLayoutChildPrivate StBoxLayoutChildPrivate; + +/** + * StBoxLayoutChild: + * + * The contents of this structure are private and should only be accessed + * through the public API. + */ +struct _StBoxLayoutChild +{ + /*< private >*/ + ClutterChildMeta parent; + + gboolean expand; + gboolean x_fill : 1; + gboolean y_fill : 1; + StAlign x_align; + StAlign y_align; +}; + +struct _StBoxLayoutChildClass +{ + ClutterChildMetaClass parent_class; +}; + +GType st_box_layout_child_get_type (void); + +G_END_DECLS + +#endif /* _ST_BOX_LAYOUT_CHILD_H */ diff --git a/src/st/st-box-layout.c b/src/st/st-box-layout.c new file mode 100644 index 000000000..f6fb76fe7 --- /dev/null +++ b/src/st/st-box-layout.c @@ -0,0 +1,1038 @@ +/* + * st-box-layout.h: box layout actor + * + * Copyright 2009 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, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Written by: Thomas Wood + * + */ + +/** + * SECTION:st-box-layout + * @short_description: a layout container arranging children in a single line + * + * The #StBoxLayout arranges its children along a single line, where each + * child can be allocated either its preferred size or larger if the expand + * option is set. If the fill option is set, the actor will be allocated more + * than its requested size. If the fill option is not set, but the expand option + * is enabled, then the position of the actor within the available space can + * be determined by the alignment child property. + * + */ + +#include "st-box-layout.h" + +#include "st-private.h" +#include "st-scrollable.h" +#include "st-box-layout-child.h" + + + +static void st_box_container_iface_init (ClutterContainerIface *iface); +static void st_box_scrollable_interface_init (StScrollableInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (StBoxLayout, st_box_layout, ST_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, + st_box_container_iface_init) + G_IMPLEMENT_INTERFACE (ST_TYPE_SCROLLABLE, + st_box_scrollable_interface_init)); + +#define BOX_LAYOUT_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_BOX_LAYOUT, StBoxLayoutPrivate)) + +enum { + PROP_0, + + PROP_VERTICAL, + PROP_PACK_START, + PROP_SPACING, + + PROP_HADJUST, + PROP_VADJUST +}; + +struct _StBoxLayoutPrivate +{ + GList *children; + + guint spacing; + + guint is_vertical : 1; + guint is_pack_start : 1; + + StAdjustment *hadjustment; + StAdjustment *vadjustment; +}; + +/* + * StScrollable Interface Implementation + */ +static void +adjustment_value_notify_cb (StAdjustment *adjustment, + GParamSpec *pspec, + StBoxLayout *box) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (box)); +} + +static void +scrollable_set_adjustments (StScrollable *scrollable, + StAdjustment *hadjustment, + StAdjustment *vadjustment) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (scrollable)->priv; + + if (hadjustment != priv->hadjustment) + { + if (priv->hadjustment) + { + g_signal_handlers_disconnect_by_func (priv->hadjustment, + adjustment_value_notify_cb, + scrollable); + g_object_unref (priv->hadjustment); + } + + if (hadjustment) + { + g_object_ref (hadjustment); + g_signal_connect (hadjustment, "notify::value", + G_CALLBACK (adjustment_value_notify_cb), + scrollable); + } + + priv->hadjustment = hadjustment; + } + + if (vadjustment != priv->vadjustment) + { + if (priv->vadjustment) + { + g_signal_handlers_disconnect_by_func (priv->vadjustment, + adjustment_value_notify_cb, + scrollable); + g_object_unref (priv->vadjustment); + } + + if (vadjustment) + { + g_object_ref (vadjustment); + g_signal_connect (vadjustment, "notify::value", + G_CALLBACK (adjustment_value_notify_cb), + scrollable); + } + + priv->vadjustment = vadjustment; + } +} + +static void +scrollable_get_adjustments (StScrollable *scrollable, + StAdjustment **hadjustment, + StAdjustment **vadjustment) +{ + StBoxLayoutPrivate *priv; + ClutterActor *actor, *stage; + + priv = (ST_BOX_LAYOUT (scrollable))->priv; + + actor = CLUTTER_ACTOR (scrollable); + stage = clutter_actor_get_stage (actor); + if (G_UNLIKELY (stage == NULL)) + stage = clutter_stage_get_default (); + + if (hadjustment) + { + if (priv->hadjustment) + *hadjustment = priv->hadjustment; + else + { + StAdjustment *adjustment; + gdouble width, stage_width, increment; + + width = clutter_actor_get_width (actor); + stage_width = clutter_actor_get_width (stage); + increment = MAX (1.0, MIN (stage_width, width)); + + adjustment = st_adjustment_new (0, + 0, + width, + 1.0, + increment, + increment); + + scrollable_set_adjustments (scrollable, + adjustment, + priv->vadjustment); + + *hadjustment = adjustment; + } + } + + if (vadjustment) + { + if (priv->vadjustment) + *vadjustment = priv->vadjustment; + else + { + StAdjustment *adjustment; + gdouble height, stage_height, increment; + + height = clutter_actor_get_height (actor); + stage_height = clutter_actor_get_height (stage); + increment = MAX (1.0, MIN (stage_height, height)); + + adjustment = st_adjustment_new (0, + 0, + height, + 1.0, + increment, + increment); + + scrollable_set_adjustments (scrollable, + priv->hadjustment, + adjustment); + + *vadjustment = adjustment; + } + } +} + + + +static void +st_box_scrollable_interface_init (StScrollableInterface *iface) +{ + iface->set_adjustments = scrollable_set_adjustments; + iface->get_adjustments = scrollable_get_adjustments; +} + +/* + * ClutterContainer Implementation + */ +static void +st_box_container_add_actor (ClutterContainer *container, + ClutterActor *actor) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (container)->priv; + + clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); + + priv->children = g_list_append (priv->children, actor); + + g_signal_emit_by_name (container, "actor-added", actor); +} + +static void +st_box_container_remove_actor (ClutterContainer *container, + ClutterActor *actor) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (container)->priv; + + GList *item = NULL; + + item = g_list_find (priv->children, actor); + + if (item == NULL) + { + g_warning ("Actor of type '%s' is not a child of container of type '%s'", + g_type_name (G_OBJECT_TYPE (actor)), + g_type_name (G_OBJECT_TYPE (container))); + return; + } + + g_object_ref (actor); + + priv->children = g_list_delete_link (priv->children, item); + clutter_actor_unparent (actor); + + g_signal_emit_by_name (container, "actor-removed", actor); + + g_object_unref (actor); + + clutter_actor_queue_relayout ((ClutterActor*) container); +} + +static void +st_box_container_foreach (ClutterContainer *container, + ClutterCallback callback, + gpointer callback_data) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (container)->priv; + + g_list_foreach (priv->children, (GFunc) callback, callback_data); +} + +static void +st_box_container_lower (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling) +{ + /* XXX: not yet implemented */ + g_warning ("%s() not yet implemented", __FUNCTION__); +} + +static void +st_box_container_raise (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling) +{ + /* XXX: not yet implemented */ + g_warning ("%s() not yet implemented", __FUNCTION__); +} + +static void +st_box_container_sort_depth_order (ClutterContainer *container) +{ + /* XXX: not yet implemented */ + g_warning ("%s() not yet implemented", __FUNCTION__); +} + +static void +st_box_container_iface_init (ClutterContainerIface *iface) +{ + iface->add = st_box_container_add_actor; + iface->remove = st_box_container_remove_actor; + iface->foreach = st_box_container_foreach; + iface->lower = st_box_container_lower; + iface->raise = st_box_container_raise; + iface->sort_depth_order = st_box_container_sort_depth_order; + + iface->child_meta_type = ST_TYPE_BOX_LAYOUT_CHILD; +} + + +static void +st_box_layout_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv; + StAdjustment *adjustment; + + switch (property_id) + { + case PROP_VERTICAL: + g_value_set_boolean (value, priv->is_vertical); + break; + + case PROP_PACK_START: + g_value_set_boolean (value, priv->is_pack_start); + break; + + case PROP_SPACING: + g_value_set_uint (value, priv->spacing); + break; + + case PROP_HADJUST: + scrollable_get_adjustments (ST_SCROLLABLE (object), &adjustment, NULL); + g_value_set_object (value, adjustment); + break; + + case PROP_VADJUST: + scrollable_get_adjustments (ST_SCROLLABLE (object), NULL, &adjustment); + g_value_set_object (value, adjustment); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +st_box_layout_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + StBoxLayout *box = ST_BOX_LAYOUT (object); + + switch (property_id) + { + case PROP_VERTICAL: + st_box_layout_set_vertical (box, g_value_get_boolean (value)); + break; + + case PROP_PACK_START: + st_box_layout_set_pack_start (box, g_value_get_boolean (value)); + break; + + case PROP_SPACING: + st_box_layout_set_spacing (box, g_value_get_uint (value)); + break; + + case PROP_HADJUST: + scrollable_set_adjustments (ST_SCROLLABLE (object), + g_value_get_object (value), + box->priv->vadjustment); + break; + + case PROP_VADJUST: + scrollable_set_adjustments (ST_SCROLLABLE (object), + box->priv->hadjustment, + g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +st_box_layout_dispose (GObject *object) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv; + + while (priv->children) + { + clutter_actor_unparent (CLUTTER_ACTOR (priv->children->data)); + + priv->children = g_list_delete_link (priv->children, priv->children); + } + + if (priv->hadjustment) + { + g_object_unref (priv->hadjustment); + priv->hadjustment = NULL; + } + + if (priv->vadjustment) + { + g_object_unref (priv->vadjustment); + priv->vadjustment = NULL; + } + + G_OBJECT_CLASS (st_box_layout_parent_class)->dispose (object); +} + +static void +st_box_layout_get_preferred_width (ClutterActor *actor, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; + StPadding padding = { 0, }; + gint n_children = 0; + GList *l; + + st_widget_get_padding (ST_WIDGET (actor), &padding); + + if (min_width_p) + *min_width_p = padding.left + padding.right; + + if (natural_width_p) + *natural_width_p = padding.left + padding.right; + + if (priv->children == NULL) + return; + + for (l = priv->children; l; l = g_list_next (l)) + { + gfloat child_min = 0, child_nat = 0; + + if (!CLUTTER_ACTOR_IS_VISIBLE ((ClutterActor*) l->data)) + continue; + + n_children++; + + clutter_actor_get_preferred_width ((ClutterActor*) l->data, + (!priv->is_vertical) ? for_height : -1, + &child_min, + &child_nat); + + if (priv->is_vertical) + { + if (min_width_p) + *min_width_p = MAX (child_min, *min_width_p); + + if (natural_width_p) + *natural_width_p = MAX (child_nat, *natural_width_p); + } + else + { + if (min_width_p) + *min_width_p += child_min; + + if (natural_width_p) + *natural_width_p += child_nat; + + } + } + + + if (!priv->is_vertical && n_children > 1) + { + if (min_width_p) + *min_width_p += priv->spacing * (n_children - 1); + + if (natural_width_p) + *natural_width_p += priv->spacing * (n_children - 1); + } +} + +static void +st_box_layout_get_preferred_height (ClutterActor *actor, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; + StPadding padding = { 0, }; + gint n_children = 0; + GList *l; + + st_widget_get_padding (ST_WIDGET (actor), &padding); + + if (min_height_p) + *min_height_p = padding.top + padding.bottom; + + if (natural_height_p) + *natural_height_p = padding.top + padding.bottom; + + if (priv->children == NULL) + return; + + for (l = priv->children; l; l = g_list_next (l)) + { + gfloat child_min = 0, child_nat = 0; + + if (!CLUTTER_ACTOR_IS_VISIBLE ((ClutterActor*) l->data)) + continue; + + n_children++; + + clutter_actor_get_preferred_height ((ClutterActor*) l->data, + (priv->is_vertical) ? for_width : -1, + &child_min, + &child_nat); + + if (!priv->is_vertical) + { + if (min_height_p) + *min_height_p = MAX (child_min, *min_height_p); + + if (natural_height_p) + *natural_height_p = MAX (child_nat, *natural_height_p); + } + else + { + if (min_height_p) + *min_height_p += child_min; + + if (natural_height_p) + *natural_height_p += child_nat; + } + } + + if (priv->is_vertical && n_children > 1) + { + if (min_height_p) + *min_height_p += priv->spacing * (n_children - 1); + + if (natural_height_p) + *natural_height_p += priv->spacing * (n_children - 1); + } +} + +static void +st_box_layout_allocate (ClutterActor *actor, + const ClutterActorBox *box, + ClutterAllocationFlags flags) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; + gfloat avail_width, avail_height, pref_width, pref_height; + StPadding padding = { 0, }; + gfloat position = 0; + GList *l; + gint n_expand_children, extra_space; + + CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->allocate (actor, box, + flags); + + if (priv->children == NULL) + return; + + st_widget_get_padding (ST_WIDGET (actor), &padding); + avail_width = box->x2 - box->x1 + - padding.left + - padding.right; + avail_height = box->y2 - box->y1 + - padding.top + - padding.bottom; + + st_box_layout_get_preferred_height (actor, avail_width, NULL, + &pref_height); + st_box_layout_get_preferred_width (actor, avail_height, NULL, + &pref_width); + + /* update adjustments for scrolling */ + if (priv->vadjustment) + { + gdouble prev_value; + + g_object_set (G_OBJECT (priv->vadjustment), + "lower", 0.0, + "upper", pref_height, + "page-size", avail_height, + "step-increment", avail_height / 6, + "page-increment", avail_height, + NULL); + + prev_value = st_adjustment_get_value (priv->vadjustment); + st_adjustment_set_value (priv->vadjustment, prev_value); + } + + if (priv->hadjustment) + { + gdouble prev_value; + + g_object_set (G_OBJECT (priv->hadjustment), + "lower", 0.0, + "upper", pref_width, + "page-size", avail_width, + "step-increment", avail_width / 6, + "page-increment", avail_width, + NULL); + + prev_value = st_adjustment_get_value (priv->hadjustment); + st_adjustment_set_value (priv->hadjustment, prev_value); + } + + /* count the number of children with expand set to TRUE */ + n_expand_children = 0; + for (l = priv->children; l; l = l->next) + { + gboolean expand; + clutter_container_child_get ((ClutterContainer *) actor, + (ClutterActor*) l->data, + "expand", &expand, + NULL); + if (expand) + n_expand_children++; + } + + if (n_expand_children == 0) + { + extra_space = 0; + n_expand_children = 1; + } + else + { + if (priv->is_vertical) + extra_space = (avail_height - pref_height) / n_expand_children; + else + extra_space = (avail_width - pref_width) / n_expand_children; + + /* don't shrink anything */ + if (extra_space < 0) + extra_space = 0; + } + + if (priv->is_vertical) + position = padding.top; + else + position = padding.left; + + if (priv->is_pack_start) + l = g_list_last (priv->children); + else + l = priv->children; + + for (l = (priv->is_pack_start) ? g_list_last (priv->children) : priv->children; + l; + l = (priv->is_pack_start) ? l->prev : l->next) + { + ClutterActor *child = (ClutterActor*) l->data; + ClutterActorBox child_box; + gfloat child_nat; + gboolean xfill, yfill, expand; + StAlign xalign, yalign; + + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + clutter_container_child_get ((ClutterContainer*) actor, child, + "x-fill", &xfill, + "y-fill", &yfill, + "x-align", &xalign, + "y-align", &yalign, + "expand", &expand, + NULL); + + if (priv->is_vertical) + { + clutter_actor_get_preferred_height (child, avail_width, + NULL, &child_nat); + + child_box.y1 = position; + if (expand) + child_box.y2 = position + child_nat + extra_space; + else + child_box.y2 = position + child_nat; + child_box.x1 = padding.left; + child_box.x2 = avail_width; + + _st_allocate_fill (child, &child_box, xalign, yalign, xfill, yfill); + clutter_actor_allocate (child, &child_box, flags); + + if (expand) + position += (child_nat + priv->spacing + extra_space); + else + position += (child_nat + priv->spacing); + } + else + { + + clutter_actor_get_preferred_width (child, avail_height, + NULL, &child_nat); + + child_box.x1 = position; + + if (expand) + child_box.x2 = position + child_nat + extra_space; + else + child_box.x2 = position + child_nat; + + child_box.y1 = padding.top; + child_box.y2 = avail_height; + _st_allocate_fill (child, &child_box, xalign, yalign, xfill, yfill); + clutter_actor_allocate (child, &child_box, flags); + + if (expand) + position += (child_nat + priv->spacing + extra_space); + else + position += (child_nat + priv->spacing); + } + } +} + +static void +st_box_layout_apply_transform (ClutterActor *a, + CoglMatrix *m) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (a)->priv; + gdouble x, y; + + CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->apply_transform (a, m); + + if (priv->hadjustment) + x = st_adjustment_get_value (priv->hadjustment); + else + x = 0; + + if (priv->vadjustment) + y = st_adjustment_get_value (priv->vadjustment); + else + y = 0; + + cogl_matrix_translate (m, (int) -x, (int) -y, 0); +} + + +static void +st_box_layout_paint (ClutterActor *actor) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; + GList *l; + gdouble x, y; + ClutterActorBox child_b; + ClutterActorBox box_b; + + CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->paint (actor); + + if (priv->children == NULL) + return; + + if (priv->hadjustment) + x = st_adjustment_get_value (priv->hadjustment); + else + x = 0; + + if (priv->vadjustment) + y = st_adjustment_get_value (priv->vadjustment); + else + y = 0; + + clutter_actor_get_allocation_box (actor, &box_b); + box_b.x2 = (box_b.x2 - box_b.x1) + x; + box_b.x1 = x; + box_b.y2 = (box_b.y2 - box_b.y1) + y; + box_b.y1 = y; + + for (l = priv->children; l; l = g_list_next (l)) + { + ClutterActor *child = (ClutterActor*) l->data; + + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + clutter_actor_get_allocation_box (child, &child_b); + + if ((child_b.x1 < box_b.x2) && + (child_b.x2 > box_b.x1) && + (child_b.y1 < box_b.y2) && + (child_b.y2 > box_b.y1)) + { + clutter_actor_paint (child); + } + } +} + +static void +st_box_layout_pick (ClutterActor *actor, + const ClutterColor *color) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; + GList *l; + gdouble x, y; + ClutterActorBox child_b; + ClutterActorBox box_b; + + CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->pick (actor, color); + + if (priv->children == NULL) + return; + + if (priv->hadjustment) + x = st_adjustment_get_value (priv->hadjustment); + else + x = 0; + + if (priv->vadjustment) + y = st_adjustment_get_value (priv->vadjustment); + else + y = 0; + + clutter_actor_get_allocation_box (actor, &box_b); + box_b.x2 = (box_b.x2 - box_b.x1) + x; + box_b.x1 = x; + box_b.y2 = (box_b.y2 - box_b.y1) + y; + box_b.y1 = y; + + for (l = priv->children; l; l = g_list_next (l)) + { + ClutterActor *child = (ClutterActor*) l->data; + + if (!CLUTTER_ACTOR_IS_VISIBLE (child)) + continue; + + clutter_actor_get_allocation_box (child, &child_b); + + if ((child_b.x1 < box_b.x2) + && (child_b.x2 > box_b.x1) + && (child_b.y1 < box_b.y2) + && (child_b.y2 > box_b.y1)) + { + clutter_actor_paint (child); + } + } +} + +static void +st_box_layout_class_init (StBoxLayoutClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (StBoxLayoutPrivate)); + + object_class->get_property = st_box_layout_get_property; + object_class->set_property = st_box_layout_set_property; + object_class->dispose = st_box_layout_dispose; + + actor_class->allocate = st_box_layout_allocate; + actor_class->get_preferred_width = st_box_layout_get_preferred_width; + actor_class->get_preferred_height = st_box_layout_get_preferred_height; + actor_class->apply_transform = st_box_layout_apply_transform; + + actor_class->paint = st_box_layout_paint; + actor_class->pick = st_box_layout_pick; + + pspec = g_param_spec_boolean ("vertical", + "Vertical", + "Whether the layout should be vertical, rather" + "than horizontal", + FALSE, + ST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_VERTICAL, pspec); + + pspec = g_param_spec_boolean ("pack-start", + "Pack Start", + "Whether to pack items at the start of the box", + FALSE, + ST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_PACK_START, pspec); + + pspec = g_param_spec_uint ("spacing", + "Spacing", + "Spacing between children", + 0, G_MAXUINT, 0, + ST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_SPACING, pspec); + + /* StScrollable properties */ + g_object_class_override_property (object_class, + PROP_HADJUST, + "hadjustment"); + + g_object_class_override_property (object_class, + PROP_VADJUST, + "vadjustment"); + +} + +static void +st_box_layout_init (StBoxLayout *self) +{ + self->priv = BOX_LAYOUT_PRIVATE (self); +} + +/** + * st_box_layout_new: + * + * Create a new #StBoxLayout. + * + * Returns: a newly allocated #StBoxLayout + */ +StWidget * +st_box_layout_new (void) +{ + return g_object_new (ST_TYPE_BOX_LAYOUT, NULL); +} + +/** + * st_box_layout_set_vertical: + * @box: A #StBoxLayout + * @vertical: #TRUE if the layout should be vertical + * + * Set the value of the #StBoxLayout::vertical property + * + */ +void +st_box_layout_set_vertical (StBoxLayout *box, + gboolean vertical) +{ + g_return_if_fail (ST_IS_BOX_LAYOUT (box)); + + if (box->priv->is_vertical != vertical) + { + box->priv->is_vertical = vertical; + clutter_actor_queue_relayout ((ClutterActor*) box); + + g_object_notify (G_OBJECT (box), "vertical"); + } +} + +/** + * st_box_layout_get_vertical: + * @box: A #StBoxLayout + * + * Get the value of the #StBoxLayout::vertical property. + * + * Returns: #TRUE if the layout is vertical + */ +gboolean +st_box_layout_get_vertical (StBoxLayout *box) +{ + g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE); + + return box->priv->is_vertical; +} + +/** + * st_box_layout_set_pack_start: + * @box: A #StBoxLayout + * @pack_start: #TRUE if the layout should use pack-start + * + * Set the value of the #StBoxLayout::pack-start property. + * + */ +void +st_box_layout_set_pack_start (StBoxLayout *box, + gboolean pack_start) +{ + g_return_if_fail (ST_IS_BOX_LAYOUT (box)); + + if (box->priv->is_pack_start != pack_start) + { + box->priv->is_pack_start = pack_start; + clutter_actor_queue_relayout ((ClutterActor*) box); + + g_object_notify (G_OBJECT (box), "pack-start"); + } +} + +/** + * st_box_layout_get_pack_start: + * @box: A #StBoxLayout + * + * Get the value of the #StBoxLayout::pack-start property. + * + * Returns: #TRUE if pack-start is enabled + */ +gboolean +st_box_layout_get_pack_start (StBoxLayout *box) +{ + g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE); + + return box->priv->is_pack_start; +} + +/** + * st_box_layout_set_spacing: + * @box: A #StBoxLayout + * @spacing: the spacing value + * + * Set the amount of spacing between children in pixels + * + */ +void +st_box_layout_set_spacing (StBoxLayout *box, + guint spacing) +{ + StBoxLayoutPrivate *priv; + + g_return_if_fail (ST_IS_BOX_LAYOUT (box)); + + priv = box->priv; + + if (priv->spacing != spacing) + { + priv->spacing = spacing; + + clutter_actor_queue_relayout (CLUTTER_ACTOR (box)); + + g_object_notify (G_OBJECT (box), "spacing"); + } +} + +/** + * st_box_layout_get_spacing: + * @box: A #StBoxLayout + * + * Get the spacing between children in pixels + * + * Returns: the spacing value + */ +guint +st_box_layout_get_spacing (StBoxLayout *box) +{ + g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), 0); + + return box->priv->spacing; +} diff --git a/src/st/st-box-layout.h b/src/st/st-box-layout.h new file mode 100644 index 000000000..7a1217476 --- /dev/null +++ b/src/st/st-box-layout.h @@ -0,0 +1,97 @@ +/* + * st-box-layout.h: box layout actor + * + * Copyright 2009 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, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Written by: Thomas Wood + * + */ + +#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) +#error "Only can be included directly.h" +#endif + +#ifndef _ST_BOX_LAYOUT_H +#define _ST_BOX_LAYOUT_H + +#include + +G_BEGIN_DECLS + +#define ST_TYPE_BOX_LAYOUT st_box_layout_get_type() + +#define ST_BOX_LAYOUT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + ST_TYPE_BOX_LAYOUT, StBoxLayout)) + +#define ST_BOX_LAYOUT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + ST_TYPE_BOX_LAYOUT, StBoxLayoutClass)) + +#define ST_IS_BOX_LAYOUT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + ST_TYPE_BOX_LAYOUT)) + +#define ST_IS_BOX_LAYOUT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + ST_TYPE_BOX_LAYOUT)) + +#define ST_BOX_LAYOUT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + ST_TYPE_BOX_LAYOUT, StBoxLayoutClass)) + +typedef struct _StBoxLayout StBoxLayout; +typedef struct _StBoxLayoutClass StBoxLayoutClass; +typedef struct _StBoxLayoutPrivate StBoxLayoutPrivate; + +/** + * StBoxLayout: + * + * The contents of this structure are private and should only be accessed + * through the public API. + */ +struct _StBoxLayout +{ + /*< private >*/ + StWidget parent; + + StBoxLayoutPrivate *priv; +}; + +struct _StBoxLayoutClass +{ + StWidgetClass parent_class; +}; + +GType st_box_layout_get_type (void); + +StWidget *st_box_layout_new (void); + +void st_box_layout_set_vertical (StBoxLayout *box, + gboolean vertical); +gboolean st_box_layout_get_vertical (StBoxLayout *box); + +void st_box_layout_set_pack_start (StBoxLayout *box, + gboolean pack_start); +gboolean st_box_layout_get_pack_start (StBoxLayout *box); + +void st_box_layout_set_spacing (StBoxLayout *box, + guint spacing); +guint st_box_layout_get_spacing (StBoxLayout *box); + +G_END_DECLS + +#endif /* _ST_BOX_LAYOUT_H */