/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-bin.c: Basic container actor * * Copyright 2009 Intel Corporation. * Copyright 2009, 2010 Red Hat, Inc. * * 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-bin * @short_description: a simple container with one actor * * #StBin is a simple container capable of having only one * #ClutterActor as a child. * * #StBin inherits from #StWidget, so it is fully themable. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "st-bin.h" #include "st-enum-types.h" #include "st-private.h" typedef struct _StBinPrivate StBinPrivate; struct _StBinPrivate { ClutterActor *child; guint x_fill : 1; guint y_fill : 1; }; enum { PROP_0, PROP_CHILD, PROP_X_FILL, PROP_Y_FILL, N_PROPS }; static GParamSpec *props[N_PROPS] = { NULL, }; static void clutter_container_iface_init (ClutterContainerIface *iface); G_DEFINE_TYPE_WITH_CODE (StBin, st_bin, ST_TYPE_WIDGET, G_ADD_PRIVATE (StBin) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init)); static void st_bin_add (ClutterContainer *container, ClutterActor *actor) { st_bin_set_child (ST_BIN (container), actor); } static void st_bin_remove (ClutterContainer *container, ClutterActor *actor) { StBin *bin = ST_BIN (container); StBinPrivate *priv = st_bin_get_instance_private (bin); if (priv->child == actor) st_bin_set_child (bin, NULL); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = st_bin_add; iface->remove = st_bin_remove; } static double get_align_factor (ClutterActorAlign align) { switch (align) { case CLUTTER_ACTOR_ALIGN_CENTER: return 0.5; case CLUTTER_ACTOR_ALIGN_START: return 0.0; case CLUTTER_ACTOR_ALIGN_END: return 1.0; case CLUTTER_ACTOR_ALIGN_FILL: break; } return 0.0; } static void st_bin_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (self)); clutter_actor_set_allocation (self, box, flags); if (priv->child && clutter_actor_is_visible (priv->child)) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); ClutterActorAlign x_align = clutter_actor_get_x_align (priv->child); ClutterActorAlign y_align = clutter_actor_get_y_align (priv->child); ClutterActorBox childbox; st_theme_node_get_content_box (theme_node, box, &childbox); clutter_actor_allocate_align_fill (priv->child, &childbox, get_align_factor (x_align), get_align_factor (y_align), x_align == CLUTTER_ACTOR_ALIGN_FILL, y_align == CLUTTER_ACTOR_ALIGN_FILL, flags); } } static void st_bin_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (self)); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_adjust_for_height (theme_node, &for_height); if (priv->child == NULL || !clutter_actor_is_visible (priv->child)) { if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; } else { ClutterActorAlign y_align = clutter_actor_get_y_align (priv->child); _st_actor_get_preferred_width (priv->child, for_height, y_align == CLUTTER_ACTOR_ALIGN_FILL, min_width_p, natural_width_p); } st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); } static void st_bin_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (self)); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); st_theme_node_adjust_for_width (theme_node, &for_width); if (priv->child == NULL || !clutter_actor_is_visible (priv->child)) { if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; } else { ClutterActorAlign x_align = clutter_actor_get_y_align (priv->child); _st_actor_get_preferred_height (priv->child, for_width, x_align == CLUTTER_ACTOR_ALIGN_FILL, min_height_p, natural_height_p); } st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_bin_destroy (ClutterActor *actor) { StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (actor)); if (priv->child) clutter_actor_destroy (priv->child); g_assert (priv->child == NULL); CLUTTER_ACTOR_CLASS (st_bin_parent_class)->destroy (actor); } static void st_bin_popup_menu (StWidget *widget) { StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (widget)); if (priv->child && ST_IS_WIDGET (priv->child)) st_widget_popup_menu (ST_WIDGET (priv->child)); } static gboolean st_bin_navigate_focus (StWidget *widget, ClutterActor *from, StDirectionType direction) { StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (widget)); ClutterActor *bin_actor = CLUTTER_ACTOR (widget); if (st_widget_get_can_focus (widget)) { if (from && clutter_actor_contains (bin_actor, from)) return FALSE; if (clutter_actor_is_mapped (bin_actor)) { clutter_actor_grab_key_focus (bin_actor); return TRUE; } else { return FALSE; } } else if (priv->child && ST_IS_WIDGET (priv->child)) return st_widget_navigate_focus (ST_WIDGET (priv->child), from, direction, FALSE); else return FALSE; } static void st_bin_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StBin *bin = ST_BIN (gobject); StBinPrivate *priv = st_bin_get_instance_private (bin); switch (prop_id) { case PROP_CHILD: st_bin_set_child (bin, g_value_get_object (value)); break; case PROP_X_FILL: st_bin_set_fill (bin, g_value_get_boolean (value), priv->y_fill); break; case PROP_Y_FILL: st_bin_set_fill (bin, priv->x_fill, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void st_bin_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (gobject)); switch (prop_id) { case PROP_CHILD: g_value_set_object (value, priv->child); break; case PROP_X_FILL: g_value_set_boolean (value, priv->x_fill); break; case PROP_Y_FILL: g_value_set_boolean (value, priv->y_fill); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void st_bin_class_init (StBinClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); gobject_class->set_property = st_bin_set_property; gobject_class->get_property = st_bin_get_property; actor_class->get_preferred_width = st_bin_get_preferred_width; actor_class->get_preferred_height = st_bin_get_preferred_height; actor_class->allocate = st_bin_allocate; actor_class->destroy = st_bin_destroy; widget_class->popup_menu = st_bin_popup_menu; widget_class->navigate_focus = st_bin_navigate_focus; /** * StBin:child: * * The child #ClutterActor of the #StBin container. */ props[PROP_CHILD] = g_param_spec_object ("child", "Child", "The child of the Bin", CLUTTER_TYPE_ACTOR, ST_PARAM_READWRITE); /** * StBin:x-fill: * * Whether the child should fill the horizontal allocation */ props[PROP_X_FILL] = g_param_spec_boolean ("x-fill", "X Fill", "Whether the child should fill the " "horizontal allocation", FALSE, ST_PARAM_READWRITE | G_PARAM_DEPRECATED); /** * StBin:y-fill: * * Whether the child should fill the vertical allocation */ props[PROP_Y_FILL] = g_param_spec_boolean ("y-fill", "Y Fill", "Whether the child should fill the " "vertical allocation", FALSE, ST_PARAM_READWRITE | G_PARAM_DEPRECATED); g_object_class_install_properties (gobject_class, N_PROPS, props); } static void st_bin_init (StBin *bin) { } /** * st_bin_new: * * Creates a new #StBin, a simple container for one child. * * Return value: the newly created #StBin actor */ StWidget * st_bin_new (void) { return g_object_new (ST_TYPE_BIN, NULL); } /** * st_bin_set_child: * @bin: a #StBin * @child: (nullable): a #ClutterActor, or %NULL * * Sets @child as the child of @bin. * * If @bin already has a child, the previous child is removed. */ void st_bin_set_child (StBin *bin, ClutterActor *child) { StBinPrivate *priv; g_return_if_fail (ST_IS_BIN (bin)); g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child)); priv = st_bin_get_instance_private (bin); if (priv->child == child) return; if (priv->child) clutter_actor_remove_child (CLUTTER_ACTOR (bin), priv->child); priv->child = NULL; if (child) { priv->child = child; clutter_actor_add_child (CLUTTER_ACTOR (bin), child); } clutter_actor_queue_relayout (CLUTTER_ACTOR (bin)); g_object_notify_by_pspec (G_OBJECT (bin), props[PROP_CHILD]); } /** * st_bin_get_child: * @bin: a #StBin * * Retrieves a pointer to the child of @bin. * * Return value: (transfer none): a #ClutterActor, or %NULL */ ClutterActor * st_bin_get_child (StBin *bin) { g_return_val_if_fail (ST_IS_BIN (bin), NULL); return ((StBinPrivate *)st_bin_get_instance_private (bin))->child; } /** * st_bin_set_fill: * @bin: a #StBin * @x_fill: %TRUE if the child should fill horizontally the @bin * @y_fill: %TRUE if the child should fill vertically the @bin * * Sets whether the child of @bin should fill out the horizontal * and/or vertical allocation of the parent */ void st_bin_set_fill (StBin *bin, gboolean x_fill, gboolean y_fill) { StBinPrivate *priv; g_return_if_fail (ST_IS_BIN (bin)); priv = st_bin_get_instance_private (bin); g_object_freeze_notify (G_OBJECT (bin)); if (priv->x_fill != x_fill) { priv->x_fill = x_fill; g_object_notify_by_pspec (G_OBJECT (bin), props[PROP_X_FILL]); } if (priv->y_fill != y_fill) { priv->y_fill = y_fill; g_object_notify_by_pspec (G_OBJECT (bin), props[PROP_Y_FILL]); } g_object_thaw_notify (G_OBJECT (bin)); } /** * st_bin_get_fill: * @bin: a #StBin * @x_fill: (out): return location for the horizontal fill, or %NULL * @y_fill: (out): return location for the vertical fill, or %NULL * * Retrieves the horizontal and vertical fill settings */ void st_bin_get_fill (StBin *bin, gboolean *x_fill, gboolean *y_fill) { StBinPrivate *priv; g_return_if_fail (ST_IS_BIN (bin)); priv = st_bin_get_instance_private (bin); if (x_fill) *x_fill = priv->x_fill; if (y_fill) *y_fill = priv->y_fill; }