1 /*
2 * gtkoverlay.c
3 * This file is part of gtk
4 *
5 * Copyright (C) 2011 - Ignacio Casal Quinteiro, Mike Krüger
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include "gtkoverlay.h"
24 #include "gtkbuildable.h"
25 #include "gtkscrolledwindow.h"
26 #include "gtkmarshalers.h"
27
28 #include "gtkprivate.h"
29 #include "gtkintl.h"
30
31 /**
32 * SECTION:gtkoverlay
33 * @short_description: A container which overlays widgets on top of each other
34 * @title: GtkOverlay
35 *
36 * GtkOverlay is a container which contains a single main child, on top
37 * of which it can place “overlay” widgets. The position of each overlay
38 * widget is determined by its #GtkWidget:halign and #GtkWidget:valign
39 * properties. E.g. a widget with both alignments set to %GTK_ALIGN_START
40 * will be placed at the top left corner of the GtkOverlay container,
41 * whereas an overlay with halign set to %GTK_ALIGN_CENTER and valign set
42 * to %GTK_ALIGN_END will be placed a the bottom edge of the GtkOverlay,
43 * horizontally centered. The position can be adjusted by setting the margin
44 * properties of the child to non-zero values.
45 *
46 * More complicated placement of overlays is possible by connecting
47 * to the #GtkOverlay::get-child-position signal.
48 *
49 * An overlay’s minimum and natural sizes are those of its main child. The sizes
50 * of overlay children are not considered when measuring these preferred sizes.
51 *
52 * # GtkOverlay as GtkBuildable
53 *
54 * The GtkOverlay implementation of the GtkBuildable interface
55 * supports placing a child as an overlay by specifying “overlay” as
56 * the “type” attribute of a `<child>` element.
57 *
58 * # CSS nodes
59 *
60 * GtkOverlay has a single CSS node with the name “overlay”. Overlay children
61 * whose alignments cause them to be positioned at an edge get the style classes
62 * “.left”, “.right”, “.top”, and/or “.bottom” according to their position.
63 */
64
65 struct _GtkOverlayPrivate
66 {
67 GSList *children;
68 };
69
70 typedef struct _GtkOverlayChild GtkOverlayChild;
71
72 struct _GtkOverlayChild
73 {
74 GtkWidget *widget;
75 GdkWindow *window;
76 gboolean pass_through;
77 };
78
79 enum {
80 GET_CHILD_POSITION,
81 LAST_SIGNAL
82 };
83
84 enum
85 {
86 CHILD_PROP_0,
87 CHILD_PROP_PASS_THROUGH,
88 CHILD_PROP_INDEX
89 };
90
91 static guint signals[LAST_SIGNAL] = { 0 };
92
93 static void gtk_overlay_buildable_init (GtkBuildableIface *iface);
94
G_DEFINE_TYPE_WITH_CODE(GtkOverlay,gtk_overlay,GTK_TYPE_BIN,G_ADD_PRIVATE (GtkOverlay)G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_overlay_buildable_init))95 G_DEFINE_TYPE_WITH_CODE (GtkOverlay, gtk_overlay, GTK_TYPE_BIN,
96 G_ADD_PRIVATE (GtkOverlay)
97 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
98 gtk_overlay_buildable_init))
99
100 static void
101 gtk_overlay_compute_child_allocation (GtkOverlay *overlay,
102 GtkOverlayChild *child,
103 GtkAllocation *window_allocation,
104 GtkAllocation *widget_allocation)
105 {
106 gint left, right, top, bottom;
107 GtkAllocation allocation, overlay_allocation;
108 gboolean result;
109
110 g_signal_emit (overlay, signals[GET_CHILD_POSITION],
111 0, child->widget, &allocation, &result);
112
113 gtk_widget_get_allocation (GTK_WIDGET (overlay), &overlay_allocation);
114
115 allocation.x += overlay_allocation.x;
116 allocation.y += overlay_allocation.y;
117
118 /* put the margins outside the window; also arrange things
119 * so that the adjusted child allocation still ends up at 0, 0
120 */
121 left = gtk_widget_get_margin_start (child->widget);
122 right = gtk_widget_get_margin_end (child->widget);
123 top = gtk_widget_get_margin_top (child->widget);
124 bottom = gtk_widget_get_margin_bottom (child->widget);
125
126 if (widget_allocation)
127 {
128 widget_allocation->x = - left;
129 widget_allocation->y = - top;
130 widget_allocation->width = allocation.width;
131 widget_allocation->height = allocation.height;
132 }
133
134 if (window_allocation)
135 {
136 window_allocation->x = allocation.x + left;
137 window_allocation->y = allocation.y + top;
138 window_allocation->width = allocation.width - (left + right);
139 window_allocation->height = allocation.height - (top + bottom);
140 }
141 }
142
143 static GdkWindow *
gtk_overlay_create_child_window(GtkOverlay * overlay,GtkOverlayChild * child)144 gtk_overlay_create_child_window (GtkOverlay *overlay,
145 GtkOverlayChild *child)
146 {
147 GtkWidget *widget = GTK_WIDGET (overlay);
148 GtkAllocation allocation;
149 GdkWindow *window;
150 GdkWindowAttr attributes;
151 gint attributes_mask;
152
153 gtk_overlay_compute_child_allocation (overlay, child, &allocation, NULL);
154
155 attributes.window_type = GDK_WINDOW_CHILD;
156 attributes.wclass = GDK_INPUT_OUTPUT;
157 attributes.width = allocation.width;
158 attributes.height = allocation.height;
159 attributes.x = allocation.x;
160 attributes.y = allocation.y;
161 attributes.visual = gtk_widget_get_visual (widget);
162 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
163 attributes.event_mask = gtk_widget_get_events (widget);
164
165 window = gdk_window_new (gtk_widget_get_window (widget),
166 &attributes, attributes_mask);
167 gtk_widget_register_window (widget, window);
168
169 gdk_window_set_pass_through (window, child->pass_through);
170
171 gtk_widget_set_parent_window (child->widget, window);
172
173 return window;
174 }
175
176 static GtkAlign
effective_align(GtkAlign align,GtkTextDirection direction)177 effective_align (GtkAlign align,
178 GtkTextDirection direction)
179 {
180 switch (align)
181 {
182 case GTK_ALIGN_START:
183 return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START;
184 case GTK_ALIGN_END:
185 return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END;
186 default:
187 return align;
188 }
189 }
190
191 static void
gtk_overlay_get_main_widget_allocation(GtkOverlay * overlay,GtkAllocation * main_alloc_out)192 gtk_overlay_get_main_widget_allocation (GtkOverlay *overlay,
193 GtkAllocation *main_alloc_out)
194 {
195 GtkWidget *main_widget;
196 GtkAllocation main_alloc;
197
198 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
199
200 /* special-case scrolled windows */
201 if (GTK_IS_SCROLLED_WINDOW (main_widget))
202 {
203 GtkWidget *grandchild;
204 gint x, y;
205 gboolean res;
206
207 grandchild = gtk_bin_get_child (GTK_BIN (main_widget));
208 res = gtk_widget_translate_coordinates (grandchild, main_widget, 0, 0, &x, &y);
209
210 if (res)
211 {
212 main_alloc.x = x;
213 main_alloc.y = y;
214 }
215 else
216 {
217 main_alloc.x = 0;
218 main_alloc.y = 0;
219 }
220
221 main_alloc.width = gtk_widget_get_allocated_width (grandchild);
222 main_alloc.height = gtk_widget_get_allocated_height (grandchild);
223 }
224 else
225 {
226 main_alloc.x = 0;
227 main_alloc.y = 0;
228 main_alloc.width = gtk_widget_get_allocated_width (GTK_WIDGET (overlay));
229 main_alloc.height = gtk_widget_get_allocated_height (GTK_WIDGET (overlay));
230 }
231
232 if (main_alloc_out)
233 *main_alloc_out = main_alloc;
234 }
235
236 static void
gtk_overlay_child_update_style_classes(GtkOverlay * overlay,GtkWidget * child,GtkAllocation * child_allocation)237 gtk_overlay_child_update_style_classes (GtkOverlay *overlay,
238 GtkWidget *child,
239 GtkAllocation *child_allocation)
240 {
241 GtkAllocation overlay_allocation, main_allocation;
242 GtkAlign valign, halign;
243 gboolean is_left, is_right, is_top, is_bottom;
244 gboolean has_left, has_right, has_top, has_bottom;
245 GtkStyleContext *context;
246
247 context = gtk_widget_get_style_context (child);
248 has_left = gtk_style_context_has_class (context, GTK_STYLE_CLASS_LEFT);
249 has_right = gtk_style_context_has_class (context, GTK_STYLE_CLASS_RIGHT);
250 has_top = gtk_style_context_has_class (context, GTK_STYLE_CLASS_TOP);
251 has_bottom = gtk_style_context_has_class (context, GTK_STYLE_CLASS_BOTTOM);
252
253 is_left = is_right = is_top = is_bottom = FALSE;
254
255 gtk_overlay_get_main_widget_allocation (overlay, &main_allocation);
256 gtk_widget_get_allocation (GTK_WIDGET (overlay), &overlay_allocation);
257
258 main_allocation.x += overlay_allocation.x;
259 main_allocation.y += overlay_allocation.y;
260
261 halign = effective_align (gtk_widget_get_halign (child),
262 gtk_widget_get_direction (child));
263
264 if (halign == GTK_ALIGN_START)
265 is_left = (child_allocation->x == main_allocation.x);
266 else if (halign == GTK_ALIGN_END)
267 is_right = (child_allocation->x + child_allocation->width ==
268 main_allocation.x + main_allocation.width);
269
270 valign = gtk_widget_get_valign (child);
271
272 if (valign == GTK_ALIGN_START)
273 is_top = (child_allocation->y == main_allocation.y);
274 else if (valign == GTK_ALIGN_END)
275 is_bottom = (child_allocation->y + child_allocation->height ==
276 main_allocation.y + main_allocation.height);
277
278 if (has_left && !is_left)
279 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_LEFT);
280 else if (!has_left && is_left)
281 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
282
283 if (has_right && !is_right)
284 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_RIGHT);
285 else if (!has_right && is_right)
286 gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
287
288 if (has_top && !is_top)
289 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_TOP);
290 else if (!has_top && is_top)
291 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
292
293 if (has_bottom && !is_bottom)
294 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_BOTTOM);
295 else if (!has_bottom && is_bottom)
296 gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
297 }
298
299 static void
gtk_overlay_child_allocate(GtkOverlay * overlay,GtkOverlayChild * child)300 gtk_overlay_child_allocate (GtkOverlay *overlay,
301 GtkOverlayChild *child)
302 {
303 GtkAllocation window_allocation, child_allocation;
304
305 if (gtk_widget_get_mapped (GTK_WIDGET (overlay)))
306 {
307 /* Note: This calls show every size allocation, which makes
308 * us keep the z-order of the chilren, as gdk_window_show()
309 * does an implicit raise. */
310 if (gtk_widget_get_visible (child->widget))
311 gdk_window_show (child->window);
312 else if (gdk_window_is_visible (child->window))
313 gdk_window_hide (child->window);
314 }
315
316 if (!gtk_widget_get_visible (child->widget))
317 return;
318
319 gtk_overlay_compute_child_allocation (overlay, child, &window_allocation, &child_allocation);
320
321 if (child->window)
322 gdk_window_move_resize (child->window,
323 window_allocation.x, window_allocation.y,
324 window_allocation.width, window_allocation.height);
325
326 gtk_overlay_child_update_style_classes (overlay, child->widget, &window_allocation);
327 gtk_widget_size_allocate (child->widget, &child_allocation);
328 }
329
330 static void
gtk_overlay_size_allocate(GtkWidget * widget,GtkAllocation * allocation)331 gtk_overlay_size_allocate (GtkWidget *widget,
332 GtkAllocation *allocation)
333 {
334 GtkOverlay *overlay = GTK_OVERLAY (widget);
335 GtkOverlayPrivate *priv = overlay->priv;
336 GSList *children;
337 GtkWidget *main_widget;
338
339 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->size_allocate (widget, allocation);
340
341 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
342 if (main_widget && gtk_widget_get_visible (main_widget))
343 gtk_widget_size_allocate (main_widget, allocation);
344
345 for (children = priv->children; children; children = children->next)
346 gtk_overlay_child_allocate (overlay, children->data);
347 }
348
349 static gboolean
gtk_overlay_get_child_position(GtkOverlay * overlay,GtkWidget * widget,GtkAllocation * alloc)350 gtk_overlay_get_child_position (GtkOverlay *overlay,
351 GtkWidget *widget,
352 GtkAllocation *alloc)
353 {
354 GtkAllocation main_alloc;
355 GtkRequisition min, req;
356 GtkAlign halign;
357 GtkTextDirection direction;
358
359 gtk_overlay_get_main_widget_allocation (overlay, &main_alloc);
360 gtk_widget_get_preferred_size (widget, &min, &req);
361
362 alloc->x = main_alloc.x;
363 alloc->width = MAX (min.width, MIN (main_alloc.width, req.width));
364
365 direction = gtk_widget_get_direction (widget);
366
367 halign = gtk_widget_get_halign (widget);
368 switch (effective_align (halign, direction))
369 {
370 case GTK_ALIGN_START:
371 /* nothing to do */
372 break;
373 case GTK_ALIGN_FILL:
374 alloc->width = MAX (alloc->width, main_alloc.width);
375 break;
376 case GTK_ALIGN_CENTER:
377 alloc->x += main_alloc.width / 2 - alloc->width / 2;
378 break;
379 case GTK_ALIGN_END:
380 alloc->x += main_alloc.width - alloc->width;
381 break;
382 case GTK_ALIGN_BASELINE:
383 default:
384 g_assert_not_reached ();
385 break;
386 }
387
388 alloc->y = main_alloc.y;
389 alloc->height = MAX (min.height, MIN (main_alloc.height, req.height));
390
391 switch (gtk_widget_get_valign (widget))
392 {
393 case GTK_ALIGN_START:
394 /* nothing to do */
395 break;
396 case GTK_ALIGN_FILL:
397 alloc->height = MAX (alloc->height, main_alloc.height);
398 break;
399 case GTK_ALIGN_CENTER:
400 alloc->y += main_alloc.height / 2 - alloc->height / 2;
401 break;
402 case GTK_ALIGN_END:
403 alloc->y += main_alloc.height - alloc->height;
404 break;
405 case GTK_ALIGN_BASELINE:
406 default:
407 g_assert_not_reached ();
408 break;
409 }
410
411 return TRUE;
412 }
413
414 static void
gtk_overlay_realize(GtkWidget * widget)415 gtk_overlay_realize (GtkWidget *widget)
416 {
417 GtkOverlay *overlay = GTK_OVERLAY (widget);
418 GtkOverlayPrivate *priv = overlay->priv;
419 GtkOverlayChild *child;
420 GSList *children;
421
422 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->realize (widget);
423
424 for (children = priv->children; children; children = children->next)
425 {
426 child = children->data;
427
428 if (child->window == NULL)
429 child->window = gtk_overlay_create_child_window (overlay, child);
430 }
431 }
432
433 static void
gtk_overlay_unrealize(GtkWidget * widget)434 gtk_overlay_unrealize (GtkWidget *widget)
435 {
436 GtkOverlay *overlay = GTK_OVERLAY (widget);
437 GtkOverlayPrivate *priv = overlay->priv;
438 GtkOverlayChild *child;
439 GSList *children;
440
441 for (children = priv->children; children; children = children->next)
442 {
443 child = children->data;
444
445 gtk_widget_set_parent_window (child->widget, NULL);
446 gtk_widget_unregister_window (widget, child->window);
447 gdk_window_destroy (child->window);
448 child->window = NULL;
449 }
450
451 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unrealize (widget);
452 }
453
454 static void
gtk_overlay_map(GtkWidget * widget)455 gtk_overlay_map (GtkWidget *widget)
456 {
457 GtkOverlay *overlay = GTK_OVERLAY (widget);
458 GtkOverlayPrivate *priv = overlay->priv;
459 GtkOverlayChild *child;
460 GSList *children;
461
462 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->map (widget);
463
464 for (children = priv->children; children; children = children->next)
465 {
466 child = children->data;
467
468 if (child->window != NULL &&
469 gtk_widget_get_visible (child->widget) &&
470 gtk_widget_get_child_visible (child->widget))
471 gdk_window_show (child->window);
472 }
473 }
474
475 static void
gtk_overlay_unmap(GtkWidget * widget)476 gtk_overlay_unmap (GtkWidget *widget)
477 {
478 GtkOverlay *overlay = GTK_OVERLAY (widget);
479 GtkOverlayPrivate *priv = overlay->priv;
480 GtkOverlayChild *child;
481 GSList *children;
482
483 for (children = priv->children; children; children = children->next)
484 {
485 child = children->data;
486
487 if (child->window != NULL &&
488 gdk_window_is_visible (child->window))
489 gdk_window_hide (child->window);
490 }
491
492 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unmap (widget);
493 }
494
495 static void
gtk_overlay_remove(GtkContainer * container,GtkWidget * widget)496 gtk_overlay_remove (GtkContainer *container,
497 GtkWidget *widget)
498 {
499 GtkOverlayPrivate *priv = GTK_OVERLAY (container)->priv;
500 GtkOverlayChild *child;
501 GSList *children, *next;
502 gboolean removed;
503
504 removed = FALSE;
505 for (children = priv->children; children; children = next)
506 {
507 child = children->data;
508 next = children->next;
509
510 if (child->widget == widget)
511 {
512 if (child->window != NULL)
513 {
514 gtk_widget_unregister_window (GTK_WIDGET (container), child->window);
515 gdk_window_destroy (child->window);
516 }
517
518 gtk_widget_unparent (widget);
519
520 priv->children = g_slist_delete_link (priv->children, children);
521 g_slice_free (GtkOverlayChild, child);
522
523 removed = TRUE;
524 }
525 else if (removed)
526 gtk_widget_child_notify (child->widget, "index");
527 }
528
529 if (!removed)
530 GTK_CONTAINER_CLASS (gtk_overlay_parent_class)->remove (container, widget);
531 }
532
533 /**
534 * gtk_overlay_reorder_overlay:
535 * @overlay: a #GtkOverlay
536 * @child: the overlaid #GtkWidget to move
537 * @index_: the new index for @child in the list of overlay children
538 * of @overlay, starting from 0. If negative, indicates the end of
539 * the list
540 *
541 * Moves @child to a new @index in the list of @overlay children.
542 * The list contains overlays in the order that these were
543 * added to @overlay by default. See also #GtkOverlay:index.
544 *
545 * A widget’s index in the @overlay children list determines which order
546 * the children are drawn if they overlap. The first child is drawn at
547 * the bottom. It also affects the default focus chain order.
548 *
549 * Since: 3.18
550 */
551 void
gtk_overlay_reorder_overlay(GtkOverlay * overlay,GtkWidget * child,int index_)552 gtk_overlay_reorder_overlay (GtkOverlay *overlay,
553 GtkWidget *child,
554 int index_)
555 {
556 GtkOverlayPrivate *priv;
557 GSList *old_link;
558 GSList *new_link;
559 GSList *l;
560 GtkOverlayChild *child_info = NULL;
561 gint old_index, i;
562 gint index;
563
564 g_return_if_fail (GTK_IS_OVERLAY (overlay));
565 g_return_if_fail (GTK_IS_WIDGET (child));
566
567 priv = GTK_OVERLAY (overlay)->priv;
568
569 old_link = priv->children;
570 old_index = 0;
571 while (old_link)
572 {
573 child_info = old_link->data;
574 if (child_info->widget == child)
575 break;
576
577 old_link = old_link->next;
578 old_index++;
579 }
580
581 g_return_if_fail (old_link != NULL);
582
583 if (index_ < 0)
584 {
585 new_link = NULL;
586 index = g_slist_length (priv->children) - 1;
587 }
588 else
589 {
590 new_link = g_slist_nth (priv->children, index_);
591 index = MIN (index_, g_slist_length (priv->children) - 1);
592 }
593
594 if (index == old_index)
595 return;
596
597 priv->children = g_slist_delete_link (priv->children, old_link);
598 priv->children = g_slist_insert_before (priv->children, new_link, child_info);
599
600 for (i = 0, l = priv->children; l != NULL; l = l->next, i++)
601 {
602 GtkOverlayChild *info = l->data;
603 if ((i < index && i < old_index) ||
604 (i > index && i > old_index))
605 continue;
606 gtk_widget_child_notify (info->widget, "index");
607 }
608
609 if (gtk_widget_get_visible (child) &&
610 gtk_widget_get_visible (GTK_WIDGET (overlay)))
611 gtk_widget_queue_resize (GTK_WIDGET (overlay));
612 }
613
614
615 static void
gtk_overlay_forall(GtkContainer * overlay,gboolean include_internals,GtkCallback callback,gpointer callback_data)616 gtk_overlay_forall (GtkContainer *overlay,
617 gboolean include_internals,
618 GtkCallback callback,
619 gpointer callback_data)
620 {
621 GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv;
622 GtkOverlayChild *child;
623 GSList *children;
624 GtkWidget *main_widget;
625
626 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
627 if (main_widget)
628 (* callback) (main_widget, callback_data);
629
630 children = priv->children;
631 while (children)
632 {
633 child = children->data;
634 children = children->next;
635
636 (* callback) (child->widget, callback_data);
637 }
638 }
639
640 static GtkOverlayChild *
gtk_overlay_get_overlay_child(GtkOverlay * overlay,GtkWidget * child)641 gtk_overlay_get_overlay_child (GtkOverlay *overlay,
642 GtkWidget *child)
643 {
644 GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv;
645 GtkOverlayChild *child_info;
646 GSList *children;
647
648 for (children = priv->children; children; children = children->next)
649 {
650 child_info = children->data;
651
652 if (child_info->widget == child)
653 return child_info;
654 }
655
656 return NULL;
657 }
658
659 static void
gtk_overlay_set_child_property(GtkContainer * container,GtkWidget * child,guint property_id,const GValue * value,GParamSpec * pspec)660 gtk_overlay_set_child_property (GtkContainer *container,
661 GtkWidget *child,
662 guint property_id,
663 const GValue *value,
664 GParamSpec *pspec)
665 {
666 GtkOverlay *overlay = GTK_OVERLAY (container);
667 GtkOverlayChild *child_info;
668 GtkWidget *main_widget;
669
670 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
671 if (child == main_widget)
672 child_info = NULL;
673 else
674 {
675 child_info = gtk_overlay_get_overlay_child (overlay, child);
676 if (child_info == NULL)
677 {
678 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
679 return;
680 }
681 }
682
683 switch (property_id)
684 {
685 case CHILD_PROP_PASS_THROUGH:
686 /* Ignore value on main child */
687 if (child_info)
688 {
689 if (g_value_get_boolean (value) != child_info->pass_through)
690 {
691 child_info->pass_through = g_value_get_boolean (value);
692 if (child_info->window)
693 gdk_window_set_pass_through (child_info->window, child_info->pass_through);
694 gtk_container_child_notify (container, child, "pass-through");
695 }
696 }
697 break;
698 case CHILD_PROP_INDEX:
699 if (child_info != NULL)
700 gtk_overlay_reorder_overlay (GTK_OVERLAY (container),
701 child,
702 g_value_get_int (value));
703 break;
704
705 default:
706 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
707 break;
708 }
709 }
710
711 static void
gtk_overlay_get_child_property(GtkContainer * container,GtkWidget * child,guint property_id,GValue * value,GParamSpec * pspec)712 gtk_overlay_get_child_property (GtkContainer *container,
713 GtkWidget *child,
714 guint property_id,
715 GValue *value,
716 GParamSpec *pspec)
717 {
718 GtkOverlay *overlay = GTK_OVERLAY (container);
719 GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv;
720 GtkOverlayChild *child_info;
721 GtkWidget *main_widget;
722
723 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
724 if (child == main_widget)
725 child_info = NULL;
726 else
727 {
728 child_info = gtk_overlay_get_overlay_child (overlay, child);
729 if (child_info == NULL)
730 {
731 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
732 return;
733 }
734 }
735
736 switch (property_id)
737 {
738 case CHILD_PROP_PASS_THROUGH:
739 if (child_info)
740 g_value_set_boolean (value, child_info->pass_through);
741 else
742 g_value_set_boolean (value, FALSE);
743 break;
744 case CHILD_PROP_INDEX:
745 g_value_set_int (value, g_slist_index (priv->children, child_info));
746 break;
747 default:
748 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
749 break;
750 }
751 }
752
753
754 static void
gtk_overlay_class_init(GtkOverlayClass * klass)755 gtk_overlay_class_init (GtkOverlayClass *klass)
756 {
757 GObjectClass *object_class = G_OBJECT_CLASS (klass);
758 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
759 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
760
761 widget_class->size_allocate = gtk_overlay_size_allocate;
762 widget_class->realize = gtk_overlay_realize;
763 widget_class->unrealize = gtk_overlay_unrealize;
764 widget_class->map = gtk_overlay_map;
765 widget_class->unmap = gtk_overlay_unmap;
766
767 container_class->remove = gtk_overlay_remove;
768 container_class->forall = gtk_overlay_forall;
769 container_class->set_child_property = gtk_overlay_set_child_property;
770 container_class->get_child_property = gtk_overlay_get_child_property;
771
772 klass->get_child_position = gtk_overlay_get_child_position;
773
774 /**
775 * GtkOverlay:pass-through:
776 *
777 * Whether to pass input through the overlay child to the main child.
778 * (Of course, this has no effect when set on the main child itself.)
779 *
780 * Note that this is implemented by calling gdk_window_set_pass_through()
781 * on the window that the overlay child is placed in. If the descendents
782 * of the overlay child have their own windows, you need to manually call
783 * that function on them to achieve the desired effect.
784 *
785 * Since: 3.18
786 */
787 gtk_container_class_install_child_property (container_class, CHILD_PROP_PASS_THROUGH,
788 g_param_spec_boolean ("pass-through", P_("Pass Through"), P_("Pass through input, does not affect main child"),
789 FALSE,
790 GTK_PARAM_READWRITE));
791
792 /**
793 * GtkOverlay:index:
794 *
795 * The index of the overlay child in the parent (or -1 for the main child).
796 * See gtk_overlay_reorder_overlay().
797 *
798 * Since: 3.18
799 */
800 gtk_container_class_install_child_property (container_class, CHILD_PROP_INDEX,
801 g_param_spec_int ("index",
802 P_("Index"),
803 P_("The index of the overlay in the parent, -1 for the main child"),
804 -1, G_MAXINT, 0,
805 GTK_PARAM_READWRITE));
806
807 /**
808 * GtkOverlay::get-child-position:
809 * @overlay: the #GtkOverlay
810 * @widget: the child widget to position
811 * @allocation: (type Gdk.Rectangle) (out caller-allocates): return
812 * location for the allocation
813 *
814 * The ::get-child-position signal is emitted to determine
815 * the position and size of any overlay child widgets. A
816 * handler for this signal should fill @allocation with
817 * the desired position and size for @widget, relative to
818 * the 'main' child of @overlay.
819 *
820 * The default handler for this signal uses the @widget's
821 * halign and valign properties to determine the position
822 * and gives the widget its natural size (except that an
823 * alignment of %GTK_ALIGN_FILL will cause the overlay to
824 * be full-width/height). If the main child is a
825 * #GtkScrolledWindow, the overlays are placed relative
826 * to its contents.
827 *
828 * Returns: %TRUE if the @allocation has been filled
829 */
830 signals[GET_CHILD_POSITION] =
831 g_signal_new (I_("get-child-position"),
832 G_TYPE_FROM_CLASS (object_class),
833 G_SIGNAL_RUN_LAST,
834 G_STRUCT_OFFSET (GtkOverlayClass, get_child_position),
835 _gtk_boolean_handled_accumulator, NULL,
836 _gtk_marshal_BOOLEAN__OBJECT_BOXED,
837 G_TYPE_BOOLEAN, 2,
838 GTK_TYPE_WIDGET,
839 GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE);
840 g_signal_set_va_marshaller (signals[GET_CHILD_POSITION],
841 G_TYPE_FROM_CLASS (object_class),
842 _gtk_marshal_BOOLEAN__OBJECT_BOXEDv);
843
844 gtk_widget_class_set_css_name (widget_class, "overlay");
845 }
846
847 static void
gtk_overlay_init(GtkOverlay * overlay)848 gtk_overlay_init (GtkOverlay *overlay)
849 {
850 overlay->priv = gtk_overlay_get_instance_private (overlay);
851
852 gtk_widget_set_has_window (GTK_WIDGET (overlay), FALSE);
853 }
854
855 static void
gtk_overlay_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)856 gtk_overlay_buildable_add_child (GtkBuildable *buildable,
857 GtkBuilder *builder,
858 GObject *child,
859 const gchar *type)
860 {
861 if (type && strcmp (type, "overlay") == 0)
862 gtk_overlay_add_overlay (GTK_OVERLAY (buildable), GTK_WIDGET (child));
863 else if (!type)
864 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
865 else
866 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
867 }
868
869 static void
gtk_overlay_buildable_init(GtkBuildableIface * iface)870 gtk_overlay_buildable_init (GtkBuildableIface *iface)
871 {
872 iface->add_child = gtk_overlay_buildable_add_child;
873 }
874
875 /**
876 * gtk_overlay_new:
877 *
878 * Creates a new #GtkOverlay.
879 *
880 * Returns: a new #GtkOverlay object.
881 *
882 * Since: 3.2
883 */
884 GtkWidget *
gtk_overlay_new(void)885 gtk_overlay_new (void)
886 {
887 return g_object_new (GTK_TYPE_OVERLAY, NULL);
888 }
889
890 /**
891 * gtk_overlay_add_overlay:
892 * @overlay: a #GtkOverlay
893 * @widget: a #GtkWidget to be added to the container
894 *
895 * Adds @widget to @overlay.
896 *
897 * The widget will be stacked on top of the main widget
898 * added with gtk_container_add().
899 *
900 * The position at which @widget is placed is determined
901 * from its #GtkWidget:halign and #GtkWidget:valign properties.
902 *
903 * Since: 3.2
904 */
905 void
gtk_overlay_add_overlay(GtkOverlay * overlay,GtkWidget * widget)906 gtk_overlay_add_overlay (GtkOverlay *overlay,
907 GtkWidget *widget)
908 {
909 GtkOverlayPrivate *priv;
910 GtkOverlayChild *child;
911
912 g_return_if_fail (GTK_IS_OVERLAY (overlay));
913 g_return_if_fail (GTK_IS_WIDGET (widget));
914
915 priv = overlay->priv;
916 child = g_slice_new0 (GtkOverlayChild);
917 child->widget = widget;
918
919 priv->children = g_slist_append (priv->children, child);
920
921 if (gtk_widget_get_realized (GTK_WIDGET (overlay)))
922 {
923 child->window = gtk_overlay_create_child_window (overlay, child);
924 gtk_widget_set_parent (widget, GTK_WIDGET (overlay));
925 }
926 else
927 gtk_widget_set_parent (widget, GTK_WIDGET (overlay));
928
929 gtk_widget_child_notify (widget, "index");
930 }
931
932 /**
933 * gtk_overlay_set_overlay_pass_through:
934 * @overlay: a #GtkOverlay
935 * @widget: an overlay child of #GtkOverlay
936 * @pass_through: whether the child should pass the input through
937 *
938 * Convenience function to set the value of the #GtkOverlay:pass-through
939 * child property for @widget.
940 *
941 * Since: 3.18
942 */
943 void
gtk_overlay_set_overlay_pass_through(GtkOverlay * overlay,GtkWidget * widget,gboolean pass_through)944 gtk_overlay_set_overlay_pass_through (GtkOverlay *overlay,
945 GtkWidget *widget,
946 gboolean pass_through)
947 {
948 g_return_if_fail (GTK_IS_OVERLAY (overlay));
949 g_return_if_fail (GTK_IS_WIDGET (widget));
950
951 gtk_container_child_set (GTK_CONTAINER (overlay), widget,
952 "pass-through", pass_through,
953 NULL);
954 }
955
956 /**
957 * gtk_overlay_get_overlay_pass_through:
958 * @overlay: a #GtkOverlay
959 * @widget: an overlay child of #GtkOverlay
960 *
961 * Convenience function to get the value of the #GtkOverlay:pass-through
962 * child property for @widget.
963 *
964 * Returns: whether the widget is a pass through child.
965 *
966 * Since: 3.18
967 */
968 gboolean
gtk_overlay_get_overlay_pass_through(GtkOverlay * overlay,GtkWidget * widget)969 gtk_overlay_get_overlay_pass_through (GtkOverlay *overlay,
970 GtkWidget *widget)
971 {
972 gboolean pass_through;
973
974 g_return_val_if_fail (GTK_IS_OVERLAY (overlay), FALSE);
975 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
976
977 gtk_container_child_get (GTK_CONTAINER (overlay), widget,
978 "pass-through", &pass_through,
979 NULL);
980
981 return pass_through;
982 }
983