gnome-shell/src/shell-generic-container.c
Jasper St. Pierre ea19790828 st-widget: Implement a proper allocate
Since an StWidget now has children, it needs to allocate those children
properly. Defer to the currently installed layout manager, like Clutter
does.

Now that we have something that allocates children in St, to prevent
double allocations, we use clutter_actor_set_allocation rather than
chaining up to StWidget::allocate.

https://bugzilla.gnome.org/show_bug.cgi?id=670034
2012-02-22 17:00:44 -05:00

373 lines
12 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/**
* SECTION:shell-generic-container
* @short_description: A container class with signals for allocation
*
* #ShellGenericContainer is mainly a workaround for the current
* lack of GObject subclassing + vfunc overrides in gjs. We
* implement the container interface, but proxy the virtual functions
* into signals, which gjs can catch.
*
* #ShellGenericContainer is an #StWidget, and automatically takes its
* borders and padding into account during size request and allocation.
*/
#include "config.h"
#include "shell-generic-container.h"
#include <clutter/clutter.h>
#include <gtk/gtk.h>
#include <girepository.h>
static void shell_generic_container_iface_init (ClutterContainerIface *iface);
G_DEFINE_TYPE_WITH_CODE(ShellGenericContainer,
shell_generic_container,
ST_TYPE_CONTAINER,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
shell_generic_container_iface_init));
struct _ShellGenericContainerPrivate {
GHashTable *skip_paint;
};
/* Signals */
enum
{
GET_PREFERRED_WIDTH,
GET_PREFERRED_HEIGHT,
ALLOCATE,
LAST_SIGNAL
};
static guint shell_generic_container_signals [LAST_SIGNAL] = { 0 };
static gpointer
shell_generic_container_allocation_ref (ShellGenericContainerAllocation *alloc)
{
alloc->_refcount++;
return alloc;
}
static void
shell_generic_container_allocation_unref (ShellGenericContainerAllocation *alloc)
{
if (--alloc->_refcount == 0)
g_slice_free (ShellGenericContainerAllocation, alloc);
}
static void
shell_generic_container_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StThemeNode *theme_node;
ClutterActorBox content_box;
clutter_actor_set_allocation (self, box, flags);
theme_node = st_widget_get_theme_node (ST_WIDGET (self));
st_theme_node_get_content_box (theme_node, box, &content_box);
g_signal_emit (G_OBJECT (self), shell_generic_container_signals[ALLOCATE], 0,
&content_box, flags);
}
static void
shell_generic_container_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
ShellGenericContainerAllocation *alloc = g_slice_new0 (ShellGenericContainerAllocation);
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
st_theme_node_adjust_for_height (theme_node, &for_height);
alloc->_refcount = 1;
g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_WIDTH], 0,
for_height, alloc);
if (min_width_p)
*min_width_p = alloc->min_size;
if (natural_width_p)
*natural_width_p = alloc->natural_size;
shell_generic_container_allocation_unref (alloc);
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
}
static void
shell_generic_container_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
ShellGenericContainerAllocation *alloc = g_slice_new0 (ShellGenericContainerAllocation);
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
st_theme_node_adjust_for_width (theme_node, &for_width);
alloc->_refcount = 1;
g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_HEIGHT], 0,
for_width, alloc);
if (min_height_p)
*min_height_p = alloc->min_size;
if (natural_height_p)
*natural_height_p = alloc->natural_size;
shell_generic_container_allocation_unref (alloc);
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static void
shell_generic_container_paint (ClutterActor *actor)
{
ShellGenericContainer *self = (ShellGenericContainer*) actor;
GList *iter, *children;
st_widget_paint_background (ST_WIDGET (actor));
children = st_container_get_children_list (ST_CONTAINER (actor));
for (iter = children; iter; iter = iter->next)
{
ClutterActor *child = iter->data;
if (g_hash_table_lookup (self->priv->skip_paint, child))
continue;
clutter_actor_paint (child);
}
}
static void
shell_generic_container_pick (ClutterActor *actor,
const ClutterColor *color)
{
ShellGenericContainer *self = (ShellGenericContainer*) actor;
GList *iter, *children;
CLUTTER_ACTOR_CLASS (shell_generic_container_parent_class)->pick (actor, color);
children = st_container_get_children_list (ST_CONTAINER (actor));
for (iter = children; iter; iter = iter->next)
{
ClutterActor *child = iter->data;
if (g_hash_table_lookup (self->priv->skip_paint, child))
continue;
clutter_actor_paint (child);
}
}
static GList *
shell_generic_container_get_focus_chain (StWidget *widget)
{
ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (widget);
GList *children, *focus_chain;
focus_chain = NULL;
for (children = st_container_get_children_list (ST_CONTAINER (widget)); children; children = children->next)
{
ClutterActor *child = children->data;
if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
!shell_generic_container_get_skip_paint (self, child))
focus_chain = g_list_prepend (focus_chain, child);
}
return g_list_reverse (focus_chain);
}
/**
* shell_generic_container_get_n_skip_paint:
* @self: A #ShellGenericContainer
*
* Returns: Number of children which will not be painted.
*/
guint
shell_generic_container_get_n_skip_paint (ShellGenericContainer *self)
{
return g_hash_table_size (self->priv->skip_paint);
}
/**
* shell_generic_container_get_skip_paint:
* @self: A #ShellGenericContainer
* @child: Child #ClutterActor
*
* Gets whether or not @actor is skipped when painting.
*
* Return value: %TRUE or %FALSE
*/
gboolean
shell_generic_container_get_skip_paint (ShellGenericContainer *self,
ClutterActor *child)
{
return g_hash_table_lookup (self->priv->skip_paint, child) != NULL;
}
/**
* shell_generic_container_set_skip_paint:
* @self: A #ShellGenericContainer
* @child: Child #ClutterActor
* @skip: %TRUE if we should skip painting
*
* Set whether or not we should skip painting @actor. Workaround for
* lack of gjs ability to override _paint vfunc.
*/
void
shell_generic_container_set_skip_paint (ShellGenericContainer *self,
ClutterActor *child,
gboolean skip)
{
gboolean currently_skipping;
currently_skipping = g_hash_table_lookup (self->priv->skip_paint, child) != NULL;
if (!!skip == currently_skipping)
return;
if (!skip)
g_hash_table_remove (self->priv->skip_paint, child);
else
g_hash_table_insert (self->priv->skip_paint, child, child);
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
}
static void
shell_generic_container_finalize (GObject *object)
{
ShellGenericContainer *self = (ShellGenericContainer*) object;
g_hash_table_destroy (self->priv->skip_paint);
G_OBJECT_CLASS (shell_generic_container_parent_class)->finalize (object);
}
static void
shell_generic_container_class_init (ShellGenericContainerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
gobject_class->finalize = shell_generic_container_finalize;
actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
actor_class->allocate = shell_generic_container_allocate;
actor_class->paint = shell_generic_container_paint;
actor_class->pick = shell_generic_container_pick;
widget_class->get_focus_chain = shell_generic_container_get_focus_chain;
/**
* ShellGenericContainer::get-preferred-width:
* @self: the #ShellGenericContainer
* @for_height: as in clutter_actor_get_preferred_width()
* @alloc: a #ShellGenericContainerAllocation to be filled in
*
* Emitted when clutter_actor_get_preferred_width() is called
* on @self. You should fill in the fields of @alloc with the
* your minimum and natural widths. #ShellGenericContainer
* will deal with taking its borders and padding into account
* for you.
*
* @alloc's fields are initialized to 0, so unless you have a fixed
* width specified (via #ClutterActor:width or CSS), you must
* connect to this signal and fill in the values.
*/
shell_generic_container_signals[GET_PREFERRED_WIDTH] =
g_signal_new ("get-preferred-width",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
/**
* ShellGenericContainer::get-preferred-height:
* @self: the #ShellGenericContainer
* @for_width: as in clutter_actor_get_preferred_height()
* @alloc: a #ShellGenericContainerAllocation to be filled in
*
* Emitted when clutter_actor_get_preferred_height() is called
* on @self. You should fill in the fields of @alloc with the
* your minimum and natural heights. #ShellGenericContainer
* will deal with taking its borders and padding into account
* for you.
*
* @alloc's fields are initialized to 0, so unless you have a fixed
* height specified (via #ClutterActor:height or CSS), you must
* connect to this signal and fill in the values.
*/
shell_generic_container_signals[GET_PREFERRED_HEIGHT] =
g_signal_new ("get-preferred-height",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
/**
* ShellGenericContainer::allocate:
* @self: the #ShellGenericContainer
* @box: @self's content box
* @flags: the allocation flags.
*
* Emitted when @self is allocated, after chaining up to the parent
* allocate method.
*
* Note that @box is @self's content box (qv
* st_theme_node_get_content_box()), NOT its allocation.
*/
shell_generic_container_signals[ALLOCATE] =
g_signal_new ("allocate",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR_BOX, CLUTTER_TYPE_ALLOCATION_FLAGS);
g_type_class_add_private (gobject_class, sizeof (ShellGenericContainerPrivate));
}
static void
shell_generic_container_actor_removed (ClutterContainer *container,
ClutterActor *actor)
{
ShellGenericContainerPrivate *priv = SHELL_GENERIC_CONTAINER (container)->priv;
g_hash_table_remove (priv->skip_paint, actor);
}
static void
shell_generic_container_iface_init (ClutterContainerIface *iface)
{
iface->actor_removed = shell_generic_container_actor_removed;
}
static void
shell_generic_container_init (ShellGenericContainer *area)
{
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_GENERIC_CONTAINER,
ShellGenericContainerPrivate);
area->priv->skip_paint = g_hash_table_new (NULL, NULL);
}
GType
shell_generic_container_allocation_get_type (void)
{
static GType gtype = G_TYPE_INVALID;
if (gtype == G_TYPE_INVALID)
{
gtype = g_boxed_type_register_static ("ShellGenericContainerAllocation",
(GBoxedCopyFunc)shell_generic_container_allocation_ref,
(GBoxedFreeFunc)shell_generic_container_allocation_unref);
}
return gtype;
}