From c0f6e746a0ae378359eb065df269d58b413160dd Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 17 Feb 2015 05:01:09 +0100 Subject: [PATCH] cssnode: Implement a way to properly track invalidations See the comment in gtkcssnodeprivate.h for how this works. --- gtk/gtkcssnode.c | 116 +++++++++++++++++++++++++++------------- gtk/gtkcssnodeprivate.h | 6 +++ 2 files changed, 85 insertions(+), 37 deletions(-) diff --git a/gtk/gtkcssnode.c b/gtk/gtkcssnode.c index 0e85d9d9f5..877dd182d8 100644 --- a/gtk/gtkcssnode.c +++ b/gtk/gtkcssnode.c @@ -373,6 +373,22 @@ gtk_css_node_set_children_changed (GtkCssNode *node) gtk_css_node_set_invalid (node, TRUE); } +static void +gtk_css_node_invalidate_style (GtkCssNode *cssnode) +{ + if (cssnode->style_is_invalid) + return; + + cssnode->style_is_invalid = TRUE; + gtk_css_node_set_invalid (cssnode, TRUE); + + if (cssnode->first_child) + gtk_css_node_invalidate_style (cssnode->first_child); + + if (cssnode->next_sibling) + gtk_css_node_invalidate_style (cssnode->next_sibling); +} + static void gtk_css_node_reposition (GtkCssNode *node, GtkCssNode *parent, @@ -383,6 +399,9 @@ gtk_css_node_reposition (GtkCssNode *node, /* Take a reference here so the whole function has a reference */ g_object_ref (node); + if (node->next_sibling) + gtk_css_node_invalidate_style (node->next_sibling); + if (node->parent != NULL) gtk_css_node_unlink_from_siblings (node); @@ -419,6 +438,9 @@ gtk_css_node_reposition (GtkCssNode *node, if (parent) gtk_css_node_link_to_siblings (node, previous); + if (node->next_sibling) + gtk_css_node_invalidate_style (node->next_sibling); + gtk_css_node_invalidate (node, GTK_CSS_CHANGE_ANY_PARENT | GTK_CSS_CHANGE_ANY_SIBLING); g_object_unref (node); @@ -504,23 +526,66 @@ gtk_css_node_set_style (GtkCssNode *cssnode, cssnode->style = style; } -GtkCssStyle * -gtk_css_node_get_style (GtkCssNode *cssnode) +static void +gtk_css_node_propagate_pending_changes (GtkCssNode *cssnode) +{ + GtkCssChange change, child_change; + GtkCssNode *child; + + if (!cssnode->invalid) + return; + + change = _gtk_css_change_for_child (cssnode->pending_changes); + if (cssnode->children_changed) + { + change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_ANY_SIBLING; + cssnode->children_changed = FALSE; + } + + for (child = gtk_css_node_get_first_child (cssnode); + child; + child = gtk_css_node_get_next_sibling (child)) + { + child_change = child->pending_changes; + gtk_css_node_invalidate (child, change); + if (child->visible) + change |= _gtk_css_change_for_sibling (child_change); + } +} + +static void +gtk_css_node_ensure_style (GtkCssNode *cssnode) { GtkCssStyle *new_style; - if (cssnode->pending_changes) + if (!cssnode->style_is_invalid) + return; + + if (cssnode->parent) + gtk_css_node_ensure_style (cssnode->parent); + + if (cssnode->previous_sibling) + gtk_css_node_ensure_style (cssnode->previous_sibling); + + gtk_css_node_propagate_pending_changes (cssnode); + + new_style = GTK_CSS_NODE_GET_CLASS (cssnode)->update_style (cssnode, + cssnode->pending_changes, + cssnode->style); + if (new_style) { - new_style = GTK_CSS_NODE_GET_CLASS (cssnode)->update_style (cssnode, - cssnode->pending_changes, - cssnode->style); - if (new_style) - { - gtk_css_node_set_style (cssnode, new_style); - g_object_unref (new_style); - cssnode->pending_changes = 0; - } + gtk_css_node_set_style (cssnode, new_style); + g_object_unref (new_style); + cssnode->pending_changes = 0; } + + cssnode->style_is_invalid = FALSE; +} + +GtkCssStyle * +gtk_css_node_get_style (GtkCssNode *cssnode) +{ + gtk_css_node_ensure_style (cssnode); return cssnode->style; } @@ -674,31 +739,7 @@ gtk_css_node_invalidate (GtkCssNode *cssnode, GTK_CSS_NODE_GET_CLASS (cssnode)->invalidate (cssnode); - gtk_css_node_set_invalid (cssnode, TRUE); -} - -static void -gtk_css_node_propagate_pending_changes (GtkCssNode *cssnode) -{ - GtkCssChange change; - GtkCssNode *child; - - if (!cssnode->invalid) - return; - - change = _gtk_css_change_for_child (cssnode->pending_changes); - if (cssnode->children_changed) - { - change |= GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_ANY_SIBLING; - cssnode->children_changed = FALSE; - } - - for (child = gtk_css_node_get_first_child (cssnode); - child; - child = gtk_css_node_get_next_sibling (child)) - { - gtk_css_node_invalidate (child, change); - } + gtk_css_node_invalidate_style (cssnode); } void @@ -735,6 +776,7 @@ gtk_css_node_validate (GtkCssNode *cssnode, change = cssnode->pending_changes; cssnode->pending_changes = 0; + cssnode->style_is_invalid = FALSE; new_style = GTK_CSS_NODE_GET_CLASS (cssnode)->validate (cssnode, cssnode->style, timestamp, change, parent_changed); if (new_style) diff --git a/gtk/gtkcssnodeprivate.h b/gtk/gtkcssnodeprivate.h index 9101507fca..32f938f130 100644 --- a/gtk/gtkcssnodeprivate.h +++ b/gtk/gtkcssnodeprivate.h @@ -52,6 +52,12 @@ struct _GtkCssNode guint visible :1; /* node will be skipped when validating or computing styles */ guint invalid :1; /* node or a child needs to be validated (even if just for animation) */ guint children_changed :1; /* the children changed since last validation */ + /* Two invariants hold for this variable: + * style_is_invalid == TRUE => next_sibling->style_is_invalid == TRUE + * style_is_invalid == FALSE => first_child->style_is_invalid == TRUE + * So if a valid style is computed, one has to previously ensure that the parent's and the previous sibling's style + * are valid. This allows both validation and invalidation to run in O(nodes-in-tree) */ + guint style_is_invalid :1; /* the style needs to be recomputed */ }; struct _GtkCssNodeClass