3 * This file is part of gtk
5 * Copyright (C) 2011 - Ignacio Casal Quinteiro, Mike Krüger
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.
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.
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/>.
23 #include "gtkoverlay.h"
24 #include "gtkbuildable.h"
25 #include "gtkscrolledwindow.h"
26 #include "gtkmarshalers.h"
28 #include "gtkprivate.h"
33 * @short_description: A container which overlays widgets on top of each other
36 * GtkOverlay is a container which contains a single main child, on top
37 * of which it can place <firstterm>overlay</firstterm> widgets. The
38 * position of each overlay widget is determined by its #GtkWidget:halign
39 * and #GtkWidget:valign properties. E.g. a widget with both alignments
40 * set to %GTK_ALIGN_START will be placed at the top left corner of the
41 * main widget, whereas an overlay with halign set to %GTK_ALIGN_CENTER
42 * and valign set to %GTK_ALIGN_END will be placed a the bottom edge of
43 * the main widget, horizontally centered. The position can be adjusted
44 * by setting the margin properties of the child to non-zero values.
46 * More complicated placement of overlays is possible by connecting
47 * to the #GtkOverlay::get-child-position signal.
49 * <refsect2 id="GtkOverlay-BUILDER-UI">
50 * <title>GtkOverlay as GtkBuildable</title>
52 * The GtkOverlay implementation of the GtkBuildable interface
53 * supports placing a child as an overlay by specifying "overlay" as
54 * the "type" attribute of a <tag class="starttag">child</tag> element.
59 struct _GtkOverlayPrivate
64 typedef struct _GtkOverlayChild GtkOverlayChild;
66 struct _GtkOverlayChild
77 static guint signals[LAST_SIGNAL] = { 0 };
79 static void gtk_overlay_buildable_init (GtkBuildableIface *iface);
81 G_DEFINE_TYPE_WITH_CODE (GtkOverlay, gtk_overlay, GTK_TYPE_BIN,
82 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
83 gtk_overlay_buildable_init))
86 gtk_overlay_create_child_window (GtkOverlay *overlay,
89 GtkWidget *widget = GTK_WIDGET (overlay);
90 GtkAllocation allocation;
92 GdkWindowAttr attributes;
95 gtk_widget_get_allocation (child, &allocation);
97 attributes.window_type = GDK_WINDOW_CHILD;
98 attributes.wclass = GDK_INPUT_OUTPUT;
99 attributes.width = allocation.width;
100 attributes.height = allocation.height;
101 attributes.x = allocation.x;
102 attributes.y = allocation.y;
103 attributes_mask = GDK_WA_X | GDK_WA_Y;
104 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
106 window = gdk_window_new (gtk_widget_get_window (widget),
107 &attributes, attributes_mask);
108 gdk_window_set_user_data (window, overlay);
109 gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
111 gtk_widget_set_parent_window (child, window);
117 gtk_overlay_get_main_widget_allocation (GtkOverlay *overlay,
118 GtkAllocation *main_alloc_out)
120 GtkWidget *main_widget;
121 GtkAllocation main_alloc;
123 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
125 /* special-case scrolled windows */
126 if (GTK_IS_SCROLLED_WINDOW (main_widget))
128 GtkWidget *grandchild;
132 grandchild = gtk_bin_get_child (GTK_BIN (main_widget));
133 res = gtk_widget_translate_coordinates (grandchild, main_widget, 0, 0, &x, &y);
146 main_alloc.width = gtk_widget_get_allocated_width (grandchild);
147 main_alloc.height = gtk_widget_get_allocated_height (grandchild);
153 main_alloc.width = gtk_widget_get_allocated_width (main_widget);
154 main_alloc.height = gtk_widget_get_allocated_height (main_widget);
158 *main_alloc_out = main_alloc;
162 gtk_overlay_child_allocate (GtkOverlay *overlay,
163 GtkOverlayChild *child)
165 gint left, right, top, bottom;
166 GtkAllocation allocation, child_allocation, overlay_allocation;
169 if (gtk_widget_get_mapped (GTK_WIDGET (overlay)))
171 if (gtk_widget_get_visible (child->widget))
172 gdk_window_show (child->window);
173 else if (gdk_window_is_visible (child->window))
174 gdk_window_hide (child->window);
177 if (!gtk_widget_get_visible (child->widget))
180 g_signal_emit (overlay, signals[GET_CHILD_POSITION],
181 0, child->widget, &allocation, &result);
183 gtk_widget_get_allocation (GTK_WIDGET (overlay), &overlay_allocation);
185 allocation.x += overlay_allocation.x;
186 allocation.y += overlay_allocation.y;
188 /* put the margins outside the window; also arrange things
189 * so that the adjusted child allocation still ends up at 0, 0
191 left = gtk_widget_get_margin_left (child->widget);
192 right = gtk_widget_get_margin_right (child->widget);
193 top = gtk_widget_get_margin_top (child->widget);
194 bottom = gtk_widget_get_margin_bottom (child->widget);
196 child_allocation.x = - left;
197 child_allocation.y = - top;
198 child_allocation.width = allocation.width;
199 child_allocation.height = allocation.height;
201 allocation.x += left;
203 allocation.width -= left + right;
204 allocation.height -= top + bottom;
207 gdk_window_move_resize (child->window,
208 allocation.x, allocation.y,
209 allocation.width, allocation.height);
211 gtk_widget_size_allocate (child->widget, &child_allocation);
215 gtk_overlay_get_preferred_width (GtkWidget *widget,
219 GtkBin *bin = GTK_BIN (widget);
228 child = gtk_bin_get_child (bin);
229 if (child && gtk_widget_get_visible (child))
230 gtk_widget_get_preferred_width (child, minimum, natural);
234 gtk_overlay_get_preferred_height (GtkWidget *widget,
238 GtkBin *bin = GTK_BIN (widget);
247 child = gtk_bin_get_child (bin);
248 if (child && gtk_widget_get_visible (child))
249 gtk_widget_get_preferred_height (child, minimum, natural);
253 gtk_overlay_size_allocate (GtkWidget *widget,
254 GtkAllocation *allocation)
256 GtkOverlay *overlay = GTK_OVERLAY (widget);
257 GtkOverlayPrivate *priv = overlay->priv;
259 GtkWidget *main_widget;
261 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->size_allocate (widget, allocation);
263 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
264 if (!main_widget || !gtk_widget_get_visible (main_widget))
267 gtk_widget_size_allocate (main_widget, allocation);
269 for (children = priv->children; children; children = children->next)
271 gtk_overlay_child_allocate (overlay, children->data);
276 effective_align (GtkAlign align,
277 GtkTextDirection direction)
281 case GTK_ALIGN_START:
282 return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START;
284 return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END;
291 gtk_overlay_get_child_position (GtkOverlay *overlay,
293 GtkAllocation *alloc)
295 GtkAllocation main_alloc;
298 GtkTextDirection direction;
300 gtk_overlay_get_main_widget_allocation (overlay, &main_alloc);
301 gtk_widget_get_preferred_size (widget, NULL, &req);
303 alloc->x = main_alloc.x;
304 alloc->width = MIN (main_alloc.width, req.width);
306 direction = gtk_widget_get_direction (widget);
308 halign = gtk_widget_get_halign (widget);
309 switch (effective_align (halign, direction))
311 case GTK_ALIGN_START:
315 alloc->width = main_alloc.width;
317 case GTK_ALIGN_CENTER:
318 alloc->x += main_alloc.width / 2 - req.width / 2;
321 alloc->x += main_alloc.width - req.width;
325 alloc->y = main_alloc.y;
326 alloc->height = MIN (main_alloc.height, req.height);
328 switch (gtk_widget_get_valign (widget))
330 case GTK_ALIGN_START:
334 alloc->height = main_alloc.height;
336 case GTK_ALIGN_CENTER:
337 alloc->y += main_alloc.height / 2 - req.height / 2;
340 alloc->y += main_alloc.height - req.height;
348 gtk_overlay_realize (GtkWidget *widget)
350 GtkOverlay *overlay = GTK_OVERLAY (widget);
351 GtkOverlayPrivate *priv = overlay->priv;
352 GtkOverlayChild *child;
355 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->realize (widget);
357 for (children = priv->children; children; children = children->next)
359 child = children->data;
361 if (child->window == NULL)
362 child->window = gtk_overlay_create_child_window (overlay, child->widget);
367 gtk_overlay_unrealize (GtkWidget *widget)
369 GtkOverlay *overlay = GTK_OVERLAY (widget);
370 GtkOverlayPrivate *priv = overlay->priv;
371 GtkOverlayChild *child;
374 for (children = priv->children; children; children = children->next)
376 child = children->data;
378 gtk_widget_set_parent_window (child->widget, NULL);
379 gdk_window_set_user_data (child->window, NULL);
380 gdk_window_destroy (child->window);
381 child->window = NULL;
384 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unrealize (widget);
388 gtk_overlay_map (GtkWidget *widget)
390 GtkOverlay *overlay = GTK_OVERLAY (widget);
391 GtkOverlayPrivate *priv = overlay->priv;
392 GtkOverlayChild *child;
395 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->map (widget);
397 for (children = priv->children; children; children = children->next)
399 child = children->data;
401 if (child->window != NULL &&
402 gtk_widget_get_visible (child->widget) &&
403 gtk_widget_get_child_visible (child->widget))
404 gdk_window_show (child->window);
409 gtk_overlay_unmap (GtkWidget *widget)
411 GtkOverlay *overlay = GTK_OVERLAY (widget);
412 GtkOverlayPrivate *priv = overlay->priv;
413 GtkOverlayChild *child;
416 for (children = priv->children; children; children = children->next)
418 child = children->data;
420 if (child->window != NULL &&
421 gdk_window_is_visible (child->window))
422 gdk_window_hide (child->window);
425 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unmap (widget);
429 gtk_overlay_draw (GtkWidget *widget,
432 GtkOverlay *overlay = GTK_OVERLAY (widget);
433 GtkOverlayPrivate *priv = overlay->priv;
434 GtkOverlayChild *child;
437 for (children = priv->children; children; children = children->next)
439 child = children->data;
441 if (gtk_cairo_should_draw_window (cr, child->window))
444 gtk_cairo_transform_to_window (cr, widget, child->window);
445 gtk_render_background (gtk_widget_get_style_context (widget),
448 gdk_window_get_width (child->window),
449 gdk_window_get_height (child->window));
454 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->draw (widget, cr);
460 gtk_overlay_remove (GtkContainer *container,
463 GtkOverlayPrivate *priv = GTK_OVERLAY (container)->priv;
464 GtkOverlayChild *child;
467 for (children = priv->children; children; children = children->next)
469 child = children->data;
471 if (child->widget == widget)
473 if (child->window != NULL)
475 gdk_window_set_user_data (child->window, NULL);
476 gdk_window_destroy (child->window);
479 gtk_widget_unparent (widget);
481 priv->children = g_slist_delete_link (priv->children, children);
482 g_slice_free (GtkOverlayChild, child);
488 GTK_CONTAINER_CLASS (gtk_overlay_parent_class)->remove (container, widget);
492 gtk_overlay_forall (GtkContainer *overlay,
493 gboolean include_internals,
494 GtkCallback callback,
495 gpointer callback_data)
497 GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv;
498 GtkOverlayChild *child;
500 GtkWidget *main_widget;
502 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
504 (* callback) (main_widget, callback_data);
506 children = priv->children;
509 child = children->data;
510 children = children->next;
512 (* callback) (child->widget, callback_data);
517 gtk_overlay_class_init (GtkOverlayClass *klass)
519 GObjectClass *object_class = G_OBJECT_CLASS (klass);
520 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
521 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
523 widget_class->get_preferred_width = gtk_overlay_get_preferred_width;
524 widget_class->get_preferred_height = gtk_overlay_get_preferred_height;
525 widget_class->size_allocate = gtk_overlay_size_allocate;
526 widget_class->realize = gtk_overlay_realize;
527 widget_class->unrealize = gtk_overlay_unrealize;
528 widget_class->map = gtk_overlay_map;
529 widget_class->unmap = gtk_overlay_unmap;
530 widget_class->draw = gtk_overlay_draw;
532 container_class->remove = gtk_overlay_remove;
533 container_class->forall = gtk_overlay_forall;
535 klass->get_child_position = gtk_overlay_get_child_position;
538 * GtkOverlay::get-child-position:
539 * @overlay: the #GtkOverlay
540 * @widget: the child widget to position
541 * @allocation: (out): return location for the allocation
543 * The ::get-child-position signal is emitted to determine
544 * the position and size of any overlay child widgets. A
545 * handler for this signal should fill @allocation with
546 * the desired position and size for @widget, relative to
547 * the 'main' child of @overlay.
549 * The default handler for this signal uses the @widget's
550 * halign and valign properties to determine the position
551 * and gives the widget its natural size (except that an
552 * alignment of %GTK_ALIGN_FILL will cause the overlay to
553 * be full-width/height). If the main child is a
554 * #GtkScrolledWindow, the overlays are placed relative
557 * Return: %TRUE if the @allocation has been filled
559 signals[GET_CHILD_POSITION] =
560 g_signal_new (I_("get-child-position"),
561 G_TYPE_FROM_CLASS (object_class),
563 G_STRUCT_OFFSET (GtkOverlayClass, get_child_position),
564 _gtk_boolean_handled_accumulator, NULL,
565 _gtk_marshal_BOOLEAN__OBJECT_BOXED,
568 GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE);
570 g_type_class_add_private (object_class, sizeof (GtkOverlayPrivate));
574 gtk_overlay_init (GtkOverlay *overlay)
576 overlay->priv = G_TYPE_INSTANCE_GET_PRIVATE (overlay, GTK_TYPE_OVERLAY, GtkOverlayPrivate);
578 gtk_widget_set_has_window (GTK_WIDGET (overlay), FALSE);
582 gtk_overlay_buildable_add_child (GtkBuildable *buildable,
587 if (type && strcmp (type, "overlay") == 0)
588 gtk_overlay_add_overlay (GTK_OVERLAY (buildable), GTK_WIDGET (child));
590 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
592 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
596 gtk_overlay_buildable_init (GtkBuildableIface *iface)
598 iface->add_child = gtk_overlay_buildable_add_child;
604 * Creates a new #GtkOverlay.
606 * Returns: a new #GtkOverlay object.
611 gtk_overlay_new (void)
613 return g_object_new (GTK_TYPE_OVERLAY, NULL);
617 * gtk_overlay_add_overlay:
618 * @overlay: a #GtkOverlay
619 * @widget: a #GtkWidget to be added to the container
621 * Adds @widget to @overlay.
623 * The widget will be stacked on top of the main widget
624 * added with gtk_container_add().
626 * The position at which @widget is placed is determined
627 * from its #GtkWidget:halign and #GtkWidget:valign properties.
632 gtk_overlay_add_overlay (GtkOverlay *overlay,
635 GtkOverlayPrivate *priv = overlay->priv;
636 GtkOverlayChild *child;
638 g_return_if_fail (GTK_IS_OVERLAY (overlay));
639 g_return_if_fail (GTK_IS_WIDGET (widget));
641 child = g_slice_new0 (GtkOverlayChild);
642 child->widget = widget;
644 priv->children = g_slist_append (priv->children, child);
646 if (gtk_widget_get_realized (GTK_WIDGET (overlay)))
648 child->window = gtk_overlay_create_child_window (overlay, widget);
649 gtk_widget_set_parent (widget, GTK_WIDGET (overlay));
650 gtk_overlay_child_allocate (overlay, child);
653 gtk_widget_set_parent (widget, GTK_WIDGET (overlay));