1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2010 Carlos Garnacho <carlosg@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 "gtkstylecontextprivate.h" |
21 | |
22 | #include <gdk/gdk.h> |
23 | |
24 | #include "gtkcsscolorvalueprivate.h" |
25 | #include "gtkcssnumbervalueprivate.h" |
26 | #include "gtkcsstransientnodeprivate.h" |
27 | #include "gtkdebug.h" |
28 | #include "gtkintl.h" |
29 | #include "gtkprivate.h" |
30 | #include "gtksettings.h" |
31 | #include "gtksettingsprivate.h" |
32 | #include "gtksnapshot.h" |
33 | |
34 | |
35 | /** |
36 | * GtkStyleContext: |
37 | * |
38 | * `GtkStyleContext` stores styling information affecting a widget. |
39 | * |
40 | * In order to construct the final style information, `GtkStyleContext` |
41 | * queries information from all attached `GtkStyleProviders`. Style |
42 | * providers can be either attached explicitly to the context through |
43 | * [method@Gtk.StyleContext.add_provider], or to the display through |
44 | * [func@Gtk.StyleContext.add_provider_for_display]. The resulting |
45 | * style is a combination of all providers’ information in priority order. |
46 | * |
47 | * For GTK widgets, any `GtkStyleContext` returned by |
48 | * [method@Gtk.Widget.get_style_context] will already have a `GdkDisplay` |
49 | * and RTL/LTR information set. The style context will also be updated |
50 | * automatically if any of these settings change on the widget. |
51 | * |
52 | * # Style Classes |
53 | * |
54 | * Widgets can add style classes to their context, which can be used to associate |
55 | * different styles by class. The documentation for individual widgets lists |
56 | * which style classes it uses itself, and which style classes may be added by |
57 | * applications to affect their appearance. |
58 | * |
59 | * # Custom styling in UI libraries and applications |
60 | * |
61 | * If you are developing a library with custom widgets that render differently |
62 | * than standard components, you may need to add a `GtkStyleProvider` yourself |
63 | * with the %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK priority, either a |
64 | * `GtkCssProvider` or a custom object implementing the `GtkStyleProvider` |
65 | * interface. This way themes may still attempt to style your UI elements in |
66 | * a different way if needed so. |
67 | * |
68 | * If you are using custom styling on an applications, you probably want then |
69 | * to make your style information prevail to the theme’s, so you must use |
70 | * a `GtkStyleProvider` with the %GTK_STYLE_PROVIDER_PRIORITY_APPLICATION |
71 | * priority, keep in mind that the user settings in |
72 | * `XDG_CONFIG_HOME/gtk-4.0/gtk.css` will |
73 | * still take precedence over your changes, as it uses the |
74 | * %GTK_STYLE_PROVIDER_PRIORITY_USER priority. |
75 | */ |
76 | |
77 | #define CURSOR_ASPECT_RATIO (0.04) |
78 | |
79 | struct _GtkStyleContextPrivate |
80 | { |
81 | GdkDisplay *display; |
82 | |
83 | guint cascade_changed_id; |
84 | GtkStyleCascade *cascade; |
85 | GtkCssNode *cssnode; |
86 | GSList *saved_nodes; |
87 | }; |
88 | typedef struct _GtkStyleContextPrivate GtkStyleContextPrivate; |
89 | |
90 | enum { |
91 | PROP_0, |
92 | PROP_DISPLAY, |
93 | LAST_PROP |
94 | }; |
95 | |
96 | static GParamSpec *properties[LAST_PROP] = { NULL, }; |
97 | |
98 | static void gtk_style_context_finalize (GObject *object); |
99 | |
100 | static void gtk_style_context_impl_set_property (GObject *object, |
101 | guint prop_id, |
102 | const GValue *value, |
103 | GParamSpec *pspec); |
104 | static void gtk_style_context_impl_get_property (GObject *object, |
105 | guint prop_id, |
106 | GValue *value, |
107 | GParamSpec *pspec); |
108 | |
109 | static GtkCssNode * gtk_style_context_get_root (GtkStyleContext *context); |
110 | |
111 | G_DEFINE_TYPE_WITH_PRIVATE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT) |
112 | |
113 | static void |
114 | gtk_style_context_class_init (GtkStyleContextClass *klass) |
115 | { |
116 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
117 | |
118 | object_class->finalize = gtk_style_context_finalize; |
119 | object_class->set_property = gtk_style_context_impl_set_property; |
120 | object_class->get_property = gtk_style_context_impl_get_property; |
121 | |
122 | properties[PROP_DISPLAY] = |
123 | g_param_spec_object (name: "display" , |
124 | P_("Display" ), |
125 | P_("The associated GdkDisplay" ), |
126 | GDK_TYPE_DISPLAY, |
127 | GTK_PARAM_READWRITE); |
128 | |
129 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: properties); |
130 | } |
131 | |
132 | static void |
133 | gtk_style_context_pop_style_node (GtkStyleContext *context) |
134 | { |
135 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
136 | |
137 | g_return_if_fail (priv->saved_nodes != NULL); |
138 | |
139 | if (GTK_IS_CSS_TRANSIENT_NODE (priv->cssnode)) |
140 | gtk_css_node_set_parent (cssnode: priv->cssnode, NULL); |
141 | g_object_unref (object: priv->cssnode); |
142 | priv->cssnode = priv->saved_nodes->data; |
143 | priv->saved_nodes = g_slist_remove (list: priv->saved_nodes, data: priv->cssnode); |
144 | } |
145 | |
146 | static void |
147 | gtk_style_context_cascade_changed (GtkStyleCascade *cascade, |
148 | GtkStyleContext *context) |
149 | { |
150 | gtk_css_node_invalidate_style_provider (cssnode: gtk_style_context_get_root (context)); |
151 | } |
152 | |
153 | static void |
154 | gtk_style_context_set_cascade (GtkStyleContext *context, |
155 | GtkStyleCascade *cascade) |
156 | { |
157 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
158 | |
159 | if (priv->cascade == cascade) |
160 | return; |
161 | |
162 | if (priv->cascade) |
163 | { |
164 | g_signal_handler_disconnect (instance: priv->cascade, handler_id: priv->cascade_changed_id); |
165 | priv->cascade_changed_id = 0; |
166 | g_object_unref (object: priv->cascade); |
167 | } |
168 | |
169 | if (cascade) |
170 | { |
171 | g_object_ref (cascade); |
172 | priv->cascade_changed_id = g_signal_connect (cascade, |
173 | "gtk-private-changed" , |
174 | G_CALLBACK (gtk_style_context_cascade_changed), |
175 | context); |
176 | } |
177 | |
178 | priv->cascade = cascade; |
179 | |
180 | if (cascade && priv->cssnode != NULL) |
181 | gtk_style_context_cascade_changed (cascade, context); |
182 | } |
183 | |
184 | static void |
185 | gtk_style_context_init (GtkStyleContext *context) |
186 | { |
187 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
188 | |
189 | priv->display = gdk_display_get_default (); |
190 | |
191 | if (priv->display == NULL) |
192 | g_error ("Can't create a GtkStyleContext without a display connection" ); |
193 | |
194 | gtk_style_context_set_cascade (context, |
195 | cascade: _gtk_settings_get_style_cascade (settings: gtk_settings_get_for_display (display: priv->display), scale: 1)); |
196 | } |
197 | |
198 | static void |
199 | gtk_style_context_finalize (GObject *object) |
200 | { |
201 | GtkStyleContext *context = GTK_STYLE_CONTEXT (object); |
202 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
203 | |
204 | while (priv->saved_nodes) |
205 | gtk_style_context_pop_style_node (context); |
206 | |
207 | gtk_style_context_set_cascade (context, NULL); |
208 | |
209 | if (priv->cssnode) |
210 | g_object_unref (object: priv->cssnode); |
211 | |
212 | G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object); |
213 | } |
214 | |
215 | static void |
216 | gtk_style_context_impl_set_property (GObject *object, |
217 | guint prop_id, |
218 | const GValue *value, |
219 | GParamSpec *pspec) |
220 | { |
221 | GtkStyleContext *context = GTK_STYLE_CONTEXT (object); |
222 | |
223 | switch (prop_id) |
224 | { |
225 | case PROP_DISPLAY: |
226 | gtk_style_context_set_display (context, display: g_value_get_object (value)); |
227 | break; |
228 | default: |
229 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
230 | break; |
231 | } |
232 | } |
233 | |
234 | static void |
235 | gtk_style_context_impl_get_property (GObject *object, |
236 | guint prop_id, |
237 | GValue *value, |
238 | GParamSpec *pspec) |
239 | { |
240 | GtkStyleContext *context = GTK_STYLE_CONTEXT (object); |
241 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
242 | |
243 | switch (prop_id) |
244 | { |
245 | case PROP_DISPLAY: |
246 | g_value_set_object (value, v_object: priv->display); |
247 | break; |
248 | default: |
249 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
250 | break; |
251 | } |
252 | } |
253 | |
254 | /* returns TRUE if someone called gtk_style_context_save() but hasn’t |
255 | * called gtk_style_context_restore() yet. |
256 | * In those situations we don’t invalidate the context when somebody |
257 | * changes state/classes. |
258 | */ |
259 | static gboolean |
260 | gtk_style_context_is_saved (GtkStyleContext *context) |
261 | { |
262 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
263 | |
264 | return priv->saved_nodes != NULL; |
265 | } |
266 | |
267 | static GtkCssNode * |
268 | gtk_style_context_get_root (GtkStyleContext *context) |
269 | { |
270 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
271 | |
272 | if (priv->saved_nodes != NULL) |
273 | return g_slist_last (list: priv->saved_nodes)->data; |
274 | else |
275 | return priv->cssnode; |
276 | } |
277 | |
278 | GtkStyleProvider * |
279 | gtk_style_context_get_style_provider (GtkStyleContext *context) |
280 | { |
281 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
282 | |
283 | return GTK_STYLE_PROVIDER (priv->cascade); |
284 | } |
285 | |
286 | static gboolean |
287 | gtk_style_context_has_custom_cascade (GtkStyleContext *context) |
288 | { |
289 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
290 | GtkSettings *settings = gtk_settings_get_for_display (display: priv->display); |
291 | |
292 | return priv->cascade != _gtk_settings_get_style_cascade (settings, scale: _gtk_style_cascade_get_scale (cascade: priv->cascade)); |
293 | } |
294 | |
295 | GtkCssStyle * |
296 | gtk_style_context_lookup_style (GtkStyleContext *context) |
297 | { |
298 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
299 | |
300 | /* Code will recreate style if it was changed */ |
301 | return gtk_css_node_get_style (cssnode: priv->cssnode); |
302 | } |
303 | |
304 | GtkCssNode* |
305 | gtk_style_context_get_node (GtkStyleContext *context) |
306 | { |
307 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
308 | |
309 | return priv->cssnode; |
310 | } |
311 | |
312 | GtkStyleContext * |
313 | gtk_style_context_new_for_node (GtkCssNode *node) |
314 | { |
315 | GtkStyleContext *context; |
316 | GtkStyleContextPrivate *priv; |
317 | |
318 | g_return_val_if_fail (GTK_IS_CSS_NODE (node), NULL); |
319 | |
320 | context = g_object_new (GTK_TYPE_STYLE_CONTEXT, NULL); |
321 | priv = gtk_style_context_get_instance_private (self: context); |
322 | priv->cssnode = g_object_ref (node); |
323 | |
324 | return context; |
325 | } |
326 | |
327 | /** |
328 | * gtk_style_context_add_provider: |
329 | * @context: a `GtkStyleContext` |
330 | * @provider: a `GtkStyleProvider` |
331 | * @priority: the priority of the style provider. The lower |
332 | * it is, the earlier it will be used in the style construction. |
333 | * Typically this will be in the range between |
334 | * %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK and |
335 | * %GTK_STYLE_PROVIDER_PRIORITY_USER |
336 | * |
337 | * Adds a style provider to @context, to be used in style construction. |
338 | * |
339 | * Note that a style provider added by this function only affects |
340 | * the style of the widget to which @context belongs. If you want |
341 | * to affect the style of all widgets, use |
342 | * [func@Gtk.StyleContext.add_provider_for_display]. |
343 | * |
344 | * Note: If both priorities are the same, a `GtkStyleProvider` |
345 | * added through this function takes precedence over another added |
346 | * through [func@Gtk.StyleContext.add_provider_for_display]. |
347 | */ |
348 | void |
349 | gtk_style_context_add_provider (GtkStyleContext *context, |
350 | GtkStyleProvider *provider, |
351 | guint priority) |
352 | { |
353 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
354 | |
355 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
356 | g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider)); |
357 | |
358 | if (!gtk_style_context_has_custom_cascade (context)) |
359 | { |
360 | GtkStyleCascade *new_cascade; |
361 | |
362 | new_cascade = _gtk_style_cascade_new (); |
363 | _gtk_style_cascade_set_scale (cascade: new_cascade, scale: _gtk_style_cascade_get_scale (cascade: priv->cascade)); |
364 | _gtk_style_cascade_set_parent (cascade: new_cascade, |
365 | parent: _gtk_settings_get_style_cascade (settings: gtk_settings_get_for_display (display: priv->display), scale: 1)); |
366 | _gtk_style_cascade_add_provider (cascade: new_cascade, provider, priority); |
367 | gtk_style_context_set_cascade (context, cascade: new_cascade); |
368 | g_object_unref (object: new_cascade); |
369 | } |
370 | else |
371 | { |
372 | _gtk_style_cascade_add_provider (cascade: priv->cascade, provider, priority); |
373 | } |
374 | } |
375 | |
376 | /** |
377 | * gtk_style_context_remove_provider: |
378 | * @context: a `GtkStyleContext` |
379 | * @provider: a `GtkStyleProvider` |
380 | * |
381 | * Removes @provider from the style providers list in @context. |
382 | */ |
383 | void |
384 | gtk_style_context_remove_provider (GtkStyleContext *context, |
385 | GtkStyleProvider *provider) |
386 | { |
387 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
388 | |
389 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
390 | g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider)); |
391 | |
392 | if (!gtk_style_context_has_custom_cascade (context)) |
393 | return; |
394 | |
395 | _gtk_style_cascade_remove_provider (cascade: priv->cascade, provider); |
396 | } |
397 | |
398 | /** |
399 | * gtk_style_context_add_provider_for_display: |
400 | * @display: a `GdkDisplay` |
401 | * @provider: a `GtkStyleProvider` |
402 | * @priority: the priority of the style provider. The lower |
403 | * it is, the earlier it will be used in the style construction. |
404 | * Typically this will be in the range between |
405 | * %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK and |
406 | * %GTK_STYLE_PROVIDER_PRIORITY_USER |
407 | * |
408 | * Adds a global style provider to @display, which will be used |
409 | * in style construction for all `GtkStyleContexts` under @display. |
410 | * |
411 | * GTK uses this to make styling information from `GtkSettings` |
412 | * available. |
413 | * |
414 | * Note: If both priorities are the same, A `GtkStyleProvider` |
415 | * added through [method@Gtk.StyleContext.add_provider] takes |
416 | * precedence over another added through this function. |
417 | **/ |
418 | void |
419 | gtk_style_context_add_provider_for_display (GdkDisplay *display, |
420 | GtkStyleProvider *provider, |
421 | guint priority) |
422 | { |
423 | GtkStyleCascade *cascade; |
424 | |
425 | g_return_if_fail (GDK_IS_DISPLAY (display)); |
426 | g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider)); |
427 | g_return_if_fail (!GTK_IS_SETTINGS (provider) || _gtk_settings_get_display (GTK_SETTINGS (provider)) == display); |
428 | |
429 | cascade = _gtk_settings_get_style_cascade (settings: gtk_settings_get_for_display (display), scale: 1); |
430 | _gtk_style_cascade_add_provider (cascade, provider, priority); |
431 | } |
432 | |
433 | /** |
434 | * gtk_style_context_remove_provider_for_display: |
435 | * @display: a `GdkDisplay` |
436 | * @provider: a `GtkStyleProvider` |
437 | * |
438 | * Removes @provider from the global style providers list in @display. |
439 | */ |
440 | void |
441 | gtk_style_context_remove_provider_for_display (GdkDisplay *display, |
442 | GtkStyleProvider *provider) |
443 | { |
444 | GtkStyleCascade *cascade; |
445 | |
446 | g_return_if_fail (GDK_IS_DISPLAY (display)); |
447 | g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider)); |
448 | g_return_if_fail (!GTK_IS_SETTINGS (provider)); |
449 | |
450 | cascade = _gtk_settings_get_style_cascade (settings: gtk_settings_get_for_display (display), scale: 1); |
451 | _gtk_style_cascade_remove_provider (cascade, provider); |
452 | } |
453 | |
454 | /** |
455 | * gtk_style_context_set_state: |
456 | * @context: a `GtkStyleContext` |
457 | * @flags: state to represent |
458 | * |
459 | * Sets the state to be used for style matching. |
460 | */ |
461 | void |
462 | gtk_style_context_set_state (GtkStyleContext *context, |
463 | GtkStateFlags flags) |
464 | { |
465 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
466 | |
467 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
468 | |
469 | gtk_css_node_set_state (cssnode: priv->cssnode, state_flags: flags); |
470 | } |
471 | |
472 | /** |
473 | * gtk_style_context_get_state: |
474 | * @context: a `GtkStyleContext` |
475 | * |
476 | * Returns the state used for style matching. |
477 | * |
478 | * This method should only be used to retrieve the `GtkStateFlags` |
479 | * to pass to `GtkStyleContext` methods, like |
480 | * [method@Gtk.StyleContext.get_padding]. |
481 | * If you need to retrieve the current state of a `GtkWidget`, use |
482 | * [method@Gtk.Widget.get_state_flags]. |
483 | * |
484 | * Returns: the state flags |
485 | **/ |
486 | GtkStateFlags |
487 | gtk_style_context_get_state (GtkStyleContext *context) |
488 | { |
489 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
490 | |
491 | g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0); |
492 | |
493 | return gtk_css_node_get_state (cssnode: priv->cssnode); |
494 | } |
495 | |
496 | /** |
497 | * gtk_style_context_set_scale: |
498 | * @context: a `GtkStyleContext` |
499 | * @scale: scale |
500 | * |
501 | * Sets the scale to use when getting image assets for the style. |
502 | **/ |
503 | void |
504 | gtk_style_context_set_scale (GtkStyleContext *context, |
505 | int scale) |
506 | { |
507 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
508 | |
509 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
510 | |
511 | if (scale == _gtk_style_cascade_get_scale (cascade: priv->cascade)) |
512 | return; |
513 | |
514 | if (gtk_style_context_has_custom_cascade (context)) |
515 | { |
516 | _gtk_style_cascade_set_scale (cascade: priv->cascade, scale); |
517 | } |
518 | else |
519 | { |
520 | GtkStyleCascade *new_cascade; |
521 | |
522 | new_cascade = _gtk_settings_get_style_cascade (settings: gtk_settings_get_for_display (display: priv->display), |
523 | scale); |
524 | gtk_style_context_set_cascade (context, cascade: new_cascade); |
525 | } |
526 | } |
527 | |
528 | /** |
529 | * gtk_style_context_get_scale: |
530 | * @context: a `GtkStyleContext` |
531 | * |
532 | * Returns the scale used for assets. |
533 | * |
534 | * Returns: the scale |
535 | **/ |
536 | int |
537 | gtk_style_context_get_scale (GtkStyleContext *context) |
538 | { |
539 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
540 | |
541 | g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0); |
542 | |
543 | return _gtk_style_cascade_get_scale (cascade: priv->cascade); |
544 | } |
545 | |
546 | /* |
547 | * gtk_style_context_save_to_node: |
548 | * @context: a `GtkStyleContext` |
549 | * @node: the node to save to |
550 | * |
551 | * Saves the @context state to a node. |
552 | * |
553 | * This allows temporary modifications done through |
554 | * [method@Gtk.StyleContext.add_class], |
555 | * [method@Gtk.StyleContext.remove_class], |
556 | * [method@Gtk.StyleContext.set_state] etc. |
557 | * |
558 | * Rendering using [func@Gtk.render_background] or similar |
559 | * functions are done using the given @node. |
560 | * |
561 | * To undo, call [method@Gtk.StyleContext.restore]. |
562 | * The matching call to [method@Gtk.StyleContext.restore] |
563 | * must be done before GTK returns to the main loop. |
564 | */ |
565 | void |
566 | gtk_style_context_save_to_node (GtkStyleContext *context, |
567 | GtkCssNode *node) |
568 | { |
569 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
570 | |
571 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
572 | g_return_if_fail (GTK_IS_CSS_NODE (node)); |
573 | |
574 | priv->saved_nodes = g_slist_prepend (list: priv->saved_nodes, data: priv->cssnode); |
575 | priv->cssnode = g_object_ref (node); |
576 | } |
577 | |
578 | /** |
579 | * gtk_style_context_save: |
580 | * @context: a `GtkStyleContext` |
581 | * |
582 | * Saves the @context state. |
583 | * |
584 | * This allows temporary modifications done through |
585 | * [method@Gtk.StyleContext.add_class], |
586 | * [method@Gtk.StyleContext.remove_class], |
587 | * [method@Gtk.StyleContext.set_state] to be quickly |
588 | * reverted in one go through [method@Gtk.StyleContext.restore]. |
589 | * |
590 | * The matching call to [method@Gtk.StyleContext.restore] |
591 | * must be done before GTK returns to the main loop. |
592 | **/ |
593 | void |
594 | gtk_style_context_save (GtkStyleContext *context) |
595 | { |
596 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
597 | GtkCssNode *cssnode; |
598 | |
599 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
600 | |
601 | |
602 | /* Make sure we have the style existing. It is the |
603 | * parent of the new saved node after all. |
604 | */ |
605 | if (!gtk_style_context_is_saved (context)) |
606 | gtk_style_context_lookup_style (context); |
607 | |
608 | cssnode = gtk_css_transient_node_new (parent: priv->cssnode); |
609 | gtk_css_node_set_parent (cssnode, parent: gtk_style_context_get_root (context)); |
610 | gtk_style_context_save_to_node (context, node: cssnode); |
611 | |
612 | g_object_unref (object: cssnode); |
613 | } |
614 | |
615 | /** |
616 | * gtk_style_context_restore: |
617 | * @context: a `GtkStyleContext` |
618 | * |
619 | * Restores @context state to a previous stage. |
620 | * |
621 | * See [method@Gtk.StyleContext.save]. |
622 | **/ |
623 | void |
624 | gtk_style_context_restore (GtkStyleContext *context) |
625 | { |
626 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
627 | |
628 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
629 | |
630 | if (priv->saved_nodes == NULL) |
631 | { |
632 | g_warning ("Unpaired gtk_style_context_restore() call" ); |
633 | return; |
634 | } |
635 | |
636 | gtk_style_context_pop_style_node (context); |
637 | } |
638 | |
639 | /** |
640 | * gtk_style_context_add_class: |
641 | * @context: a `GtkStyleContext` |
642 | * @class_name: class name to use in styling |
643 | * |
644 | * Adds a style class to @context, so later uses of the |
645 | * style context will make use of this new class for styling. |
646 | * |
647 | * In the CSS file format, a `GtkEntry` defining a “search” |
648 | * class, would be matched by: |
649 | * |
650 | * ```css |
651 | * entry.search { ... } |
652 | * ``` |
653 | * |
654 | * While any widget defining a “search” class would be |
655 | * matched by: |
656 | * ```css |
657 | * .search { ... } |
658 | * ``` |
659 | */ |
660 | void |
661 | gtk_style_context_add_class (GtkStyleContext *context, |
662 | const char *class_name) |
663 | { |
664 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
665 | GQuark class_quark; |
666 | |
667 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
668 | g_return_if_fail (class_name != NULL); |
669 | |
670 | class_quark = g_quark_from_string (string: class_name); |
671 | |
672 | gtk_css_node_add_class (cssnode: priv->cssnode, style_class: class_quark); |
673 | } |
674 | |
675 | /** |
676 | * gtk_style_context_remove_class: |
677 | * @context: a `GtkStyleContext` |
678 | * @class_name: class name to remove |
679 | * |
680 | * Removes @class_name from @context. |
681 | */ |
682 | void |
683 | gtk_style_context_remove_class (GtkStyleContext *context, |
684 | const char *class_name) |
685 | { |
686 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
687 | GQuark class_quark; |
688 | |
689 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
690 | g_return_if_fail (class_name != NULL); |
691 | |
692 | class_quark = g_quark_try_string (string: class_name); |
693 | if (!class_quark) |
694 | return; |
695 | |
696 | gtk_css_node_remove_class (cssnode: priv->cssnode, style_class: class_quark); |
697 | } |
698 | |
699 | /** |
700 | * gtk_style_context_has_class: |
701 | * @context: a `GtkStyleContext` |
702 | * @class_name: a class name |
703 | * |
704 | * Returns %TRUE if @context currently has defined the |
705 | * given class name. |
706 | * |
707 | * Returns: %TRUE if @context has @class_name defined |
708 | **/ |
709 | gboolean |
710 | gtk_style_context_has_class (GtkStyleContext *context, |
711 | const char *class_name) |
712 | { |
713 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
714 | GQuark class_quark; |
715 | |
716 | g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE); |
717 | g_return_val_if_fail (class_name != NULL, FALSE); |
718 | |
719 | class_quark = g_quark_try_string (string: class_name); |
720 | if (!class_quark) |
721 | return FALSE; |
722 | |
723 | return gtk_css_node_has_class (cssnode: priv->cssnode, style_class: class_quark); |
724 | } |
725 | |
726 | GtkCssValue * |
727 | _gtk_style_context_peek_property (GtkStyleContext *context, |
728 | guint property_id) |
729 | { |
730 | GtkCssStyle *values = gtk_style_context_lookup_style (context); |
731 | |
732 | return gtk_css_style_get_value (style: values, id: property_id); |
733 | } |
734 | |
735 | /** |
736 | * gtk_style_context_set_display: |
737 | * @context: a `GtkStyleContext` |
738 | * @display: a `GdkDisplay` |
739 | * |
740 | * Attaches @context to the given display. |
741 | * |
742 | * The display is used to add style information from “global” |
743 | * style providers, such as the display's `GtkSettings` instance. |
744 | * |
745 | * If you are using a `GtkStyleContext` returned from |
746 | * [method@Gtk.Widget.get_style_context], you do not need to |
747 | * call this yourself. |
748 | */ |
749 | void |
750 | gtk_style_context_set_display (GtkStyleContext *context, |
751 | GdkDisplay *display) |
752 | { |
753 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
754 | GtkStyleCascade *display_cascade; |
755 | |
756 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
757 | g_return_if_fail (GDK_IS_DISPLAY (display)); |
758 | |
759 | if (priv->display == display) |
760 | return; |
761 | |
762 | if (gtk_style_context_has_custom_cascade (context)) |
763 | { |
764 | display_cascade = _gtk_settings_get_style_cascade (settings: gtk_settings_get_for_display (display), scale: 1); |
765 | _gtk_style_cascade_set_parent (cascade: priv->cascade, parent: display_cascade); |
766 | } |
767 | else |
768 | { |
769 | display_cascade = _gtk_settings_get_style_cascade (settings: gtk_settings_get_for_display (display), |
770 | scale: _gtk_style_cascade_get_scale (cascade: priv->cascade)); |
771 | gtk_style_context_set_cascade (context, cascade: display_cascade); |
772 | } |
773 | |
774 | priv->display = display; |
775 | |
776 | g_object_notify_by_pspec (G_OBJECT (context), pspec: properties[PROP_DISPLAY]); |
777 | } |
778 | |
779 | /** |
780 | * gtk_style_context_get_display: |
781 | * @context: a `GtkStyleContext` |
782 | * |
783 | * Returns the `GdkDisplay` to which @context is attached. |
784 | * |
785 | * Returns: (transfer none): a `GdkDisplay`. |
786 | */ |
787 | GdkDisplay * |
788 | gtk_style_context_get_display (GtkStyleContext *context) |
789 | { |
790 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
791 | |
792 | g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL); |
793 | |
794 | return priv->display; |
795 | } |
796 | |
797 | static gboolean |
798 | gtk_style_context_resolve_color (GtkStyleContext *context, |
799 | GtkCssValue *color, |
800 | GdkRGBA *result) |
801 | { |
802 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
803 | GtkCssValue *val; |
804 | |
805 | g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE); |
806 | g_return_val_if_fail (color != NULL, FALSE); |
807 | g_return_val_if_fail (result != NULL, FALSE); |
808 | |
809 | val = _gtk_css_color_value_resolve (color, |
810 | GTK_STYLE_PROVIDER (priv->cascade), |
811 | current: _gtk_style_context_peek_property (context, property_id: GTK_CSS_PROPERTY_COLOR), |
812 | NULL); |
813 | if (val == NULL) |
814 | return FALSE; |
815 | |
816 | *result = *gtk_css_color_value_get_rgba (color: val); |
817 | _gtk_css_value_unref (value: val); |
818 | return TRUE; |
819 | } |
820 | |
821 | /** |
822 | * gtk_style_context_lookup_color: |
823 | * @context: a `GtkStyleContext` |
824 | * @color_name: color name to lookup |
825 | * @color: (out): Return location for the looked up color |
826 | * |
827 | * Looks up and resolves a color name in the @context color map. |
828 | * |
829 | * Returns: %TRUE if @color_name was found and resolved, %FALSE otherwise |
830 | */ |
831 | gboolean |
832 | gtk_style_context_lookup_color (GtkStyleContext *context, |
833 | const char *color_name, |
834 | GdkRGBA *color) |
835 | { |
836 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
837 | GtkCssValue *value; |
838 | |
839 | g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE); |
840 | g_return_val_if_fail (color_name != NULL, FALSE); |
841 | g_return_val_if_fail (color != NULL, FALSE); |
842 | |
843 | value = gtk_style_provider_get_color (GTK_STYLE_PROVIDER (priv->cascade), name: color_name); |
844 | if (value == NULL) |
845 | return FALSE; |
846 | |
847 | return gtk_style_context_resolve_color (context, color: value, result: color); |
848 | } |
849 | |
850 | /** |
851 | * gtk_style_context_get_color: |
852 | * @context: a `GtkStyleContext` |
853 | * @color: (out): return value for the foreground color |
854 | * |
855 | * Gets the foreground color for a given state. |
856 | */ |
857 | void |
858 | gtk_style_context_get_color (GtkStyleContext *context, |
859 | GdkRGBA *color) |
860 | { |
861 | g_return_if_fail (color != NULL); |
862 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
863 | |
864 | *color = *gtk_css_color_value_get_rgba (color: _gtk_style_context_peek_property (context, property_id: GTK_CSS_PROPERTY_COLOR)); |
865 | } |
866 | |
867 | /** |
868 | * gtk_style_context_get_border: |
869 | * @context: a `GtkStyleContext` |
870 | * @border: (out): return value for the border settings |
871 | * |
872 | * Gets the border for a given state as a `GtkBorder`. |
873 | */ |
874 | void |
875 | gtk_style_context_get_border (GtkStyleContext *context, |
876 | GtkBorder *border) |
877 | { |
878 | GtkCssStyle *style; |
879 | |
880 | g_return_if_fail (border != NULL); |
881 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
882 | |
883 | style = gtk_style_context_lookup_style (context); |
884 | |
885 | border->top = round (x: _gtk_css_number_value_get (number: style->border->border_top_width, one_hundred_percent: 100)); |
886 | border->right = round (x: _gtk_css_number_value_get (number: style->border->border_right_width, one_hundred_percent: 100)); |
887 | border->bottom = round (x: _gtk_css_number_value_get (number: style->border->border_bottom_width, one_hundred_percent: 100)); |
888 | border->left = round (x: _gtk_css_number_value_get (number: style->border->border_left_width, one_hundred_percent: 100)); |
889 | } |
890 | |
891 | /** |
892 | * gtk_style_context_get_padding: |
893 | * @context: a `GtkStyleContext` |
894 | * @padding: (out): return value for the padding settings |
895 | * |
896 | * Gets the padding for a given state as a `GtkBorder`. |
897 | */ |
898 | void |
899 | gtk_style_context_get_padding (GtkStyleContext *context, |
900 | GtkBorder *padding) |
901 | { |
902 | GtkCssStyle *style; |
903 | |
904 | g_return_if_fail (padding != NULL); |
905 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
906 | |
907 | style = gtk_style_context_lookup_style (context); |
908 | |
909 | padding->top = round (x: _gtk_css_number_value_get (number: style->size->padding_top, one_hundred_percent: 100)); |
910 | padding->right = round (x: _gtk_css_number_value_get (number: style->size->padding_right, one_hundred_percent: 100)); |
911 | padding->bottom = round (x: _gtk_css_number_value_get (number: style->size->padding_bottom, one_hundred_percent: 100)); |
912 | padding->left = round (x: _gtk_css_number_value_get (number: style->size->padding_left, one_hundred_percent: 100)); |
913 | } |
914 | |
915 | /** |
916 | * gtk_style_context_get_margin: |
917 | * @context: a `GtkStyleContext` |
918 | * @margin: (out): return value for the margin settings |
919 | * |
920 | * Gets the margin for a given state as a `GtkBorder`. |
921 | */ |
922 | void |
923 | gtk_style_context_get_margin (GtkStyleContext *context, |
924 | GtkBorder *margin) |
925 | { |
926 | GtkCssStyle *style; |
927 | |
928 | g_return_if_fail (margin != NULL); |
929 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
930 | |
931 | style = gtk_style_context_lookup_style (context); |
932 | |
933 | margin->top = round (x: _gtk_css_number_value_get (number: style->size->margin_top, one_hundred_percent: 100)); |
934 | margin->right = round (x: _gtk_css_number_value_get (number: style->size->margin_right, one_hundred_percent: 100)); |
935 | margin->bottom = round (x: _gtk_css_number_value_get (number: style->size->margin_bottom, one_hundred_percent: 100)); |
936 | margin->left = round (x: _gtk_css_number_value_get (number: style->size->margin_left, one_hundred_percent: 100)); |
937 | } |
938 | |
939 | void |
940 | _gtk_style_context_get_cursor_color (GtkStyleContext *context, |
941 | GdkRGBA *primary_color, |
942 | GdkRGBA *secondary_color) |
943 | { |
944 | GtkCssStyle *style; |
945 | |
946 | style = gtk_style_context_lookup_style (context); |
947 | |
948 | if (primary_color) |
949 | *primary_color = *gtk_css_color_value_get_rgba (color: style->font->caret_color ? style->font->caret_color : style->core->color); |
950 | |
951 | if (secondary_color) |
952 | *secondary_color = *gtk_css_color_value_get_rgba (color: style->font->secondary_caret_color ? style->font->secondary_caret_color : style->core->color); |
953 | } |
954 | |
955 | static void |
956 | draw_insertion_cursor (GtkStyleContext *context, |
957 | cairo_t *cr, |
958 | double x, |
959 | double y, |
960 | double width, |
961 | double height, |
962 | double aspect_ratio, |
963 | gboolean is_primary, |
964 | PangoDirection direction, |
965 | gboolean draw_arrow) |
966 | { |
967 | GdkRGBA primary_color; |
968 | GdkRGBA secondary_color; |
969 | int stem_width; |
970 | double angle; |
971 | double dx, dy; |
972 | double xx1, yy1, xx2, yy2; |
973 | |
974 | cairo_save (cr); |
975 | cairo_new_path (cr); |
976 | |
977 | _gtk_style_context_get_cursor_color (context, primary_color: &primary_color, secondary_color: &secondary_color); |
978 | gdk_cairo_set_source_rgba (cr, rgba: is_primary ? &primary_color : &secondary_color); |
979 | |
980 | stem_width = height * aspect_ratio + 1; |
981 | |
982 | yy1 = y; |
983 | yy2 = y + height; |
984 | |
985 | if (width < 0) |
986 | { |
987 | xx1 = x; |
988 | xx2 = x - width; |
989 | } |
990 | else |
991 | { |
992 | xx1 = x + width; |
993 | xx2 = x; |
994 | } |
995 | |
996 | angle = atan2 (y: height, x: width); |
997 | |
998 | dx = (stem_width/2.0) * cos (M_PI/2 - angle); |
999 | dy = (stem_width/2.0) * sin (M_PI/2 - angle); |
1000 | |
1001 | if (draw_arrow) |
1002 | { |
1003 | if (direction == PANGO_DIRECTION_RTL) |
1004 | { |
1005 | double x0, y0, x1, y1, x2, y2; |
1006 | |
1007 | x0 = xx2 - dx + 2 * dy; |
1008 | y0 = yy2 - dy - 2 * dx; |
1009 | |
1010 | x1 = x0 + 4 * dy; |
1011 | y1 = y0 - 4 * dx; |
1012 | x2 = x0 + 2 * dy - 3 * dx; |
1013 | y2 = y0 - 2 * dx - 3 * dy; |
1014 | |
1015 | cairo_move_to (cr, x: xx1 + dx, y: yy1 + dy); |
1016 | cairo_line_to (cr, x: xx2 + dx, y: yy2 + dy); |
1017 | cairo_line_to (cr, x: x2, y: y2); |
1018 | cairo_line_to (cr, x: x1, y: y1); |
1019 | cairo_line_to (cr, x: xx1 - dx, y: yy1 - dy); |
1020 | } |
1021 | else if (direction == PANGO_DIRECTION_LTR) |
1022 | { |
1023 | double x0, y0, x1, y1, x2, y2; |
1024 | |
1025 | x0 = xx2 + dx + 2 * dy; |
1026 | y0 = yy2 + dy - 2 * dx; |
1027 | |
1028 | x1 = x0 + 4 * dy; |
1029 | y1 = y0 - 4 * dx; |
1030 | x2 = x0 + 2 * dy + 3 * dx; |
1031 | y2 = y0 - 2 * dx + 3 * dy; |
1032 | |
1033 | cairo_move_to (cr, x: xx1 - dx, y: yy1 - dy); |
1034 | cairo_line_to (cr, x: xx2 - dx, y: yy2 - dy); |
1035 | cairo_line_to (cr, x: x2, y: y2); |
1036 | cairo_line_to (cr, x: x1, y: y1); |
1037 | cairo_line_to (cr, x: xx1 + dx, y: yy1 + dy); |
1038 | } |
1039 | else |
1040 | g_assert_not_reached(); |
1041 | } |
1042 | else |
1043 | { |
1044 | cairo_move_to (cr, x: xx1 + dx, y: yy1 + dy); |
1045 | cairo_line_to (cr, x: xx2 + dx, y: yy2 + dy); |
1046 | cairo_line_to (cr, x: xx2 - dx, y: yy2 - dy); |
1047 | cairo_line_to (cr, x: xx1 - dx, y: yy1 - dy); |
1048 | } |
1049 | |
1050 | cairo_fill (cr); |
1051 | |
1052 | cairo_restore (cr); |
1053 | } |
1054 | |
1055 | static void |
1056 | get_insertion_cursor_bounds (double width, |
1057 | double height, |
1058 | double aspect_ratio, |
1059 | PangoDirection direction, |
1060 | gboolean draw_arrow, |
1061 | graphene_rect_t *bounds) |
1062 | { |
1063 | int stem_width; |
1064 | |
1065 | if (width < 0) |
1066 | width = - width; |
1067 | |
1068 | stem_width = height * aspect_ratio + 1; |
1069 | |
1070 | graphene_rect_init (r: bounds, |
1071 | x: - 2 * stem_width, y: - stem_width, |
1072 | width: width + 4 * stem_width, height: height + 2 * stem_width); |
1073 | } |
1074 | |
1075 | static void |
1076 | snapshot_insertion_cursor (GtkSnapshot *snapshot, |
1077 | GtkStyleContext *context, |
1078 | double width, |
1079 | double height, |
1080 | double aspect_ratio, |
1081 | gboolean is_primary, |
1082 | PangoDirection direction, |
1083 | gboolean draw_arrow) |
1084 | { |
1085 | if (width != 0 || draw_arrow) |
1086 | { |
1087 | cairo_t *cr; |
1088 | graphene_rect_t bounds; |
1089 | |
1090 | get_insertion_cursor_bounds (width, height, aspect_ratio, direction, draw_arrow, bounds: &bounds); |
1091 | cr = gtk_snapshot_append_cairo (snapshot, bounds: &bounds); |
1092 | |
1093 | draw_insertion_cursor (context, cr, x: 0, y: 0, width, height, aspect_ratio, is_primary, direction, draw_arrow); |
1094 | |
1095 | cairo_destroy (cr); |
1096 | } |
1097 | else |
1098 | { |
1099 | GdkRGBA primary_color; |
1100 | GdkRGBA secondary_color; |
1101 | int stem_width; |
1102 | int offset; |
1103 | |
1104 | _gtk_style_context_get_cursor_color (context, primary_color: &primary_color, secondary_color: &secondary_color); |
1105 | |
1106 | stem_width = height * aspect_ratio + 1; |
1107 | |
1108 | /* put (stem_width % 2) on the proper side of the cursor */ |
1109 | if (direction == PANGO_DIRECTION_LTR) |
1110 | offset = stem_width / 2; |
1111 | else |
1112 | offset = stem_width - stem_width / 2; |
1113 | |
1114 | gtk_snapshot_append_color (snapshot, |
1115 | color: is_primary ? &primary_color : &secondary_color, |
1116 | bounds: &GRAPHENE_RECT_INIT (- offset, 0, stem_width, height)); |
1117 | } |
1118 | } |
1119 | |
1120 | /** |
1121 | * gtk_snapshot_render_insertion_cursor: |
1122 | * @snapshot: snapshot to render to |
1123 | * @context: a `GtkStyleContext` |
1124 | * @x: X origin |
1125 | * @y: Y origin |
1126 | * @layout: the `PangoLayout` of the text |
1127 | * @index: the index in the `PangoLayout` |
1128 | * @direction: the `PangoDirection` of the text |
1129 | * |
1130 | * Draws a text caret using @snapshot at the specified index of @layout. |
1131 | */ |
1132 | void |
1133 | gtk_snapshot_render_insertion_cursor (GtkSnapshot *snapshot, |
1134 | GtkStyleContext *context, |
1135 | double x, |
1136 | double y, |
1137 | PangoLayout *layout, |
1138 | int index, |
1139 | PangoDirection direction) |
1140 | { |
1141 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
1142 | gboolean split_cursor; |
1143 | double aspect_ratio; |
1144 | PangoRectangle strong_pos, weak_pos; |
1145 | PangoRectangle *cursor1, *cursor2; |
1146 | GdkSeat *seat; |
1147 | PangoDirection keyboard_direction; |
1148 | PangoDirection direction2; |
1149 | |
1150 | g_return_if_fail (snapshot != NULL); |
1151 | g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); |
1152 | g_return_if_fail (PANGO_IS_LAYOUT (layout)); |
1153 | g_return_if_fail (index >= 0); |
1154 | |
1155 | g_object_get (object: gtk_settings_get_for_display (display: priv->display), |
1156 | first_property_name: "gtk-split-cursor" , &split_cursor, |
1157 | "gtk-cursor-aspect-ratio" , &aspect_ratio, |
1158 | NULL); |
1159 | |
1160 | keyboard_direction = PANGO_DIRECTION_LTR; |
1161 | seat = gdk_display_get_default_seat (display: priv->display); |
1162 | if (seat) |
1163 | { |
1164 | GdkDevice *keyboard = gdk_seat_get_keyboard (seat); |
1165 | |
1166 | if (keyboard) |
1167 | keyboard_direction = gdk_device_get_direction (device: keyboard); |
1168 | } |
1169 | |
1170 | pango_layout_get_caret_pos (layout, index_: index, strong_pos: &strong_pos, weak_pos: &weak_pos); |
1171 | |
1172 | direction2 = PANGO_DIRECTION_NEUTRAL; |
1173 | |
1174 | if (split_cursor) |
1175 | { |
1176 | cursor1 = &strong_pos; |
1177 | |
1178 | if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y) |
1179 | { |
1180 | direction2 = (direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR; |
1181 | cursor2 = &weak_pos; |
1182 | } |
1183 | } |
1184 | else |
1185 | { |
1186 | if (keyboard_direction == direction) |
1187 | cursor1 = &strong_pos; |
1188 | else |
1189 | cursor1 = &weak_pos; |
1190 | } |
1191 | |
1192 | gtk_snapshot_save (snapshot); |
1193 | gtk_snapshot_translate (snapshot, point: &GRAPHENE_POINT_INIT (x + PANGO_PIXELS (MIN (cursor1->x, cursor1->x + cursor1->width)), y + PANGO_PIXELS (cursor1->y))); |
1194 | snapshot_insertion_cursor (snapshot, |
1195 | context, |
1196 | PANGO_PIXELS (cursor1->width), |
1197 | PANGO_PIXELS (cursor1->height), |
1198 | aspect_ratio, |
1199 | TRUE, |
1200 | direction, |
1201 | draw_arrow: direction2 != PANGO_DIRECTION_NEUTRAL); |
1202 | gtk_snapshot_restore (snapshot); |
1203 | |
1204 | if (direction2 != PANGO_DIRECTION_NEUTRAL) |
1205 | { |
1206 | gtk_snapshot_save (snapshot); |
1207 | gtk_snapshot_translate (snapshot, point: &GRAPHENE_POINT_INIT (x + PANGO_PIXELS (MIN (cursor2->x, cursor2->x + cursor2->width)), y + PANGO_PIXELS (cursor2->y))); |
1208 | snapshot_insertion_cursor (snapshot, |
1209 | context, |
1210 | PANGO_PIXELS (cursor2->width), |
1211 | PANGO_PIXELS (cursor2->height), |
1212 | aspect_ratio, |
1213 | FALSE, |
1214 | direction: direction2, |
1215 | TRUE); |
1216 | gtk_snapshot_restore (snapshot); |
1217 | } |
1218 | } |
1219 | |
1220 | /** |
1221 | * GtkStyleContextPrintFlags: |
1222 | * @GTK_STYLE_CONTEXT_PRINT_NONE: Default value. |
1223 | * @GTK_STYLE_CONTEXT_PRINT_RECURSE: Print the entire tree of |
1224 | * CSS nodes starting at the style context's node |
1225 | * @GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE: Show the values of the |
1226 | * CSS properties for each node |
1227 | * @GTK_STYLE_CONTEXT_PRINT_SHOW_CHANGE: Show information about |
1228 | * what changes affect the styles |
1229 | * |
1230 | * Flags that modify the behavior of gtk_style_context_to_string(). |
1231 | * |
1232 | * New values may be added to this enumeration. |
1233 | */ |
1234 | |
1235 | /** |
1236 | * gtk_style_context_to_string: |
1237 | * @context: a `GtkStyleContext` |
1238 | * @flags: Flags that determine what to print |
1239 | * |
1240 | * Converts the style context into a string representation. |
1241 | * |
1242 | * The string representation always includes information about |
1243 | * the name, state, id, visibility and style classes of the CSS |
1244 | * node that is backing @context. Depending on the flags, more |
1245 | * information may be included. |
1246 | * |
1247 | * This function is intended for testing and debugging of the |
1248 | * CSS implementation in GTK. There are no guarantees about |
1249 | * the format of the returned string, it may change. |
1250 | * |
1251 | * Returns: a newly allocated string representing @context |
1252 | */ |
1253 | char * |
1254 | gtk_style_context_to_string (GtkStyleContext *context, |
1255 | GtkStyleContextPrintFlags flags) |
1256 | { |
1257 | GtkStyleContextPrivate *priv = gtk_style_context_get_instance_private (self: context); |
1258 | GString *string; |
1259 | |
1260 | g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL); |
1261 | |
1262 | string = g_string_new (init: "" ); |
1263 | |
1264 | gtk_css_node_print (cssnode: priv->cssnode, flags, string, indent: 0); |
1265 | |
1266 | return g_string_free (string, FALSE); |
1267 | } |
1268 | |