Commit dc2ec0a8 authored by Simon McVittie's avatar Simon McVittie

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=687465Reviewed-by: Jasper St. Pierre's avatarJasper St. Pierre <jstpierre@mecheye.net>
parent 2cfed952
......@@ -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;
}
......@@ -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__ */
......@@ -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)
{
......
......@@ -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);
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment