st: Delay handling of :first/:last-child changes

Updating the :first/:last-child pseudo classes can result in a lot
of unnecessary style changes when bulk-adding children to a container,
as every child ends up as the new last child.

Address this by deferring the style change to an idle, so we only do
the work once for the actual first and last child.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/529
This commit is contained in:
Florian Müllner 2019-05-02 21:12:01 +02:00 committed by Florian Müllner
parent 598407b14a
commit 0afd600ea4

View File

@ -65,6 +65,8 @@ struct _StWidgetPrivate
StThemeNodeTransition *transition_animation; StThemeNodeTransition *transition_animation;
guint is_style_dirty : 1; guint is_style_dirty : 1;
guint first_child_dirty : 1;
guint last_child_dirty : 1;
guint draw_bg_color : 1; guint draw_bg_color : 1;
guint draw_border_internal : 1; guint draw_border_internal : 1;
guint track_hover : 1; guint track_hover : 1;
@ -72,6 +74,7 @@ struct _StWidgetPrivate
guint can_focus : 1; guint can_focus : 1;
gulong texture_file_changed_id; gulong texture_file_changed_id;
guint update_child_styles_id;
AtkObject *accessible; AtkObject *accessible;
AtkRole accessible_role; AtkRole accessible_role;
@ -323,6 +326,8 @@ st_widget_dispose (GObject *gobject)
g_clear_object (&priv->last_visible_child); g_clear_object (&priv->last_visible_child);
G_OBJECT_CLASS (st_widget_parent_class)->dispose (gobject); G_OBJECT_CLASS (st_widget_parent_class)->dispose (gobject);
g_clear_handle_id (&priv->update_child_styles_id, g_source_remove);
} }
static void static void
@ -1608,47 +1613,85 @@ find_nearest_visible_forward (ClutterActor *actor)
return next; return next;
} }
static gboolean
st_widget_update_child_styles (StWidget *widget)
{
StWidgetPrivate *priv = st_widget_get_instance_private (widget);
if (priv->first_child_dirty)
{
ClutterActor *first_child;
priv->first_child_dirty = FALSE;
first_child = clutter_actor_get_first_child (CLUTTER_ACTOR (widget));
st_widget_set_first_visible_child (widget,
find_nearest_visible_forward (first_child));
}
if (priv->last_child_dirty)
{
ClutterActor *last_child;
priv->last_child_dirty = FALSE;
last_child = clutter_actor_get_last_child (CLUTTER_ACTOR (widget));
st_widget_set_last_visible_child (widget,
find_nearest_visible_backwards (last_child));
}
priv->update_child_styles_id = 0;
return G_SOURCE_REMOVE;
}
static void
st_widget_queue_child_styles_update (StWidget *widget)
{
StWidgetPrivate *priv = st_widget_get_instance_private (widget);
if (priv->update_child_styles_id != 0)
return;
priv->update_child_styles_id = g_idle_add ((GSourceFunc) st_widget_update_child_styles, widget);
}
static void static void
st_widget_visible_notify (StWidget *widget, st_widget_visible_notify (StWidget *widget,
GParamSpec *pspec, GParamSpec *pspec,
gpointer data) gpointer data)
{ {
StWidgetPrivate *parent_priv;
ClutterActor *actor = CLUTTER_ACTOR (widget); ClutterActor *actor = CLUTTER_ACTOR (widget);
ClutterActor *parent = clutter_actor_get_parent (actor); ClutterActor *parent = clutter_actor_get_parent (actor);
if (parent == NULL || !ST_IS_WIDGET (parent)) if (parent == NULL || !ST_IS_WIDGET (parent))
return; return;
parent_priv = st_widget_get_instance_private (ST_WIDGET (parent));
if (clutter_actor_is_visible (actor)) if (clutter_actor_is_visible (actor))
{ {
ClutterActor *before, *after; ClutterActor *before, *after;
before = clutter_actor_get_previous_sibling (actor); before = clutter_actor_get_previous_sibling (actor);
if (find_nearest_visible_backwards (before) == NULL) if (find_nearest_visible_backwards (before) == NULL)
st_widget_set_first_visible_child (ST_WIDGET (parent), actor); parent_priv->first_child_dirty = TRUE;
after = clutter_actor_get_next_sibling (actor); after = clutter_actor_get_next_sibling (actor);
if (find_nearest_visible_forward (after) == NULL) if (find_nearest_visible_forward (after) == NULL)
st_widget_set_last_visible_child (ST_WIDGET (parent), actor); parent_priv->last_child_dirty = TRUE;
} }
else else
{ {
if (st_widget_has_style_pseudo_class (widget, "first-child")) if (st_widget_has_style_pseudo_class (widget, "first-child"))
{ parent_priv->first_child_dirty = TRUE;
ClutterActor *new_first;
new_first = find_nearest_visible_forward (CLUTTER_ACTOR (widget));
st_widget_set_first_visible_child (ST_WIDGET (parent), new_first);
}
if (st_widget_has_style_pseudo_class (widget, "last-child")) if (st_widget_has_style_pseudo_class (widget, "last-child"))
{ parent_priv->last_child_dirty = TRUE;
ClutterActor *new_last;
new_last = find_nearest_visible_backwards (CLUTTER_ACTOR (widget));
st_widget_set_last_visible_child (ST_WIDGET (parent), new_last);
}
} }
if (parent_priv->first_child_dirty || parent_priv->last_child_dirty)
st_widget_queue_child_styles_update (ST_WIDGET (parent));
} }
static void static void
@ -1656,10 +1699,10 @@ st_widget_first_child_notify (StWidget *widget,
GParamSpec *pspec, GParamSpec *pspec,
gpointer data) gpointer data)
{ {
ClutterActor *first_child; StWidgetPrivate *priv = st_widget_get_instance_private (widget);
first_child = clutter_actor_get_first_child (CLUTTER_ACTOR (widget)); priv->first_child_dirty = TRUE;
st_widget_set_first_visible_child (widget, find_nearest_visible_forward (first_child)); st_widget_queue_child_styles_update (widget);
} }
static void static void
@ -1667,10 +1710,10 @@ st_widget_last_child_notify (StWidget *widget,
GParamSpec *pspec, GParamSpec *pspec,
gpointer data) gpointer data)
{ {
ClutterActor *last_child; StWidgetPrivate *priv = st_widget_get_instance_private (widget);
last_child = clutter_actor_get_last_child (CLUTTER_ACTOR (widget)); priv->last_child_dirty = TRUE;
st_widget_set_last_visible_child (widget, find_nearest_visible_backwards (last_child)); st_widget_queue_child_styles_update (widget);
} }
static void static void