1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2015 Benjamin Otte <otte@gnome.org> |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include "config.h" |
19 | |
20 | #include "gtkcssnodestylecacheprivate.h" |
21 | |
22 | #include "gtkdebug.h" |
23 | #include "gtkcssstaticstyleprivate.h" |
24 | |
25 | struct _GtkCssNodeStyleCache { |
26 | guint ref_count; |
27 | GtkCssStyle *style; |
28 | GHashTable *children; |
29 | }; |
30 | |
31 | #define UNPACK_DECLARATION(packed) ((GtkCssNodeDeclaration *) (GPOINTER_TO_SIZE (packed) & ~0x3)) |
32 | #define UNPACK_FLAGS(packed) (GPOINTER_TO_SIZE (packed) & 0x3) |
33 | #define PACK(decl, first_child, last_child) GSIZE_TO_POINTER (GPOINTER_TO_SIZE (decl) | ((first_child) ? 0x2 : 0) | ((last_child) ? 0x1 : 0)) |
34 | |
35 | GtkCssNodeStyleCache * |
36 | gtk_css_node_style_cache_new (GtkCssStyle *style) |
37 | { |
38 | GtkCssNodeStyleCache *result; |
39 | |
40 | result = g_slice_new0 (GtkCssNodeStyleCache); |
41 | |
42 | result->ref_count = 1; |
43 | result->style = g_object_ref (style); |
44 | |
45 | return result; |
46 | } |
47 | |
48 | GtkCssNodeStyleCache * |
49 | gtk_css_node_style_cache_ref (GtkCssNodeStyleCache *cache) |
50 | { |
51 | cache->ref_count++; |
52 | |
53 | return cache; |
54 | } |
55 | |
56 | void |
57 | gtk_css_node_style_cache_unref (GtkCssNodeStyleCache *cache) |
58 | { |
59 | cache->ref_count--; |
60 | |
61 | if (cache->ref_count > 0) |
62 | return; |
63 | |
64 | g_object_unref (object: cache->style); |
65 | if (cache->children) |
66 | g_hash_table_unref (hash_table: cache->children); |
67 | |
68 | g_slice_free (GtkCssNodeStyleCache, cache); |
69 | } |
70 | |
71 | GtkCssStyle * |
72 | gtk_css_node_style_cache_get_style (GtkCssNodeStyleCache *cache) |
73 | { |
74 | return cache->style; |
75 | } |
76 | |
77 | static gboolean |
78 | may_be_stored_in_cache (GtkCssStyle *style) |
79 | { |
80 | GtkCssChange change; |
81 | |
82 | /* If you run your application with |
83 | * GTK_DEBUG=no-css-cache |
84 | * no caching will happen. This is slow (in particular |
85 | * when animating), but useful for figuring out bugs. |
86 | * |
87 | * We achieve that by disallowing any inserts into caches here. |
88 | */ |
89 | #ifdef G_ENABLE_DEBUG |
90 | if (GTK_DEBUG_CHECK (NO_CSS_CACHE)) |
91 | return FALSE; |
92 | #endif |
93 | |
94 | if (!GTK_IS_CSS_STATIC_STYLE (style)) |
95 | return FALSE; |
96 | |
97 | change = gtk_css_static_style_get_change (GTK_CSS_STATIC_STYLE (style)); |
98 | |
99 | /* The cache is shared between all children of the parent, so if a |
100 | * style depends on a sibling it is not independent of the child. |
101 | */ |
102 | if (change & GTK_CSS_CHANGE_ANY_SIBLING) |
103 | return FALSE; |
104 | |
105 | /* Again, the cache is shared between all children of the parent. |
106 | * If the position is relevant, no child has the same style. |
107 | */ |
108 | if (change & (GTK_CSS_CHANGE_NTH_CHILD | GTK_CSS_CHANGE_NTH_LAST_CHILD)) |
109 | return FALSE; |
110 | |
111 | return TRUE; |
112 | } |
113 | |
114 | static guint |
115 | gtk_css_node_style_cache_decl_hash (gconstpointer item) |
116 | { |
117 | return gtk_css_node_declaration_hash (UNPACK_DECLARATION (item)) << 2 |
118 | | UNPACK_FLAGS (item); |
119 | } |
120 | |
121 | static gboolean |
122 | gtk_css_node_style_cache_decl_equal (gconstpointer item1, |
123 | gconstpointer item2) |
124 | { |
125 | if (UNPACK_FLAGS (item1) != UNPACK_FLAGS (item2)) |
126 | return FALSE; |
127 | |
128 | return gtk_css_node_declaration_equal (UNPACK_DECLARATION (item1), |
129 | UNPACK_DECLARATION (item2)); |
130 | } |
131 | |
132 | static void |
133 | gtk_css_node_style_cache_decl_free (gpointer item) |
134 | { |
135 | gtk_css_node_declaration_unref (UNPACK_DECLARATION (item)); |
136 | } |
137 | |
138 | GtkCssNodeStyleCache * |
139 | gtk_css_node_style_cache_insert (GtkCssNodeStyleCache *parent, |
140 | GtkCssNodeDeclaration *decl, |
141 | gboolean is_first, |
142 | gboolean is_last, |
143 | GtkCssStyle *style) |
144 | { |
145 | GtkCssNodeStyleCache *result; |
146 | |
147 | if (!may_be_stored_in_cache (style)) |
148 | return NULL; |
149 | |
150 | if (parent->children == NULL) |
151 | parent->children = g_hash_table_new_full (hash_func: gtk_css_node_style_cache_decl_hash, |
152 | key_equal_func: gtk_css_node_style_cache_decl_equal, |
153 | key_destroy_func: gtk_css_node_style_cache_decl_free, |
154 | value_destroy_func: (GDestroyNotify) gtk_css_node_style_cache_unref); |
155 | |
156 | result = gtk_css_node_style_cache_new (style); |
157 | |
158 | g_hash_table_insert (hash_table: parent->children, |
159 | PACK (gtk_css_node_declaration_ref (decl), is_first, is_last), |
160 | value: gtk_css_node_style_cache_ref (cache: result)); |
161 | |
162 | return result; |
163 | } |
164 | |
165 | GtkCssNodeStyleCache * |
166 | gtk_css_node_style_cache_lookup (GtkCssNodeStyleCache *parent, |
167 | const GtkCssNodeDeclaration *decl, |
168 | gboolean is_first, |
169 | gboolean is_last) |
170 | { |
171 | GtkCssNodeStyleCache *result; |
172 | |
173 | if (parent->children == NULL) |
174 | return NULL; |
175 | |
176 | result = g_hash_table_lookup (hash_table: parent->children, PACK (decl, is_first, is_last)); |
177 | if (result == NULL) |
178 | return NULL; |
179 | |
180 | return gtk_css_node_style_cache_ref (cache: result); |
181 | } |
182 | |
183 | |