Compare commits

...

6 Commits

Author SHA1 Message Date
Alexander Larsson
90bcb52293 css: Add debug output for the css rules tree 2012-11-23 16:15:29 +01:00
Alexander Larsson
3b48e562dd css: Make gtk_css_style_provider_get_change use the new rules tree 2012-11-23 16:13:27 +01:00
Alexander Larsson
fc12246758 css: Make gtk_css_style_provider_lookup use the new rule tree 2012-11-23 16:13:26 +01:00
Alexander Larsson
ae194d14d1 css: Calculate tree for faster ruleset matching
When matching a widget against the set of rules we currently iterate
over *all* rules in the theme, trying to match them until we fullfill
our needs. This takes a pretty long time since themes have a lot of rules.

This is the first step towards making this faster. Every time the
ruleset changes we pre-calculate a decision tree where the nodes are either
list of rules to match against, or checks, such that if the check fails
we can ignore a whole subset of the tree.

The tree is built up in two levels, first we check if certain states must
be set, ignoring whole branches if not. Then in the level below we look
for css classes which are used by many rules.

At the leaf nodes we store references to the full list rulesets. These are
stored as array offset, which are sorted in index order, which means css prio
order, as the rulesets are sorted by that.

With this in place we can quickly prune large parts of the tree and then
merge the non-pruned result into a much smaller list of rules that we
need to do full checking on.
2012-11-23 16:13:21 +01:00
Alexander Larsson
c55a492ad3 css: Add some methods for getting selector state
This adds the following private methods:
_gtk_css_selector_get_primary_state_flags
_gtk_css_selector_get_primary_classes
_gtk_css_selector_has_primary_class

They get state and classes for the primary selector (i.e. the rightmost
simple selector). This will be used for some css optimizations.
2012-11-23 15:53:49 +01:00
Alexander Larsson
b70d8c477d CSS: Join multiple adjacent pseudoclass selectors
Its pretty common to have a selector like :selected:active where
two or more state flags appear in a row. Right now we save
these as multiple GtkCssSelectors, but as state is a bitfield we
can easily join them. We do this in the simple case where the
state matchers are directly adjacent by just piggybacking on
a previous pseudoclass if there is one.
2012-11-23 09:42:10 +01:00
3 changed files with 510 additions and 23 deletions

View File

@@ -994,6 +994,37 @@ struct GtkCssRuleset
guint owns_widget_style : 1;
};
enum RulesetsTreeType {
RULESETS_TREE_TYPE_STATE,
RULESETS_TREE_TYPE_CLASS,
RULESETS_TREE_TYPE_RULES
};
typedef struct _GtkCssRulesetList GtkCssRulesetList;
typedef struct _GtkCssRulesetsTree GtkCssRulesetsTree;
struct _GtkCssRulesetList {
guint *rules;
guint num_rules;
};
struct _GtkCssRulesetsTree
{
enum RulesetsTreeType type;
union {
GtkCssRulesetList rules;
struct {
GtkCssRulesetsTree *matched;
GtkStateFlags state;
} state;
struct {
GtkCssRulesetsTree *matched;
GQuark class;
} class;
} u;
GtkCssRulesetsTree *next;
};
struct _GtkCssScanner
{
GtkCssProvider *provider;
@@ -1011,6 +1042,8 @@ struct _GtkCssProviderPrivate
GHashTable *keyframes;
GArray *rulesets;
guint *rulesets_refs;
GtkCssRulesetsTree *rulesets_tree;
GResource *resource;
};
@@ -1027,6 +1060,7 @@ static void gtk_css_provider_finalize (GObject *object);
static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
static void gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface);
static void widget_property_value_list_free (WidgetPropertyValue *head);
static void gtk_css_rulesets_tree_free (GtkCssRulesetsTree *tree);
static gboolean
gtk_css_provider_load_internal (GtkCssProvider *css_provider,
@@ -1561,6 +1595,117 @@ gtk_css_style_provider_get_keyframes (GtkStyleProviderPrivate *provider,
return g_hash_table_lookup (css_provider->priv->keyframes, name);
}
static void
collect_possible_rules (GPtrArray *rules_lists,
GtkCssRulesetsTree *tree,
const GtkCssMatcher *matcher)
{
if (tree == NULL)
return;
switch (tree->type)
{
case RULESETS_TREE_TYPE_RULES:
g_ptr_array_add (rules_lists, &tree->u.rules);
break;
case RULESETS_TREE_TYPE_STATE:
if ((_gtk_css_matcher_get_state (matcher) & tree->u.state.state) == tree->u.state.state)
collect_possible_rules (rules_lists,
tree->u.state.matched,
matcher);
break;
case RULESETS_TREE_TYPE_CLASS:
if (_gtk_css_matcher_has_class (matcher, tree->u.class.class))
collect_possible_rules (rules_lists,
tree->u.class.matched,
matcher);
break;
}
collect_possible_rules (rules_lists,
tree->next,
matcher);
}
/* Merged two pre-sorted arrays of uints, assumes enough space in destination to fit a_len + b_len */
static void
merge_uints (guint *dest, const guint *a, int a_len, const guint* b, int b_len)
{
const guint *a_end = a + a_len;
const guint *b_end = b + b_len;
while (a != a_end ||
b != b_end)
{
if (a == a_end)
{
memcpy (dest, b, sizeof (guint) * (b_end - b));
break;
}
else if (b == b_end)
{
memcpy (dest, a, sizeof (guint) * (a_end - a));
break;
}
else if (*a <= *b)
*dest++ = *a++;
else
*dest++ = *b++;
}
}
static guint *
find_possible_rules (GtkCssRulesetsTree *tree,
const GtkCssMatcher *matcher,
guint *num_refs_out)
{
GPtrArray *rules_lists;
gint i;
guint *merged, *tmp_refs, *swap_ptr;
guint merged_size;
guint num_refs;
/* Collect all possible rules from the tree */
rules_lists = g_ptr_array_new ();
collect_possible_rules (rules_lists, tree, matcher);
/* Merge the separate sorted lists into one list */
num_refs = 0;
for (i = 0; i < rules_lists->len; i++)
{
GtkCssRulesetList *list = (GtkCssRulesetList *)rules_lists->pdata[i];
num_refs += list->num_rules;
}
merged = g_new (guint, num_refs);
merged_size = 0;
tmp_refs = g_new (guint, num_refs);
/* Merge the already sorted refs list */
for (i = 0; i < rules_lists->len; i++)
{
GtkCssRulesetList *list = (GtkCssRulesetList *)rules_lists->pdata[i];
if (i == 0)
memcpy (merged, list->rules, sizeof (*list->rules) * list->num_rules);
else
{
merge_uints (tmp_refs, merged, merged_size, list->rules, list->num_rules);
swap_ptr = merged;
merged = tmp_refs;
tmp_refs = swap_ptr;
}
merged_size += list->num_rules;
}
g_free (tmp_refs);
g_ptr_array_free (rules_lists, TRUE);
*num_refs_out = num_refs;
return merged;
}
static void
gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider,
const GtkCssMatcher *matcher,
@@ -1570,6 +1715,9 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider,
GtkCssProviderPrivate *priv;
GtkCssRuleset *ruleset;
guint j;
gint i;
guint *refs;
guint num_refs;
css_provider = GTK_CSS_PROVIDER (provider);
priv = css_provider->priv;
@@ -1577,10 +1725,14 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider,
if (priv->rulesets->len == 0)
return;
for (ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, priv->rulesets->len - 1);
ruleset >= &g_array_index (priv->rulesets, GtkCssRuleset, 0);
ruleset--)
refs = find_possible_rules (priv->rulesets_tree, matcher, &num_refs);
if (num_refs == 0)
return;
for (i = num_refs - 1; i >= 0; i--)
{
ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, refs[i]);
if (ruleset->styles == NULL)
continue;
@@ -1608,6 +1760,8 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider,
if (_gtk_bitmask_is_empty (_gtk_css_lookup_get_missing (lookup)))
break;
}
g_free (refs);
}
static GtkCssChange
@@ -1618,15 +1772,21 @@ gtk_css_style_provider_get_change (GtkStyleProviderPrivate *provider,
GtkCssProviderPrivate *priv;
GtkCssChange change = 0;
int i;
guint *refs;
guint num_refs;
css_provider = GTK_CSS_PROVIDER (provider);
priv = css_provider->priv;
for (i = priv->rulesets->len - 1; i >= 0; i--)
refs = find_possible_rules (priv->rulesets_tree, matcher, &num_refs);
if (num_refs == 0)
return change;
for (i = num_refs - 1; i >= 0; i--)
{
GtkCssRuleset *ruleset;
ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, refs[i]);
if (ruleset->styles == NULL)
continue;
@@ -1637,6 +1797,8 @@ gtk_css_style_provider_get_change (GtkStyleProviderPrivate *provider,
change |= gtk_css_ruleset_get_change (ruleset);
}
g_free (refs);
return change;
}
@@ -1663,6 +1825,8 @@ gtk_css_provider_finalize (GObject *object)
gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));
g_array_free (priv->rulesets, TRUE);
g_free (priv->rulesets_refs);
gtk_css_rulesets_tree_free (priv->rulesets_tree);
g_hash_table_destroy (priv->symbolic_colors);
g_hash_table_destroy (priv->keyframes);
@@ -1797,6 +1961,11 @@ gtk_css_provider_reset (GtkCssProvider *css_provider)
g_hash_table_remove_all (priv->symbolic_colors);
g_hash_table_remove_all (priv->keyframes);
g_free (priv->rulesets_refs);
priv->rulesets_refs = NULL;
gtk_css_rulesets_tree_free (priv->rulesets_tree);
priv->rulesets_tree = NULL;
for (i = 0; i < priv->rulesets->len; i++)
gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));
g_array_set_size (priv->rulesets, 0);
@@ -2430,12 +2599,274 @@ gtk_css_provider_compare_rule (gconstpointer a_,
return 0;
}
static void
gtk_css_rulesets_tree_free (GtkCssRulesetsTree *tree)
{
if (tree == NULL)
return;
switch (tree->type)
{
case RULESETS_TREE_TYPE_RULES:
break;
case RULESETS_TREE_TYPE_STATE:
gtk_css_rulesets_tree_free (tree->u.state.matched);
break;
case RULESETS_TREE_TYPE_CLASS:
gtk_css_rulesets_tree_free (tree->u.class.matched);
break;
}
gtk_css_rulesets_tree_free (tree->next);
}
static gint
compare_refs_by_val (gconstpointer pa,
gconstpointer pb,
gpointer user_data)
{
guint a = *(guint *)pa;
guint b = *(guint *)pb;
return a - b;
}
static GtkCssRulesetsTree *
subdivide_by_none (GtkCssProviderPrivate *priv, guint *refs, guint start, guint end)
{
GtkCssRulesetsTree *tree;
tree = g_new0 (GtkCssRulesetsTree, 1);
tree->type = RULESETS_TREE_TYPE_RULES;
tree->u.rules.rules = refs + start;
tree->u.rules.num_rules = end - start;
tree->next = NULL;
/* Sort by rule index (i.e. css prio order) */
g_qsort_with_data (tree->u.rules.rules,
tree->u.rules.num_rules,
sizeof (guint),
compare_refs_by_val,
NULL);
return tree;
}
typedef struct {
GQuark class;
guint count;
} GtkCssPseudoClassCount;
static gint
compare_class_count (gconstpointer pa,
gconstpointer pb,
gpointer user_data)
{
GtkCssPseudoClassCount *a = (GtkCssPseudoClassCount *)pa;
GtkCssPseudoClassCount *b = (GtkCssPseudoClassCount *)pb;
return b->count - a->count;
}
static GtkCssRulesetsTree *
subdivide_by_class (GtkCssProviderPrivate *priv, guint *refs, guint start, guint end)
{
GHashTable *count_ht;
GHashTableIter iter;
guint i, j, n_classes;
gpointer key, value;
GtkCssPseudoClassCount *class_count;
GtkCssRulesetsTree *tree;
GtkCssRulesetsTree *trees = NULL;
count_ht = g_hash_table_new (g_direct_hash, g_direct_equal);
for (i = start; i < end; i++)
{
GtkCssSelector *selector = g_array_index (priv->rulesets, GtkCssRuleset, refs[i]).selector;
GQuark *classes = _gtk_css_selector_get_primary_classes (selector);
for (j = 0; classes[j] != 0; j++)
{
guint count = GPOINTER_TO_INT (g_hash_table_lookup (count_ht, GUINT_TO_POINTER (classes[j])));
g_hash_table_replace (count_ht, GUINT_TO_POINTER (classes[j]), GUINT_TO_POINTER (count + 1));
}
g_free (classes);
}
n_classes = 0;
class_count = g_new (GtkCssPseudoClassCount, g_hash_table_size (count_ht));
g_hash_table_iter_init (&iter, count_ht);
while (g_hash_table_iter_next (&iter, &key, &value))
{
guint count = GPOINTER_TO_UINT (value);
/* Random cuttoff point here. Obviously if any given class has only one item
with it, then adding a check doesn't help, and it uses memory. Where
is the inflexion point? Lets make it 3... */
if (count >= 3)
{
class_count[n_classes].class = GPOINTER_TO_UINT (key);
class_count[n_classes].count = count;
n_classes++;
}
}
g_hash_table_destroy (count_ht);
/* Sort largest class counts first */
g_qsort_with_data (class_count,
n_classes,
sizeof (*class_count),
compare_class_count,
NULL);
for (i = 0; i < n_classes; i++)
{
guint split = start;
for (j = start; j < end; j++)
{
GtkCssSelector *selector = g_array_index (priv->rulesets, GtkCssRuleset, refs[j]).selector;
if (_gtk_css_selector_has_primary_class (selector, class_count[i].class))
{
guint tmp = refs[split];
refs[split] = refs[j];
refs[j] = tmp;
split++;
}
}
/* Check size again sice nodes may have been used in other trees */
if (split - start > 3)
{
tree = g_new (GtkCssRulesetsTree, 1);
tree->type = RULESETS_TREE_TYPE_CLASS;
tree->u.class.matched = subdivide_by_none (priv, refs, start, split);
tree->u.class.class = class_count[i].class;
tree->next = trees;
trees = tree;
start = split;
}
}
g_free (class_count);
if (start != end)
{
tree = subdivide_by_none (priv, refs, start, end);
tree->next = trees;
trees = tree;
}
return trees;
}
static GtkCssRulesetsTree *
subdivide_by_state (GtkCssProviderPrivate *priv, guint *refs, guint start, guint end, guint state)
{
GtkCssRulesetsTree *tree;
guint i;
if (end == start)
return NULL;
if (state == 0)
return subdivide_by_class (priv, refs, start, end);
// Find first non-set flag
for (i = start; i < end; i++)
{
GtkCssSelector *selector = g_array_index (priv->rulesets, GtkCssRuleset, refs[i]).selector;
if ((_gtk_css_selector_get_primary_state_flags (selector) & state) == 0)
break;
}
if (i == start || i == end) /* All or none set, no use subdividing by this */
return subdivide_by_state (priv, refs, start, end, state >> 1);
tree = g_new (GtkCssRulesetsTree, 1);
tree->type = RULESETS_TREE_TYPE_STATE;
tree->u.state.matched = subdivide_by_state (priv, refs, start, i, state >> 1);
tree->u.state.state = state;
tree->next = subdivide_by_state (priv, refs, i, end, state >> 1);
return tree;
}
static gint
compare_refs_by_flags (gconstpointer pa,
gconstpointer pb,
gpointer user_data)
{
guint a = *(guint *)pa;
guint b = *(guint *)pb;
GArray *rulesets = user_data;
GtkCssSelector *selector_a, *selector_b;
guint32 flags_a, flags_b;
selector_a = g_array_index (rulesets, GtkCssRuleset, a).selector;
selector_b = g_array_index (rulesets, GtkCssRuleset, b).selector;
flags_a = _gtk_css_selector_get_primary_state_flags (selector_a);
flags_b = _gtk_css_selector_get_primary_state_flags (selector_b);
return flags_b - flags_a;
}
static void
print_tree (GtkCssRulesetsTree *tree, int indent)
{
if (tree == NULL)
return;
switch (tree->type)
{
case RULESETS_TREE_TYPE_RULES:
printf ("%*sCheck rules %p len %d\n", indent, "", tree->u.rules.rules, tree->u.rules.num_rules);
break;
case RULESETS_TREE_TYPE_STATE:
printf ("%*sMatch state 0x%x ->\n", indent, "", tree->u.state.state);
print_tree (tree->u.state.matched, indent + 4);
break;
case RULESETS_TREE_TYPE_CLASS:
printf ("%*sMatch class %s ->\n", indent, "", g_quark_to_string (tree->u.class.class));
print_tree (tree->u.class.matched, indent + 4);
break;
}
print_tree (tree->next, indent);
}
static void
gtk_css_provider_postprocess (GtkCssProvider *css_provider)
{
GtkCssProviderPrivate *priv = css_provider->priv;
guint *refs, i;
g_array_sort (priv->rulesets, gtk_css_provider_compare_rule);
refs = g_new (guint, priv->rulesets->len);
priv->rulesets_refs = refs;
for (i = 0; i < priv->rulesets->len; i++)
refs[i] = i;
/* Sort by flags, so that high bits set is sorted first at each level */
g_qsort_with_data (refs,
priv->rulesets->len,
sizeof (guint),
compare_refs_by_flags,
priv->rulesets);
priv->rulesets_tree = subdivide_by_state (priv, refs, 0, priv->rulesets->len, GTK_STATE_FLAG_BACKDROP);
if (g_getenv ("GTK_CSS_DEBUG_TREE"))
{
g_print ("Rulesets tree for proviced %p (%d rules)\n", css_provider, priv->rulesets->len);
print_tree (priv->rulesets_tree, 1);
}
}
static gboolean

View File

@@ -38,6 +38,7 @@ struct _GtkCssSelectorClass {
guint increase_id_specificity :1;
guint increase_class_specificity :1;
guint increase_element_specificity :1;
guint is_simple :1;
};
struct _GtkCssSelector
@@ -112,7 +113,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_DESCENDANT = {
gtk_css_selector_descendant_print,
gtk_css_selector_descendant_match,
gtk_css_selector_descendant_get_change,
FALSE, FALSE, FALSE
FALSE, FALSE, FALSE, FALSE
};
/* CHILD */
@@ -147,7 +148,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_CHILD = {
gtk_css_selector_child_print,
gtk_css_selector_child_match,
gtk_css_selector_child_get_change,
FALSE, FALSE, FALSE
FALSE, FALSE, FALSE, FALSE
};
/* SIBLING */
@@ -187,7 +188,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_SIBLING = {
gtk_css_selector_sibling_print,
gtk_css_selector_sibling_match,
gtk_css_selector_sibling_get_change,
FALSE, FALSE, FALSE
FALSE, FALSE, FALSE, FALSE
};
/* ADJACENT */
@@ -222,7 +223,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_ADJACENT = {
gtk_css_selector_adjacent_print,
gtk_css_selector_adjacent_match,
gtk_css_selector_adjacent_get_change,
FALSE, FALSE, FALSE
FALSE, FALSE, FALSE, FALSE
};
/* ANY */
@@ -262,7 +263,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_ANY = {
gtk_css_selector_any_print,
gtk_css_selector_any_match,
gtk_css_selector_any_get_change,
FALSE, FALSE, FALSE
FALSE, FALSE, FALSE, TRUE
};
/* NAME */
@@ -295,7 +296,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_NAME = {
gtk_css_selector_name_print,
gtk_css_selector_name_match,
gtk_css_selector_name_get_change,
FALSE, FALSE, TRUE
FALSE, FALSE, TRUE, TRUE
};
/* REGION */
@@ -341,7 +342,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_REGION = {
gtk_css_selector_region_print,
gtk_css_selector_region_match,
gtk_css_selector_region_get_change,
FALSE, FALSE, TRUE
FALSE, FALSE, TRUE, TRUE
};
/* CLASS */
@@ -375,7 +376,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_CLASS = {
gtk_css_selector_class_print,
gtk_css_selector_class_match,
gtk_css_selector_class_get_change,
FALSE, TRUE, FALSE
FALSE, TRUE, FALSE, TRUE
};
/* ID */
@@ -409,7 +410,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_ID = {
gtk_css_selector_id_print,
gtk_css_selector_id_match,
gtk_css_selector_id_get_change,
TRUE, FALSE, FALSE
TRUE, FALSE, FALSE, TRUE
};
/* PSEUDOCLASS FOR STATE */
@@ -430,18 +431,15 @@ gtk_css_selector_pseudoclass_state_print (const GtkCssSelector *selector,
guint i, state;
state = GPOINTER_TO_UINT (selector->data);
g_string_append_c (string, ':');
for (i = 0; i < G_N_ELEMENTS (state_names); i++)
{
if (state == (1 << i))
{
g_string_append_c (string, ':');
g_string_append (string, state_names[i]);
return;
}
}
g_assert_not_reached ();
}
static gboolean
@@ -467,7 +465,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_STATE = {
gtk_css_selector_pseudoclass_state_print,
gtk_css_selector_pseudoclass_state_match,
gtk_css_selector_pseudoclass_state_get_change,
FALSE, TRUE, FALSE
FALSE, TRUE, FALSE, TRUE
};
/* PSEUDOCLASS FOR POSITION */
@@ -708,7 +706,7 @@ static const GtkCssSelectorClass GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION = {
gtk_css_selector_pseudoclass_position_print,
gtk_css_selector_pseudoclass_position_match,
gtk_css_selector_pseudoclass_position_get_change,
FALSE, TRUE, FALSE
FALSE, TRUE, FALSE, TRUE
};
/* API */
@@ -950,9 +948,16 @@ parse_selector_pseudo_class (GtkCssParser *parser,
if (_gtk_css_parser_try (parser, pseudo_classes[i].name, FALSE))
{
if (pseudo_classes[i].state_flag)
selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
selector,
GUINT_TO_POINTER (pseudo_classes[i].state_flag));
{
/* Piggy back on previous pseudoclass if any */
if (selector && selector->class == &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE)
selector->data = GUINT_TO_POINTER (GPOINTER_TO_UINT (selector->data) |
pseudo_classes[i].state_flag);
else
selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_PSEUDOCLASS_STATE,
selector,
GUINT_TO_POINTER (pseudo_classes[i].state_flag));
}
else
selector = gtk_css_selector_new (&GTK_CSS_SELECTOR_PSEUDOCLASS_POSITION,
selector,
@@ -1172,3 +1177,49 @@ _gtk_css_selector_get_state_flags (const GtkCssSelector *selector)
return state;
}
GtkStateFlags
_gtk_css_selector_get_primary_state_flags (const GtkCssSelector *selector)
{
GtkStateFlags state = 0;
g_return_val_if_fail (selector != NULL, 0);
for (; selector && selector->class->is_simple; selector = gtk_css_selector_previous (selector))
{
if (selector->class == &GTK_CSS_SELECTOR_PSEUDOCLASS_STATE)
state |= GPOINTER_TO_UINT (selector->data);
}
return state;
}
GQuark *
_gtk_css_selector_get_primary_classes (const GtkCssSelector *selector)
{
GArray *array = g_array_new (TRUE, FALSE, sizeof (GQuark));
g_return_val_if_fail (selector != NULL, 0);
for (; selector && selector->class->is_simple; selector = gtk_css_selector_previous (selector))
{
if (selector->class == &GTK_CSS_SELECTOR_CLASS)
g_array_append_val (array, selector->data);
}
return (GQuark *)g_array_free (array, FALSE);
}
gboolean
_gtk_css_selector_has_primary_class (const GtkCssSelector *selector, GQuark class)
{
g_return_val_if_fail (selector != NULL, 0);
for (; selector && selector->class->is_simple; selector = gtk_css_selector_previous (selector))
{
if (selector->class == &GTK_CSS_SELECTOR_CLASS)
if (GPOINTER_TO_UINT (selector->data) == class)
return TRUE;
}
return FALSE;
}

View File

@@ -40,6 +40,11 @@ gboolean _gtk_css_selector_matches (const GtkCssSelector *sel
int _gtk_css_selector_compare (const GtkCssSelector *a,
const GtkCssSelector *b);
GtkStateFlags _gtk_css_selector_get_primary_state_flags (const GtkCssSelector *selector);
GQuark * _gtk_css_selector_get_primary_classes (const GtkCssSelector *selector);
gboolean _gtk_css_selector_has_primary_class (const GtkCssSelector *selector,
GQuark class);
G_END_DECLS
#endif /* __GTK_CSS_SELECTOR_PRIVATE_H__ */