1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2014 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 "gtkcsswidgetnodeprivate.h"
21
22 #include "gtkcontainerprivate.h"
23 #include "gtkcssanimatedstyleprivate.h"
24 #include "gtkprivate.h"
25 #include "gtksettingsprivate.h"
26 #include "gtkstylecontextprivate.h"
27 #include "gtkwidgetprivate.h"
28 /* widgets for special casing go here */
29 #include "gtkbox.h"
30
G_DEFINE_TYPE(GtkCssWidgetNode,gtk_css_widget_node,GTK_TYPE_CSS_NODE)31 G_DEFINE_TYPE (GtkCssWidgetNode, gtk_css_widget_node, GTK_TYPE_CSS_NODE)
32
33 static void
34 gtk_css_widget_node_finalize (GObject *object)
35 {
36 GtkCssWidgetNode *node = GTK_CSS_WIDGET_NODE (object);
37
38 g_object_unref (node->last_updated_style);
39
40 G_OBJECT_CLASS (gtk_css_widget_node_parent_class)->finalize (object);
41 }
42
43 static void
gtk_css_widget_node_style_changed(GtkCssNode * cssnode,GtkCssStyleChange * change)44 gtk_css_widget_node_style_changed (GtkCssNode *cssnode,
45 GtkCssStyleChange *change)
46 {
47 GtkCssWidgetNode *node;
48
49 node = GTK_CSS_WIDGET_NODE (cssnode);
50
51 if (node->widget)
52 gtk_widget_clear_path (node->widget);
53
54 GTK_CSS_NODE_CLASS (gtk_css_widget_node_parent_class)->style_changed (cssnode, change);
55 }
56
57 static gboolean
gtk_css_widget_node_queue_callback(GtkWidget * widget,GdkFrameClock * frame_clock,gpointer user_data)58 gtk_css_widget_node_queue_callback (GtkWidget *widget,
59 GdkFrameClock *frame_clock,
60 gpointer user_data)
61 {
62 GtkCssNode *node = user_data;
63
64 gtk_css_node_invalidate_frame_clock (node, TRUE);
65 _gtk_container_queue_restyle (GTK_CONTAINER (widget));
66
67 return G_SOURCE_CONTINUE;
68 }
69
70 static GtkCssStyle *
gtk_css_widget_node_update_style(GtkCssNode * cssnode,GtkCssChange change,gint64 timestamp,GtkCssStyle * style)71 gtk_css_widget_node_update_style (GtkCssNode *cssnode,
72 GtkCssChange change,
73 gint64 timestamp,
74 GtkCssStyle *style)
75 {
76 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (cssnode);
77
78 if (widget_node->widget != NULL)
79 {
80 GtkStyleContext *context = _gtk_widget_peek_style_context (widget_node->widget);
81 if (context)
82 gtk_style_context_clear_property_cache (context);
83 }
84
85 return GTK_CSS_NODE_CLASS (gtk_css_widget_node_parent_class)->update_style (cssnode, change, timestamp, style);
86 }
87
88 static void
gtk_css_widget_node_queue_validate(GtkCssNode * node)89 gtk_css_widget_node_queue_validate (GtkCssNode *node)
90 {
91 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (node);
92
93 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
94 if (GTK_IS_RESIZE_CONTAINER (widget_node->widget))
95 widget_node->validate_cb_id = gtk_widget_add_tick_callback (widget_node->widget,
96 gtk_css_widget_node_queue_callback,
97 node,
98 NULL);
99 G_GNUC_END_IGNORE_DEPRECATIONS
100 }
101
102 static void
gtk_css_widget_node_dequeue_validate(GtkCssNode * node)103 gtk_css_widget_node_dequeue_validate (GtkCssNode *node)
104 {
105 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (node);
106
107 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
108 if (GTK_IS_RESIZE_CONTAINER (widget_node->widget))
109 gtk_widget_remove_tick_callback (widget_node->widget,
110 widget_node->validate_cb_id);
111 G_GNUC_END_IGNORE_DEPRECATIONS
112 }
113
114 static void
gtk_css_widget_node_validate(GtkCssNode * node)115 gtk_css_widget_node_validate (GtkCssNode *node)
116 {
117 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (node);
118 GtkCssStyleChange change;
119 GtkCssStyle *style;
120
121 if (widget_node->widget == NULL)
122 return;
123
124 style = gtk_css_node_get_style (node);
125
126 gtk_css_style_change_init (&change, widget_node->last_updated_style, style);
127 if (gtk_css_style_change_has_change (&change))
128 {
129 GtkStyleContext *context;
130
131 context = _gtk_widget_peek_style_context (widget_node->widget);
132 if (context)
133 gtk_style_context_validate (context, &change);
134 else
135 _gtk_widget_style_context_invalidated (widget_node->widget);
136 g_set_object (&widget_node->last_updated_style, style);
137 }
138 gtk_css_style_change_finish (&change);
139 }
140
141 typedef GtkWidgetPath * (* GetPathForChildFunc) (GtkContainer *, GtkWidget *);
142
143 static gboolean
widget_needs_widget_path(GtkWidget * widget)144 widget_needs_widget_path (GtkWidget *widget)
145 {
146 static GetPathForChildFunc funcs[2];
147 GtkContainerClass *class;
148 GtkWidget *parent;
149 GetPathForChildFunc parent_func;
150 guint i;
151
152 if (G_UNLIKELY (funcs[0] == NULL))
153 {
154 i = 0;
155
156 class = (GtkContainerClass*)g_type_class_ref (GTK_TYPE_CONTAINER);
157 funcs[i++] = class->get_path_for_child;
158 g_type_class_unref (class);
159
160 class = (GtkContainerClass*)g_type_class_ref (GTK_TYPE_BOX);
161 funcs[i++] = class->get_path_for_child;
162 g_type_class_unref (class);
163
164 g_assert (i == G_N_ELEMENTS (funcs));
165 }
166
167 parent = _gtk_widget_get_parent (widget);
168 if (parent == NULL)
169 return FALSE;
170
171 parent_func = GTK_CONTAINER_GET_CLASS (GTK_CONTAINER (parent))->get_path_for_child;
172 for (i = 0; i < G_N_ELEMENTS (funcs); i++)
173 {
174 if (funcs[i] == parent_func)
175 return FALSE;
176 }
177
178 return TRUE;
179 }
180
181 gboolean
gtk_css_widget_node_init_matcher(GtkCssNode * node,GtkCssMatcher * matcher)182 gtk_css_widget_node_init_matcher (GtkCssNode *node,
183 GtkCssMatcher *matcher)
184 {
185 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (node);
186
187 if (widget_node->widget == NULL)
188 return FALSE;
189
190 if (!widget_needs_widget_path (widget_node->widget))
191 return GTK_CSS_NODE_CLASS (gtk_css_widget_node_parent_class)->init_matcher (node, matcher);
192
193 return _gtk_css_matcher_init (matcher,
194 gtk_widget_get_path (widget_node->widget),
195 gtk_css_node_get_declaration (node));
196 }
197
198 static GtkWidgetPath *
gtk_css_widget_node_create_widget_path(GtkCssNode * node)199 gtk_css_widget_node_create_widget_path (GtkCssNode *node)
200 {
201 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (node);
202 GtkWidgetPath *path;
203 guint length;
204
205 if (widget_node->widget == NULL)
206 path = gtk_widget_path_new ();
207 else
208 path = _gtk_widget_create_path (widget_node->widget);
209
210 length = gtk_widget_path_length (path);
211 if (length > 0)
212 {
213 gtk_css_node_declaration_add_to_widget_path (gtk_css_node_get_declaration (node),
214 path,
215 length - 1);
216 }
217
218 return path;
219 }
220
221 static const GtkWidgetPath *
gtk_css_widget_node_get_widget_path(GtkCssNode * node)222 gtk_css_widget_node_get_widget_path (GtkCssNode *node)
223 {
224 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (node);
225
226 if (widget_node->widget == NULL)
227 return NULL;
228
229 return gtk_widget_get_path (widget_node->widget);
230 }
231
232 static GtkStyleProviderPrivate *
gtk_css_widget_node_get_style_provider(GtkCssNode * node)233 gtk_css_widget_node_get_style_provider (GtkCssNode *node)
234 {
235 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (node);
236 GtkStyleContext *context;
237 GtkStyleCascade *cascade;
238 GtkSettings *settings;
239
240 if (widget_node->widget == NULL)
241 return NULL;
242
243 context = _gtk_widget_peek_style_context (widget_node->widget);
244 if (context)
245 return gtk_style_context_get_style_provider (context);
246
247 settings = gtk_widget_get_settings (widget_node->widget);
248 if (!settings)
249 return NULL;
250
251 cascade = _gtk_settings_get_style_cascade (gtk_widget_get_settings (widget_node->widget),
252 gtk_widget_get_scale_factor (widget_node->widget));
253 return GTK_STYLE_PROVIDER_PRIVATE (cascade);
254 }
255
256 static GdkFrameClock *
gtk_css_widget_node_get_frame_clock(GtkCssNode * node)257 gtk_css_widget_node_get_frame_clock (GtkCssNode *node)
258 {
259 GtkCssWidgetNode *widget_node = GTK_CSS_WIDGET_NODE (node);
260
261 if (widget_node->widget == NULL)
262 return NULL;
263
264 if (!gtk_settings_get_enable_animations (gtk_widget_get_settings (widget_node->widget)))
265 return NULL;
266
267 return gtk_widget_get_frame_clock (widget_node->widget);
268 }
269
270 static void
gtk_css_widget_node_class_init(GtkCssWidgetNodeClass * klass)271 gtk_css_widget_node_class_init (GtkCssWidgetNodeClass *klass)
272 {
273 GtkCssNodeClass *node_class = GTK_CSS_NODE_CLASS (klass);
274 GObjectClass *object_class = G_OBJECT_CLASS (klass);
275
276 object_class->finalize = gtk_css_widget_node_finalize;
277 node_class->update_style = gtk_css_widget_node_update_style;
278 node_class->validate = gtk_css_widget_node_validate;
279 node_class->queue_validate = gtk_css_widget_node_queue_validate;
280 node_class->dequeue_validate = gtk_css_widget_node_dequeue_validate;
281 node_class->init_matcher = gtk_css_widget_node_init_matcher;
282 node_class->create_widget_path = gtk_css_widget_node_create_widget_path;
283 node_class->get_widget_path = gtk_css_widget_node_get_widget_path;
284 node_class->get_style_provider = gtk_css_widget_node_get_style_provider;
285 node_class->get_frame_clock = gtk_css_widget_node_get_frame_clock;
286 node_class->style_changed = gtk_css_widget_node_style_changed;
287 }
288
289 static void
gtk_css_widget_node_init(GtkCssWidgetNode * node)290 gtk_css_widget_node_init (GtkCssWidgetNode *node)
291 {
292 node->last_updated_style = g_object_ref (gtk_css_static_style_get_default ());
293 }
294
295 GtkCssNode *
gtk_css_widget_node_new(GtkWidget * widget)296 gtk_css_widget_node_new (GtkWidget *widget)
297 {
298 GtkCssWidgetNode *result;
299
300 gtk_internal_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
301
302 result = g_object_new (GTK_TYPE_CSS_WIDGET_NODE, NULL);
303 result->widget = widget;
304 gtk_css_node_set_visible (GTK_CSS_NODE (result),
305 _gtk_widget_get_visible (widget));
306
307 return GTK_CSS_NODE (result);
308 }
309
310 void
gtk_css_widget_node_widget_destroyed(GtkCssWidgetNode * node)311 gtk_css_widget_node_widget_destroyed (GtkCssWidgetNode *node)
312 {
313 gtk_internal_return_if_fail (GTK_IS_CSS_WIDGET_NODE (node));
314 gtk_internal_return_if_fail (node->widget != NULL);
315
316 node->widget = NULL;
317 /* Contents of this node are now undefined.
318 * So we don't clear the style or anything.
319 */
320 }
321
322 GtkWidget *
gtk_css_widget_node_get_widget(GtkCssWidgetNode * node)323 gtk_css_widget_node_get_widget (GtkCssWidgetNode *node)
324 {
325 gtk_internal_return_val_if_fail (GTK_IS_CSS_WIDGET_NODE (node), NULL);
326
327 return node->widget;
328 }
329
330