summaryrefslogtreecommitdiffstats
path: root/src/st/st-bin.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/st/st-bin.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/st/st-bin.c b/src/st/st-bin.c
new file mode 100644
index 0000000..e967686
--- /dev/null
+++ b/src/st/st-bin.c
@@ -0,0 +1,404 @@
+/* -*- 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * 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 <clutter/clutter.h>
+
+#include "st-bin.h"
+#include "st-enum-types.h"
+#include "st-private.h"
+
+typedef struct _StBinPrivate StBinPrivate;
+struct _StBinPrivate
+{
+ ClutterActor *child;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_CHILD,
+
+ 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)
+{
+ StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (self));
+
+ clutter_actor_set_allocation (self, box);
+
+ 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);
+ }
+}
+
+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);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD:
+ st_bin_set_child (bin, g_value_get_object (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;
+
+ 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);
+
+ 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.
+ *
+ * Returns: 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 (child)
+ {
+ ClutterActor *parent = clutter_actor_get_parent (child);
+
+ if (parent)
+ {
+ g_warning ("%s: The provided 'child' actor %p already has a "
+ "(different) parent %p and can't be made a child of %p.",
+ G_STRFUNC, child, parent, bin);
+ 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
+ *
+ * Gets the #ClutterActor child for @bin.
+ *
+ * Returns: (transfer none) (nullable): 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;
+}