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 "gtkcssnodeprivate.h"
21
22 #include "gtkcssanimatedstyleprivate.h"
23 #include "gtkcsssectionprivate.h"
24 #include "gtkcssstylepropertyprivate.h"
25 #include "gtkintl.h"
26 #include "gtkmarshalers.h"
27 #include "gtksettingsprivate.h"
28 #include "gtktypebuiltins.h"
29
30 /*
31 * CSS nodes are the backbone of the GtkStyleContext implementation and
32 * replace the role that GtkWidgetPath played in the past. A CSS node has
33 * an element name and a state, and can have an id and style classes, which
34 * is what is needed to determine the matching CSS selectors. CSS nodes have
35 * a 'visible' property, which makes it possible to temporarily 'hide' them
36 * from CSS matching - e.g. an invisible node will not affect :nth-child
37 * matching and so forth.
38 *
39 * The API to manage states, names, ids and classes of CSS nodes is:
40 * - gtk_css_node_get/set_state. States are represented as GtkStateFlags
41 * - gtk_css_node_get/set_name. Names are represented as interned strings
42 * - gtk_css_node_get/set_id. Ids are represented as interned strings
43 * - gtk_css_node_add/remove/has_class and gtk_css_node_list_classes. Style
44 * classes are represented as quarks.
45 *
46 * CSS nodes are organized in a dom-like tree, and there is API to navigate
47 * and manipulate this tree:
48 * - gtk_css_node_set_parent
49 * - gtk_css_node_insert_before/after
50 * - gtk_css_node_get_parent
51 * - gtk_css_node_get_first/last_child
52 * - gtk_css_node_get_previous/next_sibling
53 * Note that parents keep a reference on their children in this tree.
54 *
55 * Every widget has one or more CSS nodes - the first one gets created
56 * automatically by GtkStyleContext. To set the name of the main node,
57 * call gtk_widget_class_set_css_name() in class_init(). Widget implementations
58 * can and should add subnodes as suitable.
59 *
60 * Best practice is:
61 * - For permanent subnodes, create them in init(), and keep a pointer
62 * to the node (you don't have to keep a reference, cleanup will be
63 * automatic by means of the parent node getting cleaned up by the
64 * style context).
65 * - For transient nodes, create/destroy them when the conditions that
66 * warrant their existence change.
67 * - Keep the state of all your nodes up-to-date. This probably requires
68 * a ::state-flags-changed (and possibly ::direction-changed) handler,
69 * as well as code to update the state in other places. Note that GTK+
70 * does this automatically for the widget's main CSS node.
71 * - The sibling ordering in the CSS node tree is supposed to correspond
72 * to the visible order of content: top-to-bottom and left-to-right.
73 * Reorder your nodes to maintain this correlation. In particular for
74 * horizontally layed out widgets, this will require listening to
75 * ::direction-changed.
76 * - The draw function should just use gtk_style_context_save_to_node() to
77 * 'switch' to the right node, not make any other changes to the style
78 * context.
79 *
80 * A noteworthy difference between gtk_style_context_save() and
81 * gtk_style_context_save_to_node() is that the former inherits all the
82 * style classes from the main CSS node, which often leads to unintended
83 * inheritance.
84 */
85
86 /* When these change we do a full restyling. Otherwise we try to figure out
87 * if we need to change things. */
88 #define GTK_CSS_RADICAL_CHANGE (GTK_CSS_CHANGE_ID | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_SOURCE | GTK_CSS_CHANGE_PARENT_STYLE)
89
90 G_DEFINE_TYPE (GtkCssNode, gtk_css_node, G_TYPE_OBJECT)
91
92 enum {
93 NODE_ADDED,
94 NODE_REMOVED,
95 STYLE_CHANGED,
96 LAST_SIGNAL
97 };
98
99 enum {
100 PROP_0,
101 PROP_CLASSES,
102 PROP_ID,
103 PROP_NAME,
104 PROP_STATE,
105 PROP_VISIBLE,
106 PROP_WIDGET_TYPE,
107 NUM_PROPERTIES
108 };
109
110 struct _GtkCssNodeStyleChange {
111 GtkCssStyle *old_style;
112 GtkCssStyle *new_style;
113 };
114
115 static guint cssnode_signals[LAST_SIGNAL] = { 0 };
116 static GParamSpec *cssnode_properties[NUM_PROPERTIES];
117
118 static GtkStyleProviderPrivate *
gtk_css_node_get_style_provider_or_null(GtkCssNode * cssnode)119 gtk_css_node_get_style_provider_or_null (GtkCssNode *cssnode)
120 {
121 return GTK_CSS_NODE_GET_CLASS (cssnode)->get_style_provider (cssnode);
122 }
123
124 static void
gtk_css_node_set_invalid(GtkCssNode * node,gboolean invalid)125 gtk_css_node_set_invalid (GtkCssNode *node,
126 gboolean invalid)
127 {
128 if (node->invalid == invalid)
129 return;
130
131 node->invalid = invalid;
132
133 if (node->visible)
134 {
135 if (node->parent)
136 {
137 if (invalid)
138 gtk_css_node_set_invalid (node->parent, TRUE);
139 }
140 else
141 {
142 if (invalid)
143 GTK_CSS_NODE_GET_CLASS (node)->queue_validate (node);
144 else
145 GTK_CSS_NODE_GET_CLASS (node)->dequeue_validate (node);
146 }
147 }
148 }
149
150 static void
gtk_css_node_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)151 gtk_css_node_get_property (GObject *object,
152 guint property_id,
153 GValue *value,
154 GParamSpec *pspec)
155 {
156 GtkCssNode *cssnode = GTK_CSS_NODE (object);
157
158 switch (property_id)
159 {
160 case PROP_CLASSES:
161 g_value_take_boxed (value, gtk_css_node_get_classes (cssnode));
162 break;
163
164 case PROP_ID:
165 g_value_set_string (value, gtk_css_node_get_id (cssnode));
166 break;
167
168 case PROP_NAME:
169 g_value_set_string (value, gtk_css_node_get_name (cssnode));
170 break;
171
172 case PROP_STATE:
173 g_value_set_flags (value, gtk_css_node_get_state (cssnode));
174 break;
175
176 case PROP_VISIBLE:
177 g_value_set_boolean (value, gtk_css_node_get_visible (cssnode));
178 break;
179
180 case PROP_WIDGET_TYPE:
181 g_value_set_gtype (value, gtk_css_node_get_widget_type (cssnode));
182 break;
183
184 default:
185 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
186 }
187 }
188
189 static void
gtk_css_node_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)190 gtk_css_node_set_property (GObject *object,
191 guint property_id,
192 const GValue *value,
193 GParamSpec *pspec)
194 {
195 GtkCssNode *cssnode = GTK_CSS_NODE (object);
196
197 switch (property_id)
198 {
199 case PROP_CLASSES:
200 gtk_css_node_set_classes (cssnode, g_value_get_boxed (value));
201 break;
202
203 case PROP_ID:
204 gtk_css_node_set_id (cssnode, g_value_get_string (value));
205 break;
206
207 case PROP_NAME:
208 gtk_css_node_set_name (cssnode, g_value_get_string (value));
209 break;
210
211 case PROP_STATE:
212 gtk_css_node_set_state (cssnode, g_value_get_flags (value));
213 break;
214
215 case PROP_VISIBLE:
216 gtk_css_node_set_visible (cssnode, g_value_get_boolean (value));
217 break;
218
219 case PROP_WIDGET_TYPE:
220 gtk_css_node_set_widget_type (cssnode, g_value_get_gtype (value));
221 break;
222
223 default:
224 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
225 }
226 }
227
228 static void
gtk_css_node_dispose(GObject * object)229 gtk_css_node_dispose (GObject *object)
230 {
231 GtkCssNode *cssnode = GTK_CSS_NODE (object);
232
233 while (cssnode->first_child)
234 {
235 gtk_css_node_set_parent (cssnode->first_child, NULL);
236 }
237
238 gtk_css_node_set_invalid (cssnode, FALSE);
239
240 g_clear_pointer (&cssnode->cache, gtk_css_node_style_cache_unref);
241
242 G_OBJECT_CLASS (gtk_css_node_parent_class)->dispose (object);
243 }
244
245 static void
gtk_css_node_finalize(GObject * object)246 gtk_css_node_finalize (GObject *object)
247 {
248 GtkCssNode *cssnode = GTK_CSS_NODE (object);
249
250 if (cssnode->style)
251 g_object_unref (cssnode->style);
252 gtk_css_node_declaration_unref (cssnode->decl);
253
254 G_OBJECT_CLASS (gtk_css_node_parent_class)->finalize (object);
255 }
256
257 static gboolean
gtk_css_node_is_first_child(GtkCssNode * node)258 gtk_css_node_is_first_child (GtkCssNode *node)
259 {
260 GtkCssNode *iter;
261
262 for (iter = node->previous_sibling;
263 iter != NULL;
264 iter = iter->previous_sibling)
265 {
266 if (iter->visible)
267 return FALSE;
268 }
269 return TRUE;
270 }
271
272 static gboolean
gtk_css_node_is_last_child(GtkCssNode * node)273 gtk_css_node_is_last_child (GtkCssNode *node)
274 {
275 GtkCssNode *iter;
276
277 for (iter = node->next_sibling;
278 iter != NULL;
279 iter = iter->next_sibling)
280 {
281 if (iter->visible)
282 return FALSE;
283 }
284 return TRUE;
285 }
286
287 static gboolean
may_use_global_parent_cache(GtkCssNode * node)288 may_use_global_parent_cache (GtkCssNode *node)
289 {
290 GtkStyleProviderPrivate *provider;
291 GtkCssNode *parent;
292
293 parent = gtk_css_node_get_parent (node);
294 if (parent == NULL)
295 return FALSE;
296
297 provider = gtk_css_node_get_style_provider_or_null (node);
298 if (provider != NULL && provider != gtk_css_node_get_style_provider (parent))
299 return FALSE;
300
301 return TRUE;
302 }
303
304 static GtkCssStyle *
lookup_in_global_parent_cache(GtkCssNode * node,const GtkCssNodeDeclaration * decl)305 lookup_in_global_parent_cache (GtkCssNode *node,
306 const GtkCssNodeDeclaration *decl)
307 {
308 GtkCssNode *parent;
309
310 parent = node->parent;
311
312 if (parent == NULL ||
313 !may_use_global_parent_cache (node))
314 return NULL;
315
316 if (parent->cache == NULL)
317 return NULL;
318
319 g_assert (node->cache == NULL);
320 node->cache = gtk_css_node_style_cache_lookup (parent->cache,
321 decl,
322 gtk_css_node_is_first_child (node),
323 gtk_css_node_is_last_child (node));
324 if (node->cache == NULL)
325 return NULL;
326
327 return gtk_css_node_style_cache_get_style (node->cache);
328 }
329
330 static void
store_in_global_parent_cache(GtkCssNode * node,const GtkCssNodeDeclaration * decl,GtkCssStyle * style)331 store_in_global_parent_cache (GtkCssNode *node,
332 const GtkCssNodeDeclaration *decl,
333 GtkCssStyle *style)
334 {
335 GtkCssNode *parent;
336
337 g_assert (GTK_IS_CSS_STATIC_STYLE (style));
338
339 parent = node->parent;
340
341 if (parent == NULL ||
342 !may_use_global_parent_cache (node))
343 return;
344
345 if (parent->cache == NULL)
346 parent->cache = gtk_css_node_style_cache_new (parent->style);
347
348 node->cache = gtk_css_node_style_cache_insert (parent->cache,
349 (GtkCssNodeDeclaration *) decl,
350 gtk_css_node_is_first_child (node),
351 gtk_css_node_is_last_child (node),
352 style);
353 }
354
355 static GtkCssStyle *
gtk_css_node_create_style(GtkCssNode * cssnode)356 gtk_css_node_create_style (GtkCssNode *cssnode)
357 {
358 const GtkCssNodeDeclaration *decl;
359 GtkCssMatcher matcher;
360 GtkCssStyle *parent;
361 GtkCssStyle *style;
362
363 decl = gtk_css_node_get_declaration (cssnode);
364 parent = cssnode->parent ? cssnode->parent->style : NULL;
365
366 style = lookup_in_global_parent_cache (cssnode, decl);
367 if (style)
368 return g_object_ref (style);
369
370 if (gtk_css_node_init_matcher (cssnode, &matcher))
371 style = gtk_css_static_style_new_compute (gtk_css_node_get_style_provider (cssnode),
372 &matcher,
373 parent);
374 else
375 style = gtk_css_static_style_new_compute (gtk_css_node_get_style_provider (cssnode),
376 NULL,
377 parent);
378
379 store_in_global_parent_cache (cssnode, decl, style);
380
381 return style;
382 }
383
384 static gboolean
should_create_transitions(GtkCssChange change)385 should_create_transitions (GtkCssChange change)
386 {
387 return (change & GTK_CSS_CHANGE_ANIMATIONS) == 0;
388 }
389
390 static gboolean
gtk_css_style_needs_recreation(GtkCssStyle * style,GtkCssChange change)391 gtk_css_style_needs_recreation (GtkCssStyle *style,
392 GtkCssChange change)
393 {
394 /* Try to avoid invalidating if we can */
395 if (change & GTK_CSS_RADICAL_CHANGE)
396 return TRUE;
397
398 if (GTK_IS_CSS_ANIMATED_STYLE (style))
399 style = GTK_CSS_ANIMATED_STYLE (style)->style;
400
401 if (gtk_css_static_style_get_change (GTK_CSS_STATIC_STYLE (style)) & change)
402 return TRUE;
403 else
404 return FALSE;
405 }
406
407 static GtkCssStyle *
gtk_css_node_real_update_style(GtkCssNode * cssnode,GtkCssChange change,gint64 timestamp,GtkCssStyle * style)408 gtk_css_node_real_update_style (GtkCssNode *cssnode,
409 GtkCssChange change,
410 gint64 timestamp,
411 GtkCssStyle *style)
412 {
413 GtkCssStyle *static_style, *new_static_style, *new_style;
414
415 if (GTK_IS_CSS_ANIMATED_STYLE (style))
416 {
417 static_style = GTK_CSS_ANIMATED_STYLE (style)->style;
418 }
419 else
420 {
421 static_style = style;
422 }
423
424 if (gtk_css_style_needs_recreation (static_style, change))
425 new_static_style = gtk_css_node_create_style (cssnode);
426 else
427 new_static_style = g_object_ref (static_style);
428
429 if (new_static_style != static_style || (change & GTK_CSS_CHANGE_ANIMATIONS))
430 {
431 GtkCssNode *parent = gtk_css_node_get_parent (cssnode);
432 new_style = gtk_css_animated_style_new (new_static_style,
433 parent ? gtk_css_node_get_style (parent) : NULL,
434 timestamp,
435 gtk_css_node_get_style_provider (cssnode),
436 should_create_transitions (change) ? style : NULL);
437
438 /* Clear the cache again, the static style we looked up above
439 * may have populated it. */
440 g_clear_pointer (&cssnode->cache, gtk_css_node_style_cache_unref);
441 }
442 else if (static_style != style && (change & GTK_CSS_CHANGE_TIMESTAMP))
443 {
444 new_style = gtk_css_animated_style_new_advance (GTK_CSS_ANIMATED_STYLE (style),
445 static_style,
446 timestamp);
447 }
448 else
449 {
450 new_style = g_object_ref (style);
451 }
452
453 if (!gtk_css_style_is_static (new_style))
454 gtk_css_node_set_invalid (cssnode, TRUE);
455
456 g_object_unref (new_static_style);
457
458 return new_style;
459 }
460
461 static void
gtk_css_node_real_invalidate(GtkCssNode * node)462 gtk_css_node_real_invalidate (GtkCssNode *node)
463 {
464 }
465
466 static void
gtk_css_node_real_queue_validate(GtkCssNode * node)467 gtk_css_node_real_queue_validate (GtkCssNode *node)
468 {
469 }
470
471 static void
gtk_css_node_real_dequeue_validate(GtkCssNode * node)472 gtk_css_node_real_dequeue_validate (GtkCssNode *node)
473 {
474 }
475
476 static void
gtk_css_node_real_validate(GtkCssNode * node)477 gtk_css_node_real_validate (GtkCssNode *node)
478 {
479 }
480
481 gboolean
gtk_css_node_real_init_matcher(GtkCssNode * cssnode,GtkCssMatcher * matcher)482 gtk_css_node_real_init_matcher (GtkCssNode *cssnode,
483 GtkCssMatcher *matcher)
484 {
485 _gtk_css_matcher_node_init (matcher, cssnode);
486
487 return TRUE;
488 }
489
490 static GtkWidgetPath *
gtk_css_node_real_create_widget_path(GtkCssNode * cssnode)491 gtk_css_node_real_create_widget_path (GtkCssNode *cssnode)
492 {
493 return gtk_widget_path_new ();
494 }
495
496 static const GtkWidgetPath *
gtk_css_node_real_get_widget_path(GtkCssNode * cssnode)497 gtk_css_node_real_get_widget_path (GtkCssNode *cssnode)
498 {
499 return NULL;
500 }
501
502 static GtkStyleProviderPrivate *
gtk_css_node_real_get_style_provider(GtkCssNode * cssnode)503 gtk_css_node_real_get_style_provider (GtkCssNode *cssnode)
504 {
505 return NULL;
506 }
507
508 static GdkFrameClock *
gtk_css_node_real_get_frame_clock(GtkCssNode * cssnode)509 gtk_css_node_real_get_frame_clock (GtkCssNode *cssnode)
510 {
511 return NULL;
512 }
513
514 static void
gtk_css_node_real_node_removed(GtkCssNode * parent,GtkCssNode * node,GtkCssNode * previous)515 gtk_css_node_real_node_removed (GtkCssNode *parent,
516 GtkCssNode *node,
517 GtkCssNode *previous)
518 {
519 if (node->previous_sibling)
520 node->previous_sibling->next_sibling = node->next_sibling;
521 else
522 node->parent->first_child = node->next_sibling;
523
524 if (node->next_sibling)
525 node->next_sibling->previous_sibling = node->previous_sibling;
526 else
527 node->parent->last_child = node->previous_sibling;
528
529 node->previous_sibling = NULL;
530 node->next_sibling = NULL;
531 node->parent = NULL;
532 }
533
534 static void
gtk_css_node_real_node_added(GtkCssNode * parent,GtkCssNode * node,GtkCssNode * new_previous)535 gtk_css_node_real_node_added (GtkCssNode *parent,
536 GtkCssNode *node,
537 GtkCssNode *new_previous)
538 {
539 if (new_previous)
540 {
541 node->previous_sibling = new_previous;
542 node->next_sibling = new_previous->next_sibling;
543 new_previous->next_sibling = node;
544 }
545 else
546 {
547 node->next_sibling = parent->first_child;
548 parent->first_child = node;
549 }
550
551 if (node->next_sibling)
552 node->next_sibling->previous_sibling = node;
553 else
554 parent->last_child = node;
555
556 node->parent = parent;
557 }
558
559 static void
gtk_css_node_real_style_changed(GtkCssNode * cssnode,GtkCssStyleChange * change)560 gtk_css_node_real_style_changed (GtkCssNode *cssnode,
561 GtkCssStyleChange *change)
562 {
563 g_set_object (&cssnode->style, gtk_css_style_change_get_new_style (change));
564 }
565
566 static void
gtk_css_node_class_init(GtkCssNodeClass * klass)567 gtk_css_node_class_init (GtkCssNodeClass *klass)
568 {
569 GObjectClass *object_class = G_OBJECT_CLASS (klass);
570
571 object_class->get_property = gtk_css_node_get_property;
572 object_class->set_property = gtk_css_node_set_property;
573 object_class->dispose = gtk_css_node_dispose;
574 object_class->finalize = gtk_css_node_finalize;
575
576 klass->update_style = gtk_css_node_real_update_style;
577 klass->invalidate = gtk_css_node_real_invalidate;
578 klass->validate = gtk_css_node_real_validate;
579 klass->queue_validate = gtk_css_node_real_queue_validate;
580 klass->dequeue_validate = gtk_css_node_real_dequeue_validate;
581 klass->init_matcher = gtk_css_node_real_init_matcher;
582 klass->create_widget_path = gtk_css_node_real_create_widget_path;
583 klass->get_widget_path = gtk_css_node_real_get_widget_path;
584 klass->get_style_provider = gtk_css_node_real_get_style_provider;
585 klass->get_frame_clock = gtk_css_node_real_get_frame_clock;
586
587 klass->node_added = gtk_css_node_real_node_added;
588 klass->node_removed = gtk_css_node_real_node_removed;
589 klass->style_changed = gtk_css_node_real_style_changed;
590
591 cssnode_signals[NODE_ADDED] =
592 g_signal_new (I_("node-added"),
593 G_TYPE_FROM_CLASS (object_class),
594 G_SIGNAL_RUN_LAST,
595 G_STRUCT_OFFSET (GtkCssNodeClass, node_added),
596 NULL, NULL,
597 _gtk_marshal_VOID__OBJECT_OBJECT,
598 G_TYPE_NONE, 2,
599 GTK_TYPE_CSS_NODE, GTK_TYPE_CSS_NODE);
600 g_signal_set_va_marshaller (cssnode_signals[NODE_ADDED],
601 G_TYPE_FROM_CLASS (klass),
602 _gtk_marshal_VOID__OBJECT_OBJECTv);
603
604 cssnode_signals[NODE_REMOVED] =
605 g_signal_new (I_("node-removed"),
606 G_TYPE_FROM_CLASS (object_class),
607 G_SIGNAL_RUN_LAST,
608 G_STRUCT_OFFSET (GtkCssNodeClass, node_removed),
609 NULL, NULL,
610 _gtk_marshal_VOID__OBJECT_OBJECT,
611 G_TYPE_NONE, 2,
612 GTK_TYPE_CSS_NODE, GTK_TYPE_CSS_NODE);
613 g_signal_set_va_marshaller (cssnode_signals[NODE_REMOVED],
614 G_TYPE_FROM_CLASS (klass),
615 _gtk_marshal_VOID__OBJECT_OBJECTv);
616
617 cssnode_signals[STYLE_CHANGED] =
618 g_signal_new (I_("style-changed"),
619 G_TYPE_FROM_CLASS (object_class),
620 G_SIGNAL_RUN_LAST,
621 G_STRUCT_OFFSET (GtkCssNodeClass, style_changed),
622 NULL, NULL,
623 NULL,
624 G_TYPE_NONE, 1,
625 G_TYPE_POINTER);
626
627 cssnode_properties[PROP_CLASSES] =
628 g_param_spec_boxed ("classes", P_("Style Classes"), P_("List of classes"),
629 G_TYPE_STRV,
630 G_PARAM_READWRITE
631 | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
632 cssnode_properties[PROP_ID] =
633 g_param_spec_string ("id", P_("ID"), P_("Unique ID"),
634 NULL,
635 G_PARAM_READWRITE
636 | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
637 cssnode_properties[PROP_NAME] =
638 g_param_spec_string ("name", P_("Name"), "Name identifying the type of node",
639 NULL,
640 G_PARAM_READWRITE
641 | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
642 cssnode_properties[PROP_STATE] =
643 g_param_spec_flags ("state", P_("State"), P_("State flags"),
644 GTK_TYPE_STATE_FLAGS,
645 0,
646 G_PARAM_READWRITE
647 | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
648 cssnode_properties[PROP_VISIBLE] =
649 g_param_spec_boolean ("visible", P_("Visible"), P_("If other nodes can see this node"),
650 TRUE,
651 G_PARAM_READWRITE
652 | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
653 cssnode_properties[PROP_WIDGET_TYPE] =
654 g_param_spec_gtype ("widget-type", P_("Widget type"), P_("GType of the widget"),
655 G_TYPE_NONE,
656 G_PARAM_READWRITE
657 | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
658
659 g_object_class_install_properties (object_class, NUM_PROPERTIES, cssnode_properties);
660 }
661
662 static void
gtk_css_node_init(GtkCssNode * cssnode)663 gtk_css_node_init (GtkCssNode *cssnode)
664 {
665 cssnode->decl = gtk_css_node_declaration_new ();
666
667 cssnode->style = g_object_ref (gtk_css_static_style_get_default ());
668
669 cssnode->visible = TRUE;
670 }
671
672 /**
673 * gtk_css_node_new:
674 *
675 * Creates a new CSS node.
676 *
677 * Returns: (transfer full): the new CSS node
678 */
679 GtkCssNode *
gtk_css_node_new(void)680 gtk_css_node_new (void)
681 {
682 return g_object_new (GTK_TYPE_CSS_NODE, NULL);
683 }
684
685 static GdkFrameClock *
gtk_css_node_get_frame_clock_or_null(GtkCssNode * cssnode)686 gtk_css_node_get_frame_clock_or_null (GtkCssNode *cssnode)
687 {
688 while (cssnode->parent)
689 cssnode = cssnode->parent;
690
691 return GTK_CSS_NODE_GET_CLASS (cssnode)->get_frame_clock (cssnode);
692 }
693
694 static gint64
gtk_css_node_get_timestamp(GtkCssNode * cssnode)695 gtk_css_node_get_timestamp (GtkCssNode *cssnode)
696 {
697 GdkFrameClock *frameclock;
698
699 frameclock = gtk_css_node_get_frame_clock_or_null (cssnode);
700 if (frameclock == NULL)
701 return 0;
702
703 return gdk_frame_clock_get_frame_time (frameclock);
704 }
705
706 static void
gtk_css_node_parent_was_unset(GtkCssNode * node)707 gtk_css_node_parent_was_unset (GtkCssNode *node)
708 {
709 if (node->visible && node->invalid)
710 GTK_CSS_NODE_GET_CLASS (node)->queue_validate (node);
711 }
712
713 static void
gtk_css_node_parent_will_be_set(GtkCssNode * node)714 gtk_css_node_parent_will_be_set (GtkCssNode *node)
715 {
716 if (node->visible && node->invalid)
717 GTK_CSS_NODE_GET_CLASS (node)->dequeue_validate (node);
718 }
719
720 static void
gtk_css_node_invalidate_style(GtkCssNode * cssnode)721 gtk_css_node_invalidate_style (GtkCssNode *cssnode)
722 {
723 if (cssnode->style_is_invalid)
724 return;
725
726 cssnode->style_is_invalid = TRUE;
727 gtk_css_node_set_invalid (cssnode, TRUE);
728
729 if (cssnode->first_child)
730 gtk_css_node_invalidate_style (cssnode->first_child);
731
732 if (cssnode->next_sibling)
733 gtk_css_node_invalidate_style (cssnode->next_sibling);
734 }
735
736 static void
gtk_css_node_reposition(GtkCssNode * node,GtkCssNode * new_parent,GtkCssNode * previous)737 gtk_css_node_reposition (GtkCssNode *node,
738 GtkCssNode *new_parent,
739 GtkCssNode *previous)
740 {
741 GtkCssNode *old_parent;
742
743 g_assert (! (new_parent == NULL && previous != NULL));
744
745 old_parent = node->parent;
746 /* Take a reference here so the whole function has a reference */
747 g_object_ref (node);
748
749 if (node->visible)
750 {
751 if (node->next_sibling)
752 gtk_css_node_invalidate (node->next_sibling,
753 GTK_CSS_CHANGE_ANY_SIBLING
754 | GTK_CSS_CHANGE_NTH_CHILD
755 | (node->previous_sibling ? 0 : GTK_CSS_CHANGE_FIRST_CHILD));
756 else if (node->previous_sibling)
757 gtk_css_node_invalidate (node->previous_sibling, GTK_CSS_CHANGE_LAST_CHILD);
758 }
759
760 if (old_parent != NULL)
761 {
762 g_signal_emit (old_parent, cssnode_signals[NODE_REMOVED], 0, node, node->previous_sibling);
763 if (old_parent->first_child && node->visible)
764 gtk_css_node_invalidate (old_parent->first_child, GTK_CSS_CHANGE_NTH_LAST_CHILD);
765 }
766
767 if (old_parent != new_parent)
768 {
769 if (old_parent == NULL)
770 {
771 gtk_css_node_parent_will_be_set (node);
772 }
773 else
774 {
775 g_object_unref (node);
776 }
777
778 if (gtk_css_node_get_style_provider_or_null (node) == NULL)
779 gtk_css_node_invalidate_style_provider (node);
780 gtk_css_node_invalidate (node, GTK_CSS_CHANGE_TIMESTAMP | GTK_CSS_CHANGE_ANIMATIONS);
781
782 if (new_parent)
783 {
784 g_object_ref (node);
785
786 if (node->pending_changes)
787 new_parent->needs_propagation = TRUE;
788 if (node->invalid && node->visible)
789 gtk_css_node_set_invalid (new_parent, TRUE);
790 }
791 else
792 {
793 gtk_css_node_parent_was_unset (node);
794 }
795 }
796
797 if (new_parent)
798 {
799 g_signal_emit (new_parent, cssnode_signals[NODE_ADDED], 0, node, previous);
800 if (node->visible)
801 gtk_css_node_invalidate (new_parent->first_child, GTK_CSS_CHANGE_NTH_LAST_CHILD);
802 }
803
804 if (node->visible)
805 {
806 if (node->next_sibling)
807 {
808 if (node->previous_sibling == NULL)
809 gtk_css_node_invalidate (node->next_sibling, GTK_CSS_CHANGE_FIRST_CHILD);
810 else
811 gtk_css_node_invalidate_style (node->next_sibling);
812 }
813 else if (node->previous_sibling)
814 {
815 gtk_css_node_invalidate (node->previous_sibling, GTK_CSS_CHANGE_LAST_CHILD);
816 }
817 }
818 else
819 {
820 if (node->next_sibling)
821 gtk_css_node_invalidate_style (node->next_sibling);
822 }
823
824 gtk_css_node_invalidate (node, GTK_CSS_CHANGE_ANY_PARENT
825 | GTK_CSS_CHANGE_ANY_SIBLING
826 | GTK_CSS_CHANGE_NTH_CHILD
827 | (node->previous_sibling ? 0 : GTK_CSS_CHANGE_FIRST_CHILD)
828 | (node->next_sibling ? 0 : GTK_CSS_CHANGE_LAST_CHILD));
829
830 g_object_unref (node);
831 }
832
833 void
gtk_css_node_set_parent(GtkCssNode * node,GtkCssNode * parent)834 gtk_css_node_set_parent (GtkCssNode *node,
835 GtkCssNode *parent)
836 {
837 if (node->parent == parent)
838 return;
839
840 gtk_css_node_reposition (node, parent, parent ? parent->last_child : NULL);
841 }
842
843 /* If previous_sibling is NULL, insert at the beginning */
844 void
gtk_css_node_insert_after(GtkCssNode * parent,GtkCssNode * cssnode,GtkCssNode * previous_sibling)845 gtk_css_node_insert_after (GtkCssNode *parent,
846 GtkCssNode *cssnode,
847 GtkCssNode *previous_sibling)
848 {
849 g_return_if_fail (previous_sibling == NULL || previous_sibling->parent == parent);
850 g_return_if_fail (cssnode != previous_sibling);
851
852 if (cssnode->previous_sibling == previous_sibling &&
853 cssnode->parent == parent)
854 return;
855
856 gtk_css_node_reposition (cssnode,
857 parent,
858 previous_sibling);
859 }
860
861 /* If next_sibling is NULL, insert at the end */
862 void
gtk_css_node_insert_before(GtkCssNode * parent,GtkCssNode * cssnode,GtkCssNode * next_sibling)863 gtk_css_node_insert_before (GtkCssNode *parent,
864 GtkCssNode *cssnode,
865 GtkCssNode *next_sibling)
866 {
867 g_return_if_fail (next_sibling == NULL || next_sibling->parent == parent);
868 g_return_if_fail (cssnode != next_sibling);
869
870 if (cssnode->next_sibling == next_sibling &&
871 cssnode->parent == parent)
872 return;
873
874 gtk_css_node_reposition (cssnode,
875 parent,
876 next_sibling ? next_sibling->previous_sibling : parent->last_child);
877 }
878
879 void
gtk_css_node_reverse_children(GtkCssNode * cssnode)880 gtk_css_node_reverse_children (GtkCssNode *cssnode)
881 {
882 GtkCssNode *end;
883
884 end = cssnode->last_child;
885 while (cssnode->first_child != end)
886 {
887 gtk_css_node_reposition (cssnode->first_child,
888 cssnode,
889 end);
890 }
891
892 }
893
894 GtkCssNode *
gtk_css_node_get_parent(GtkCssNode * cssnode)895 gtk_css_node_get_parent (GtkCssNode *cssnode)
896 {
897 return cssnode->parent;
898 }
899
900 GtkCssNode *
gtk_css_node_get_first_child(GtkCssNode * cssnode)901 gtk_css_node_get_first_child (GtkCssNode *cssnode)
902 {
903 return cssnode->first_child;
904 }
905
906 GtkCssNode *
gtk_css_node_get_last_child(GtkCssNode * cssnode)907 gtk_css_node_get_last_child (GtkCssNode *cssnode)
908 {
909 return cssnode->last_child;
910 }
911
912 GtkCssNode *
gtk_css_node_get_previous_sibling(GtkCssNode * cssnode)913 gtk_css_node_get_previous_sibling (GtkCssNode *cssnode)
914 {
915 return cssnode->previous_sibling;
916 }
917
918 GtkCssNode *
gtk_css_node_get_next_sibling(GtkCssNode * cssnode)919 gtk_css_node_get_next_sibling (GtkCssNode *cssnode)
920 {
921 return cssnode->next_sibling;
922 }
923
924 static gboolean
gtk_css_node_set_style(GtkCssNode * cssnode,GtkCssStyle * style)925 gtk_css_node_set_style (GtkCssNode *cssnode,
926 GtkCssStyle *style)
927 {
928 GtkCssStyleChange change;
929 gboolean style_changed;
930
931 if (cssnode->style == style)
932 return FALSE;
933
934 gtk_css_style_change_init (&change, cssnode->style, style);
935
936 style_changed = gtk_css_style_change_has_change (&change);
937 if (style_changed)
938 {
939 g_signal_emit (cssnode, cssnode_signals[STYLE_CHANGED], 0, &change);
940 }
941 else if (cssnode->style != style &&
942 (GTK_IS_CSS_ANIMATED_STYLE (cssnode->style) || GTK_IS_CSS_ANIMATED_STYLE (style)))
943 {
944 /* This is when animations are starting/stopping but they didn't change any CSS this frame */
945 g_set_object (&cssnode->style, style);
946 }
947
948 gtk_css_style_change_finish (&change);
949
950 return style_changed;
951 }
952
953 static void
gtk_css_node_propagate_pending_changes(GtkCssNode * cssnode,gboolean style_changed)954 gtk_css_node_propagate_pending_changes (GtkCssNode *cssnode,
955 gboolean style_changed)
956 {
957 GtkCssChange change, child_change;
958 GtkCssNode *child;
959
960 change = _gtk_css_change_for_child (cssnode->pending_changes);
961 if (style_changed)
962 change |= GTK_CSS_CHANGE_PARENT_STYLE;
963
964 if (!cssnode->needs_propagation && change == 0)
965 return;
966
967 for (child = gtk_css_node_get_first_child (cssnode);
968 child;
969 child = gtk_css_node_get_next_sibling (child))
970 {
971 child_change = child->pending_changes;
972 gtk_css_node_invalidate (child, change);
973 if (child->visible)
974 change |= _gtk_css_change_for_sibling (child_change);
975 }
976
977 cssnode->needs_propagation = FALSE;
978 }
979
980 static gboolean
gtk_css_node_needs_new_style(GtkCssNode * cssnode)981 gtk_css_node_needs_new_style (GtkCssNode *cssnode)
982 {
983 return cssnode->style_is_invalid || cssnode->needs_propagation;
984 }
985
986 static void
gtk_css_node_ensure_style(GtkCssNode * cssnode,gint64 current_time)987 gtk_css_node_ensure_style (GtkCssNode *cssnode,
988 gint64 current_time)
989 {
990 gboolean style_changed;
991
992 if (!gtk_css_node_needs_new_style (cssnode))
993 return;
994
995 if (cssnode->parent)
996 gtk_css_node_ensure_style (cssnode->parent, current_time);
997
998 if (cssnode->style_is_invalid)
999 {
1000 GtkCssStyle *new_style;
1001
1002 if (cssnode->previous_sibling)
1003 gtk_css_node_ensure_style (cssnode->previous_sibling, current_time);
1004
1005 g_clear_pointer (&cssnode->cache, gtk_css_node_style_cache_unref);
1006
1007 new_style = GTK_CSS_NODE_GET_CLASS (cssnode)->update_style (cssnode,
1008 cssnode->pending_changes,
1009 current_time,
1010 cssnode->style);
1011
1012 style_changed = gtk_css_node_set_style (cssnode, new_style);
1013 g_object_unref (new_style);
1014 }
1015 else
1016 {
1017 style_changed = FALSE;
1018 }
1019
1020 gtk_css_node_propagate_pending_changes (cssnode, style_changed);
1021
1022 cssnode->pending_changes = 0;
1023 cssnode->style_is_invalid = FALSE;
1024 }
1025
1026 GtkCssStyle *
gtk_css_node_get_style(GtkCssNode * cssnode)1027 gtk_css_node_get_style (GtkCssNode *cssnode)
1028 {
1029 if (gtk_css_node_needs_new_style (cssnode))
1030 {
1031 gint64 timestamp = gtk_css_node_get_timestamp (cssnode);
1032
1033 gtk_css_node_ensure_style (cssnode, timestamp);
1034 }
1035
1036 return cssnode->style;
1037 }
1038
1039 void
gtk_css_node_set_visible(GtkCssNode * cssnode,gboolean visible)1040 gtk_css_node_set_visible (GtkCssNode *cssnode,
1041 gboolean visible)
1042 {
1043 GtkCssNode *iter;
1044
1045 if (cssnode->visible == visible)
1046 return;
1047
1048 cssnode->visible = visible;
1049 g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_VISIBLE]);
1050
1051 if (cssnode->invalid)
1052 {
1053 if (cssnode->visible)
1054 {
1055 if (cssnode->parent)
1056 gtk_css_node_set_invalid (cssnode->parent, TRUE);
1057 else
1058 GTK_CSS_NODE_GET_CLASS (cssnode)->queue_validate (cssnode);
1059 }
1060 else
1061 {
1062 if (cssnode->parent == NULL)
1063 GTK_CSS_NODE_GET_CLASS (cssnode)->dequeue_validate (cssnode);
1064 }
1065 }
1066
1067 if (cssnode->next_sibling)
1068 {
1069 gtk_css_node_invalidate (cssnode->next_sibling, GTK_CSS_CHANGE_ANY_SIBLING | GTK_CSS_CHANGE_NTH_CHILD);
1070 if (gtk_css_node_is_first_child (cssnode))
1071 {
1072 for (iter = cssnode->next_sibling;
1073 iter != NULL;
1074 iter = iter->next_sibling)
1075 {
1076 gtk_css_node_invalidate (iter, GTK_CSS_CHANGE_FIRST_CHILD);
1077 if (iter->visible)
1078 break;
1079 }
1080 }
1081 }
1082
1083 if (cssnode->previous_sibling)
1084 {
1085 if (gtk_css_node_is_last_child (cssnode))
1086 {
1087 for (iter = cssnode->previous_sibling;
1088 iter != NULL;
1089 iter = iter->previous_sibling)
1090 {
1091 gtk_css_node_invalidate (iter, GTK_CSS_CHANGE_LAST_CHILD);
1092 if (iter->visible)
1093 break;
1094 }
1095 }
1096 gtk_css_node_invalidate (cssnode->parent->first_child, GTK_CSS_CHANGE_NTH_LAST_CHILD);
1097 }
1098 }
1099
1100 gboolean
gtk_css_node_get_visible(GtkCssNode * cssnode)1101 gtk_css_node_get_visible (GtkCssNode *cssnode)
1102 {
1103 return cssnode->visible;
1104 }
1105
1106 void
gtk_css_node_set_name(GtkCssNode * cssnode,const char * name)1107 gtk_css_node_set_name (GtkCssNode *cssnode,
1108 /*interned*/ const char *name)
1109 {
1110 if (gtk_css_node_declaration_set_name (&cssnode->decl, name))
1111 {
1112 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_NAME);
1113 g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_NAME]);
1114 }
1115 }
1116
1117 /* interned */ const char *
gtk_css_node_get_name(GtkCssNode * cssnode)1118 gtk_css_node_get_name (GtkCssNode *cssnode)
1119 {
1120 return gtk_css_node_declaration_get_name (cssnode->decl);
1121 }
1122
1123 void
gtk_css_node_set_widget_type(GtkCssNode * cssnode,GType widget_type)1124 gtk_css_node_set_widget_type (GtkCssNode *cssnode,
1125 GType widget_type)
1126 {
1127 if (gtk_css_node_declaration_set_type (&cssnode->decl, widget_type))
1128 {
1129 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_NAME);
1130 g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_WIDGET_TYPE]);
1131 }
1132 }
1133
1134 GType
gtk_css_node_get_widget_type(GtkCssNode * cssnode)1135 gtk_css_node_get_widget_type (GtkCssNode *cssnode)
1136 {
1137 return gtk_css_node_declaration_get_type (cssnode->decl);
1138 }
1139
1140 void
gtk_css_node_set_id(GtkCssNode * cssnode,const char * id)1141 gtk_css_node_set_id (GtkCssNode *cssnode,
1142 /* interned */ const char *id)
1143 {
1144 if (gtk_css_node_declaration_set_id (&cssnode->decl, id))
1145 {
1146 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_ID);
1147 g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_ID]);
1148 }
1149 }
1150
1151 /* interned */ const char *
gtk_css_node_get_id(GtkCssNode * cssnode)1152 gtk_css_node_get_id (GtkCssNode *cssnode)
1153 {
1154 return gtk_css_node_declaration_get_id (cssnode->decl);
1155 }
1156
1157 void
gtk_css_node_set_state(GtkCssNode * cssnode,GtkStateFlags state_flags)1158 gtk_css_node_set_state (GtkCssNode *cssnode,
1159 GtkStateFlags state_flags)
1160 {
1161 if (gtk_css_node_declaration_set_state (&cssnode->decl, state_flags))
1162 {
1163 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_STATE);
1164 g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_STATE]);
1165 }
1166 }
1167
1168 GtkStateFlags
gtk_css_node_get_state(GtkCssNode * cssnode)1169 gtk_css_node_get_state (GtkCssNode *cssnode)
1170 {
1171 return gtk_css_node_declaration_get_state (cssnode->decl);
1172 }
1173
1174 void
gtk_css_node_set_junction_sides(GtkCssNode * cssnode,GtkJunctionSides junction_sides)1175 gtk_css_node_set_junction_sides (GtkCssNode *cssnode,
1176 GtkJunctionSides junction_sides)
1177 {
1178 gtk_css_node_declaration_set_junction_sides (&cssnode->decl, junction_sides);
1179 }
1180
1181 GtkJunctionSides
gtk_css_node_get_junction_sides(GtkCssNode * cssnode)1182 gtk_css_node_get_junction_sides (GtkCssNode *cssnode)
1183 {
1184 return gtk_css_node_declaration_get_junction_sides (cssnode->decl);
1185 }
1186
1187 static void
gtk_css_node_clear_classes(GtkCssNode * cssnode)1188 gtk_css_node_clear_classes (GtkCssNode *cssnode)
1189 {
1190 if (gtk_css_node_declaration_clear_classes (&cssnode->decl))
1191 {
1192 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_CLASS);
1193 g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_CLASSES]);
1194 }
1195 }
1196
1197 void
gtk_css_node_set_classes(GtkCssNode * cssnode,const char ** classes)1198 gtk_css_node_set_classes (GtkCssNode *cssnode,
1199 const char **classes)
1200 {
1201 guint i;
1202
1203 g_object_freeze_notify (G_OBJECT (cssnode));
1204
1205 gtk_css_node_clear_classes (cssnode);
1206
1207 if (classes)
1208 {
1209 for (i = 0; classes[i] != NULL; i++)
1210 {
1211 gtk_css_node_add_class (cssnode, g_quark_from_string (classes[i]));
1212 }
1213 }
1214
1215 g_object_thaw_notify (G_OBJECT (cssnode));
1216 }
1217
1218 char **
gtk_css_node_get_classes(GtkCssNode * cssnode)1219 gtk_css_node_get_classes (GtkCssNode *cssnode)
1220 {
1221 const GQuark *classes;
1222 char **result;
1223 guint n_classes, i, j;
1224
1225 classes = gtk_css_node_declaration_get_classes (cssnode->decl, &n_classes);
1226 result = g_new (char *, n_classes + 1);
1227
1228 for (i = n_classes, j = 0; i-- > 0; ++j)
1229 {
1230 result[j] = g_strdup (g_quark_to_string (classes[i]));
1231 }
1232
1233 result[n_classes] = NULL;
1234 return result;
1235 }
1236
1237 void
gtk_css_node_add_class(GtkCssNode * cssnode,GQuark style_class)1238 gtk_css_node_add_class (GtkCssNode *cssnode,
1239 GQuark style_class)
1240 {
1241 if (gtk_css_node_declaration_add_class (&cssnode->decl, style_class))
1242 {
1243 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_CLASS);
1244 g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_CLASSES]);
1245 }
1246 }
1247
1248 void
gtk_css_node_remove_class(GtkCssNode * cssnode,GQuark style_class)1249 gtk_css_node_remove_class (GtkCssNode *cssnode,
1250 GQuark style_class)
1251 {
1252 if (gtk_css_node_declaration_remove_class (&cssnode->decl, style_class))
1253 {
1254 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_CLASS);
1255 g_object_notify_by_pspec (G_OBJECT (cssnode), cssnode_properties[PROP_CLASSES]);
1256 }
1257 }
1258
1259 gboolean
gtk_css_node_has_class(GtkCssNode * cssnode,GQuark style_class)1260 gtk_css_node_has_class (GtkCssNode *cssnode,
1261 GQuark style_class)
1262 {
1263 return gtk_css_node_declaration_has_class (cssnode->decl, style_class);
1264 }
1265
1266 const GQuark *
gtk_css_node_list_classes(GtkCssNode * cssnode,guint * n_classes)1267 gtk_css_node_list_classes (GtkCssNode *cssnode,
1268 guint *n_classes)
1269 {
1270 return gtk_css_node_declaration_get_classes (cssnode->decl, n_classes);
1271 }
1272
1273 void
gtk_css_node_add_region(GtkCssNode * cssnode,GQuark region,GtkRegionFlags flags)1274 gtk_css_node_add_region (GtkCssNode *cssnode,
1275 GQuark region,
1276 GtkRegionFlags flags)
1277 {
1278 gtk_css_node_declaration_add_region (&cssnode->decl, region, flags);
1279 }
1280
1281 void
gtk_css_node_remove_region(GtkCssNode * cssnode,GQuark region)1282 gtk_css_node_remove_region (GtkCssNode *cssnode,
1283 GQuark region)
1284 {
1285 gtk_css_node_declaration_remove_region (&cssnode->decl, region);
1286 }
1287
1288 gboolean
gtk_css_node_has_region(GtkCssNode * cssnode,GQuark region,GtkRegionFlags * out_flags)1289 gtk_css_node_has_region (GtkCssNode *cssnode,
1290 GQuark region,
1291 GtkRegionFlags *out_flags)
1292 {
1293 return gtk_css_node_declaration_has_region (cssnode->decl, region, out_flags);
1294 }
1295
1296 GList *
gtk_css_node_list_regions(GtkCssNode * cssnode)1297 gtk_css_node_list_regions (GtkCssNode *cssnode)
1298 {
1299 return gtk_css_node_declaration_list_regions (cssnode->decl);
1300 }
1301
1302
1303 const GtkCssNodeDeclaration *
gtk_css_node_get_declaration(GtkCssNode * cssnode)1304 gtk_css_node_get_declaration (GtkCssNode *cssnode)
1305 {
1306 return cssnode->decl;
1307 }
1308
1309 void
gtk_css_node_invalidate_style_provider(GtkCssNode * cssnode)1310 gtk_css_node_invalidate_style_provider (GtkCssNode *cssnode)
1311 {
1312 GtkCssNode *child;
1313
1314 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_SOURCE);
1315
1316 for (child = cssnode->first_child;
1317 child;
1318 child = child->next_sibling)
1319 {
1320 if (gtk_css_node_get_style_provider_or_null (child) == NULL)
1321 gtk_css_node_invalidate_style_provider (child);
1322 }
1323 }
1324
1325 static void
gtk_css_node_invalidate_timestamp(GtkCssNode * cssnode)1326 gtk_css_node_invalidate_timestamp (GtkCssNode *cssnode)
1327 {
1328 GtkCssNode *child;
1329
1330 if (!cssnode->invalid)
1331 return;
1332
1333 if (!gtk_css_style_is_static (cssnode->style))
1334 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_TIMESTAMP);
1335
1336 for (child = cssnode->first_child; child; child = child->next_sibling)
1337 {
1338 gtk_css_node_invalidate_timestamp (child);
1339 }
1340 }
1341
1342 void
gtk_css_node_invalidate_frame_clock(GtkCssNode * cssnode,gboolean just_timestamp)1343 gtk_css_node_invalidate_frame_clock (GtkCssNode *cssnode,
1344 gboolean just_timestamp)
1345 {
1346 /* frame clock is handled by the top level */
1347 if (cssnode->parent)
1348 return;
1349
1350 gtk_css_node_invalidate_timestamp (cssnode);
1351
1352 if (!just_timestamp)
1353 gtk_css_node_invalidate (cssnode, GTK_CSS_CHANGE_ANIMATIONS);
1354 }
1355
1356 void
gtk_css_node_invalidate(GtkCssNode * cssnode,GtkCssChange change)1357 gtk_css_node_invalidate (GtkCssNode *cssnode,
1358 GtkCssChange change)
1359 {
1360 if (!cssnode->invalid)
1361 change &= ~GTK_CSS_CHANGE_TIMESTAMP;
1362
1363 if (change == 0)
1364 return;
1365
1366 cssnode->pending_changes |= change;
1367
1368 GTK_CSS_NODE_GET_CLASS (cssnode)->invalidate (cssnode);
1369
1370 if (cssnode->parent)
1371 cssnode->parent->needs_propagation = TRUE;
1372 gtk_css_node_invalidate_style (cssnode);
1373 }
1374
1375 void
gtk_css_node_validate_internal(GtkCssNode * cssnode,gint64 timestamp)1376 gtk_css_node_validate_internal (GtkCssNode *cssnode,
1377 gint64 timestamp)
1378 {
1379 GtkCssNode *child;
1380
1381 if (!cssnode->invalid)
1382 return;
1383
1384 gtk_css_node_ensure_style (cssnode, timestamp);
1385
1386 /* need to set to FALSE then to TRUE here to make it chain up */
1387 gtk_css_node_set_invalid (cssnode, FALSE);
1388 if (!gtk_css_style_is_static (cssnode->style))
1389 gtk_css_node_set_invalid (cssnode, TRUE);
1390
1391 GTK_CSS_NODE_GET_CLASS (cssnode)->validate (cssnode);
1392
1393 for (child = gtk_css_node_get_first_child (cssnode);
1394 child;
1395 child = gtk_css_node_get_next_sibling (child))
1396 {
1397 if (child->visible)
1398 gtk_css_node_validate_internal (child, timestamp);
1399 }
1400 }
1401
1402 void
gtk_css_node_validate(GtkCssNode * cssnode)1403 gtk_css_node_validate (GtkCssNode *cssnode)
1404 {
1405 gint64 timestamp;
1406
1407 timestamp = gtk_css_node_get_timestamp (cssnode);
1408
1409 gtk_css_node_validate_internal (cssnode, timestamp);
1410 }
1411
1412 gboolean
gtk_css_node_init_matcher(GtkCssNode * cssnode,GtkCssMatcher * matcher)1413 gtk_css_node_init_matcher (GtkCssNode *cssnode,
1414 GtkCssMatcher *matcher)
1415 {
1416 return GTK_CSS_NODE_GET_CLASS (cssnode)->init_matcher (cssnode, matcher);
1417 }
1418
1419 GtkWidgetPath *
gtk_css_node_create_widget_path(GtkCssNode * cssnode)1420 gtk_css_node_create_widget_path (GtkCssNode *cssnode)
1421 {
1422 return GTK_CSS_NODE_GET_CLASS (cssnode)->create_widget_path (cssnode);
1423 }
1424
1425 const GtkWidgetPath *
gtk_css_node_get_widget_path(GtkCssNode * cssnode)1426 gtk_css_node_get_widget_path (GtkCssNode *cssnode)
1427 {
1428 return GTK_CSS_NODE_GET_CLASS (cssnode)->get_widget_path (cssnode);
1429 }
1430
1431 GtkStyleProviderPrivate *
gtk_css_node_get_style_provider(GtkCssNode * cssnode)1432 gtk_css_node_get_style_provider (GtkCssNode *cssnode)
1433 {
1434 GtkStyleProviderPrivate *result;
1435 GtkSettings *settings;
1436
1437 result = gtk_css_node_get_style_provider_or_null (cssnode);
1438 if (result)
1439 return result;
1440
1441 if (cssnode->parent)
1442 return gtk_css_node_get_style_provider (cssnode->parent);
1443
1444 settings = gtk_settings_get_default ();
1445 if (!settings)
1446 return NULL;
1447
1448 return GTK_STYLE_PROVIDER_PRIVATE (_gtk_settings_get_style_cascade (settings, 1));
1449 }
1450
1451 void
gtk_css_node_print(GtkCssNode * cssnode,GtkStyleContextPrintFlags flags,GString * string,guint indent)1452 gtk_css_node_print (GtkCssNode *cssnode,
1453 GtkStyleContextPrintFlags flags,
1454 GString *string,
1455 guint indent)
1456 {
1457 gboolean need_newline = FALSE;
1458
1459 g_string_append_printf (string, "%*s", indent, "");
1460
1461 if (!cssnode->visible)
1462 g_string_append_c (string, '[');
1463
1464 gtk_css_node_declaration_print (cssnode->decl, string);
1465
1466 if (!cssnode->visible)
1467 g_string_append_c (string, ']');
1468
1469 g_string_append_c (string, '\n');
1470
1471 if (flags & GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE)
1472 need_newline = gtk_css_style_print (gtk_css_node_get_style (cssnode), string, indent + 2, TRUE);
1473
1474 if (flags & GTK_STYLE_CONTEXT_PRINT_RECURSE)
1475 {
1476 GtkCssNode *node;
1477
1478 if (need_newline && gtk_css_node_get_first_child (cssnode))
1479 g_string_append_c (string, '\n');
1480
1481 for (node = gtk_css_node_get_first_child (cssnode); node; node = gtk_css_node_get_next_sibling (node))
1482 gtk_css_node_print (node, flags, string, indent + 2);
1483 }
1484 }
1485