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>
This commit is contained in:
parent
2cfed952bb
commit
dc2ec0a8f9
@ -31,6 +31,9 @@ struct _StThemeContext {
|
|||||||
PangoFontDescription *font;
|
PangoFontDescription *font;
|
||||||
StThemeNode *root_node;
|
StThemeNode *root_node;
|
||||||
StTheme *theme;
|
StTheme *theme;
|
||||||
|
|
||||||
|
/* set of StThemeNode */
|
||||||
|
GHashTable *nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _StThemeContextClass {
|
struct _StThemeContextClass {
|
||||||
@ -66,6 +69,8 @@ st_theme_context_finalize (GObject *object)
|
|||||||
(gpointer) st_theme_context_changed,
|
(gpointer) st_theme_context_changed,
|
||||||
context);
|
context);
|
||||||
|
|
||||||
|
if (context->nodes)
|
||||||
|
g_hash_table_unref (context->nodes);
|
||||||
if (context->root_node)
|
if (context->root_node)
|
||||||
g_object_unref (context->root_node);
|
g_object_unref (context->root_node);
|
||||||
if (context->theme)
|
if (context->theme)
|
||||||
@ -105,6 +110,10 @@ st_theme_context_init (StThemeContext *context)
|
|||||||
"resolution-changed",
|
"resolution-changed",
|
||||||
G_CALLBACK (st_theme_context_changed),
|
G_CALLBACK (st_theme_context_changed),
|
||||||
context);
|
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;
|
StThemeNode *old_root = context->root_node;
|
||||||
context->root_node = NULL;
|
context->root_node = NULL;
|
||||||
|
g_hash_table_remove_all (context->nodes);
|
||||||
|
|
||||||
emit_changed (context);
|
emit_changed (context);
|
||||||
|
|
||||||
@ -299,3 +309,27 @@ st_theme_context_get_root_node (StThemeContext *context)
|
|||||||
|
|
||||||
return context->root_node;
|
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_get_root_node (StThemeContext *context);
|
||||||
|
|
||||||
|
StThemeNode * st_theme_context_intern_node (StThemeContext *context,
|
||||||
|
StThemeNode *node);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __ST_THEME_CONTEXT_H__ */
|
#endif /* __ST_THEME_CONTEXT_H__ */
|
||||||
|
@ -390,6 +390,40 @@ st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b)
|
|||||||
return TRUE;
|
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
|
static void
|
||||||
ensure_properties (StThemeNode *node)
|
ensure_properties (StThemeNode *node)
|
||||||
{
|
{
|
||||||
|
@ -110,6 +110,7 @@ StThemeNode *st_theme_node_get_parent (StThemeNode *node);
|
|||||||
StTheme *st_theme_node_get_theme (StThemeNode *node);
|
StTheme *st_theme_node_get_theme (StThemeNode *node);
|
||||||
|
|
||||||
gboolean st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b);
|
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);
|
GType st_theme_node_get_element_type (StThemeNode *node);
|
||||||
const char *st_theme_node_get_element_id (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)
|
if (priv->theme_node == NULL)
|
||||||
{
|
{
|
||||||
|
StThemeContext *context;
|
||||||
|
StThemeNode *tmp_node;
|
||||||
StThemeNode *parent_node = NULL;
|
StThemeNode *parent_node = NULL;
|
||||||
ClutterStage *stage = NULL;
|
ClutterStage *stage = NULL;
|
||||||
ClutterActor *parent;
|
ClutterActor *parent;
|
||||||
@ -629,16 +631,20 @@ st_widget_get_theme_node (StWidget *widget)
|
|||||||
else
|
else
|
||||||
pseudo_class = direction_pseudo_class;
|
pseudo_class = direction_pseudo_class;
|
||||||
|
|
||||||
priv->theme_node = st_theme_node_new (st_theme_context_get_for_stage (stage),
|
context = st_theme_context_get_for_stage (stage);
|
||||||
parent_node, priv->theme,
|
tmp_node = st_theme_node_new (context, parent_node, priv->theme,
|
||||||
G_OBJECT_TYPE (widget),
|
G_OBJECT_TYPE (widget),
|
||||||
clutter_actor_get_name (CLUTTER_ACTOR (widget)),
|
clutter_actor_get_name (CLUTTER_ACTOR (widget)),
|
||||||
priv->style_class,
|
priv->style_class,
|
||||||
pseudo_class,
|
pseudo_class,
|
||||||
priv->inline_style);
|
priv->inline_style);
|
||||||
|
|
||||||
if (pseudo_class != direction_pseudo_class)
|
if (pseudo_class != direction_pseudo_class)
|
||||||
g_free (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;
|
return priv->theme_node;
|
||||||
|
Loading…
Reference in New Issue
Block a user