[gnome-shell] Keep similar theme nodes so we don't have to recompute CSS so often



commit dc2ec0a8f95baa21aa460d49ed1ac36d398f62f4
Author: Simon McVittie <simon mcvittie collabora co uk>
Date:   Thu Nov 8 17:50:01 2012 +0000

    Keep similar theme nodes so we don't have to recompute CSS so often
    
    Because we calculate and cache CSS properties once per StThemeNode,
    and only a certain set of attributes can affect the CSS properties,
    it's advantageous for as many widgets as possible to share a single
    StThemeNode. Similarly, if a widget changes state and then changes back
    (e.g. gaining and losing the :hover pseudo-class), it should ideally
    get its original StThemeNode back again when it returns to the old
    state.
    
    Here, I'm using the StThemeContext as the location for a cache.
    StThemeNodes are currently never freed: this seems OK for Shell's usage
    (a finite number of IDs, classes, pseudo-classes and types).
    
    Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
    Reviewed-by: Jasper St. Pierre <jstpierre mecheye net>

 src/st/st-theme-context.c |   34 ++++++++++++++++++++++++++++++++++
 src/st/st-theme-context.h |    3 +++
 src/st/st-theme-node.c    |   34 ++++++++++++++++++++++++++++++++++
 src/st/st-theme-node.h    |    1 +
 src/st/st-widget.c        |   20 +++++++++++++-------
 5 files changed, 85 insertions(+), 7 deletions(-)
---
diff --git a/src/st/st-theme-context.c b/src/st/st-theme-context.c
index 7b2a304..5891cf7 100644
--- a/src/st/st-theme-context.c
+++ b/src/st/st-theme-context.c
@@ -31,6 +31,9 @@ struct _StThemeContext {
   PangoFontDescription *font;
   StThemeNode *root_node;
   StTheme *theme;
+
+  /* set of StThemeNode */
+  GHashTable *nodes;
 };
 
 struct _StThemeContextClass {
@@ -66,6 +69,8 @@ st_theme_context_finalize (GObject *object)
                                         (gpointer) st_theme_context_changed,
                                         context);
 
+  if (context->nodes)
+    g_hash_table_unref (context->nodes);
   if (context->root_node)
     g_object_unref (context->root_node);
   if (context->theme)
@@ -105,6 +110,10 @@ st_theme_context_init (StThemeContext *context)
                             "resolution-changed",
                             G_CALLBACK (st_theme_context_changed),
                             context);
+
+  context->nodes = g_hash_table_new_full ((GHashFunc) st_theme_node_hash,
+                                          (GEqualFunc) st_theme_node_equal,
+                                          g_object_unref, NULL);
 }
 
 /**
@@ -146,6 +155,7 @@ st_theme_context_changed (StThemeContext *context)
 {
   StThemeNode *old_root = context->root_node;
   context->root_node = NULL;
+  g_hash_table_remove_all (context->nodes);
 
   emit_changed (context);
 
@@ -299,3 +309,27 @@ st_theme_context_get_root_node (StThemeContext *context)
 
   return context->root_node;
 }
+
+/**
+ * st_theme_context_intern_node:
+ * @context: a #StThemeContext
+ * @node: a #StThemeNode
+ *
+ * Return an existing node matching @node, or if that isn't possible,
+ * @node itself.
+ *
+ * Return value: (transfer none): a node with the same properties as @node
+ */
+StThemeNode *
+st_theme_context_intern_node (StThemeContext *context,
+                              StThemeNode    *node)
+{
+  StThemeNode *mine = g_hash_table_lookup (context->nodes, node);
+
+  /* this might be node or not - it doesn't actually matter */
+  if (mine != NULL)
+    return mine;
+
+  g_hash_table_add (context->nodes, g_object_ref (node));
+  return node;
+}
diff --git a/src/st/st-theme-context.h b/src/st/st-theme-context.h
index a0b73d9..9fa7afb 100644
--- a/src/st/st-theme-context.h
+++ b/src/st/st-theme-context.h
@@ -62,6 +62,9 @@ const PangoFontDescription *st_theme_context_get_font       (StThemeContext
 
 StThemeNode *               st_theme_context_get_root_node  (StThemeContext             *context);
 
+StThemeNode *               st_theme_context_intern_node    (StThemeContext             *context,
+                                                             StThemeNode                *node);
+
 G_END_DECLS
 
 #endif /* __ST_THEME_CONTEXT_H__ */
diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c
index fb7e9fb..8800299 100644
--- a/src/st/st-theme-node.c
+++ b/src/st/st-theme-node.c
@@ -390,6 +390,40 @@ st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b)
   return TRUE;
 }
 
+guint
+st_theme_node_hash (StThemeNode *node)
+{
+  guint hash = GPOINTER_TO_UINT (node->parent_node);
+
+  hash = hash * 33 + GPOINTER_TO_UINT (node->context);
+  hash = hash * 33 + GPOINTER_TO_UINT (node->theme);
+  hash = hash * 33 + ((guint) node->element_type);
+
+  if (node->element_id != NULL)
+    hash = hash * 33 + g_str_hash (node->element_id);
+
+  if (node->inline_style != NULL)
+    hash = hash * 33 + g_str_hash (node->inline_style);
+
+  if (node->element_classes != NULL)
+    {
+      gchar **it;
+
+      for (it = node->element_classes; *it != NULL; it++)
+        hash = hash * 33 + g_str_hash (*it) + 1;
+    }
+
+  if (node->pseudo_classes != NULL)
+    {
+      gchar **it;
+
+      for (it = node->pseudo_classes; *it != NULL; it++)
+        hash = hash * 33 + g_str_hash (*it) + 1;
+    }
+
+  return hash;
+}
+
 static void
 ensure_properties (StThemeNode *node)
 {
diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h
index 085fa6a..120cef8 100644
--- a/src/st/st-theme-node.h
+++ b/src/st/st-theme-node.h
@@ -110,6 +110,7 @@ StThemeNode *st_theme_node_get_parent (StThemeNode *node);
 StTheme *st_theme_node_get_theme (StThemeNode *node);
 
 gboolean    st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b);
+guint       st_theme_node_hash  (StThemeNode *node);
 
 GType       st_theme_node_get_element_type  (StThemeNode *node);
 const char *st_theme_node_get_element_id    (StThemeNode *node);
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index 25b9ad8..2a9b8da 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -589,6 +589,8 @@ st_widget_get_theme_node (StWidget *widget)
 
   if (priv->theme_node == NULL)
     {
+      StThemeContext *context;
+      StThemeNode *tmp_node;
       StThemeNode *parent_node = NULL;
       ClutterStage *stage = NULL;
       ClutterActor *parent;
@@ -629,16 +631,20 @@ st_widget_get_theme_node (StWidget *widget)
       else
         pseudo_class = direction_pseudo_class;
 
-      priv->theme_node = st_theme_node_new (st_theme_context_get_for_stage (stage),
-                                            parent_node, priv->theme,
-                                            G_OBJECT_TYPE (widget),
-                                            clutter_actor_get_name (CLUTTER_ACTOR (widget)),
-                                            priv->style_class,
-                                            pseudo_class,
-                                            priv->inline_style);
+      context = st_theme_context_get_for_stage (stage);
+      tmp_node = st_theme_node_new (context, parent_node, priv->theme,
+                                    G_OBJECT_TYPE (widget),
+                                    clutter_actor_get_name (CLUTTER_ACTOR (widget)),
+                                    priv->style_class,
+                                    pseudo_class,
+                                    priv->inline_style);
 
       if (pseudo_class != direction_pseudo_class)
         g_free (pseudo_class);
+
+      priv->theme_node = g_object_ref (st_theme_context_intern_node (context,
+                                                                     tmp_node));
+      g_object_unref (tmp_node);
     }
 
   return priv->theme_node;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]