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