[StScrollBar] avoid queueing a relayout during allocation

When an StScrollView is allocated, allocating the child would
cause the adjustment values to change, which would result in
the scrollbars queueing a relayout, which isn't allowed during
allocation.

To avoid this, instead of queueing a relayout when the adjustment
changes:

 - When we have a valid allocation already, just go ahead
   and reallocate the children.
 - Otherwise do nothing immediately and wait until we get allocated

Because the 'needs_allocation' flag in ClutterActor isn't exposed,
this requires some slightly ugly code to shadow that state locally.

https://bugzilla.gnome.org/show_bug.cgi?id=611944
This commit is contained in:
Owen W. Taylor 2010-03-05 16:17:07 -05:00
parent 33dca51650
commit 524e2ca8e2

View File

@ -77,7 +77,15 @@ struct _StScrollBarPrivate
ClutterAnimation *paging_animation; ClutterAnimation *paging_animation;
gboolean vertical; guint vertical : 1;
/* We want to keep track of whether we have a currently valid
* allocation or not. This isn't exported from ClutterActor
* so we need to shadow the computations and track it ourselves.
*
* http://bugzilla.openedhand.com/show_bug.cgi?id=2024
*/
guint needs_allocation : 1;
}; };
enum enum
@ -273,19 +281,37 @@ st_scroll_bar_unmap (ClutterActor *actor)
} }
static void static void
st_scroll_bar_allocate (ClutterActor *actor, st_scroll_bar_parent_set (ClutterActor *actor,
ClutterActor *old_parent)
{
StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
priv->needs_allocation = TRUE;
if (CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->parent_set)
CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->parent_set (actor, old_parent);
}
static void
st_scroll_bar_queue_relayout (ClutterActor *actor)
{
StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
priv->needs_allocation = TRUE;
CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->queue_relayout (actor);
}
static void
scroll_bar_allocate_children (StScrollBar *bar,
const ClutterActorBox *box, const ClutterActorBox *box,
ClutterAllocationFlags flags) ClutterAllocationFlags flags)
{ {
StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv; StScrollBarPrivate *priv = bar->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (bar));
ClutterActorBox content_box, bw_box, fw_box, trough_box; ClutterActorBox content_box, bw_box, fw_box, trough_box;
gfloat bw_stepper_size, fw_stepper_size, min_size, natural_size; gfloat bw_stepper_size, fw_stepper_size, min_size, natural_size;
/* Chain up */
CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->
allocate (actor, box, flags);
st_theme_node_get_content_box (theme_node, box, &content_box); st_theme_node_get_content_box (theme_node, box, &content_box);
if (priv->vertical) if (priv->vertical)
@ -361,7 +387,6 @@ st_scroll_bar_allocate (ClutterActor *actor,
if (priv->adjustment) if (priv->adjustment)
{ {
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
float handle_size, position, avail_size, stepper_size; float handle_size, position, avail_size, stepper_size;
gdouble value, lower, upper, page_size, increment, min_size, max_size; gdouble value, lower, upper, page_size, increment, min_size, max_size;
ClutterActorBox handle_box = { 0, }; ClutterActorBox handle_box = { 0, };
@ -429,6 +454,45 @@ st_scroll_bar_allocate (ClutterActor *actor,
} }
} }
static void
st_scroll_bar_allocate (ClutterActor *actor,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StScrollBar *bar = ST_SCROLL_BAR (actor);
StScrollBarPrivate *priv = bar->priv;
priv->needs_allocation = FALSE;
/* Chain up */
CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->allocate (actor, box, flags);
scroll_bar_allocate_children (bar, box, flags);
}
static void
scroll_bar_update_positions (StScrollBar *bar)
{
StScrollBarPrivate *priv = bar->priv;
ClutterActorBox box;
/* Due to a change in the adjustments, we need to reposition our
* children; since adjustments changes can come from allocation
* changes in the scrolled area, we can't just queue a new relayout -
* we may already be in a relayout cycle. On the other hand, if
* a relayout is already queued, we can't just go ahead and allocate
* our children, since we don't have a valid allocation, and calling
* clutter_actor_get_allocation_box() will trigger an immediate
* stage relayout. So what we do is go ahead and immediately
* allocate our children if we already have a valid allocation, and
* otherwise just wait for the queued relayout.
*/
if (priv->needs_allocation)
return;
clutter_actor_get_allocation_box (CLUTTER_ACTOR (bar), &box);
scroll_bar_allocate_children (bar, &box, CLUTTER_ALLOCATION_NONE);
}
static void static void
st_scroll_bar_style_changed (StWidget *widget) st_scroll_bar_style_changed (StWidget *widget)
{ {
@ -532,6 +596,8 @@ st_scroll_bar_class_init (StScrollBarClass *klass)
object_class->dispose = st_scroll_bar_dispose; object_class->dispose = st_scroll_bar_dispose;
object_class->constructor = st_scroll_bar_constructor; object_class->constructor = st_scroll_bar_constructor;
actor_class->parent_set = st_scroll_bar_parent_set;
actor_class->queue_relayout = st_scroll_bar_queue_relayout;
actor_class->allocate = st_scroll_bar_allocate; actor_class->allocate = st_scroll_bar_allocate;
actor_class->paint = st_scroll_bar_paint; actor_class->paint = st_scroll_bar_paint;
actor_class->pick = st_scroll_bar_pick; actor_class->pick = st_scroll_bar_pick;
@ -1041,6 +1107,8 @@ st_scroll_bar_init (StScrollBar *self)
g_signal_connect (self, "notify::reactive", g_signal_connect (self, "notify::reactive",
G_CALLBACK (st_scroll_bar_notify_reactive), NULL); G_CALLBACK (st_scroll_bar_notify_reactive), NULL);
self->priv->needs_allocation = TRUE;
} }
StWidget * StWidget *
@ -1051,6 +1119,21 @@ st_scroll_bar_new (StAdjustment *adjustment)
NULL); NULL);
} }
static void
on_notify_value (GObject *object,
GParamSpec *pspec,
StScrollBar *bar)
{
scroll_bar_update_positions (bar);
}
static void
on_changed (StAdjustment *adjustment,
StScrollBar *bar)
{
scroll_bar_update_positions (bar);
}
void void
st_scroll_bar_set_adjustment (StScrollBar *bar, st_scroll_bar_set_adjustment (StScrollBar *bar,
StAdjustment *adjustment) StAdjustment *adjustment)
@ -1063,10 +1146,10 @@ st_scroll_bar_set_adjustment (StScrollBar *bar,
if (priv->adjustment) if (priv->adjustment)
{ {
g_signal_handlers_disconnect_by_func (priv->adjustment, g_signal_handlers_disconnect_by_func (priv->adjustment,
clutter_actor_queue_relayout, on_notify_value,
bar); bar);
g_signal_handlers_disconnect_by_func (priv->adjustment, g_signal_handlers_disconnect_by_func (priv->adjustment,
clutter_actor_queue_relayout, on_changed,
bar); bar);
g_object_unref (priv->adjustment); g_object_unref (priv->adjustment);
priv->adjustment = NULL; priv->adjustment = NULL;
@ -1076,11 +1159,11 @@ st_scroll_bar_set_adjustment (StScrollBar *bar,
{ {
priv->adjustment = g_object_ref (adjustment); priv->adjustment = g_object_ref (adjustment);
g_signal_connect_swapped (priv->adjustment, "notify::value", g_signal_connect (priv->adjustment, "notify::value",
G_CALLBACK (clutter_actor_queue_relayout), G_CALLBACK (on_notify_value),
bar); bar);
g_signal_connect_swapped (priv->adjustment, "changed", g_signal_connect (priv->adjustment, "changed",
G_CALLBACK (clutter_actor_queue_relayout), G_CALLBACK (on_changed),
bar); bar);
clutter_actor_queue_relayout (CLUTTER_ACTOR (bar)); clutter_actor_queue_relayout (CLUTTER_ACTOR (bar));