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, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 #include "gtkoverlay.h"
26 #include "gtkbuildable.h"
27 #include "gtkscrolledwindow.h"
28 #include "gtkmainprivate.h"
29 #include "gtkmarshalers.h"
31 #include "gtkprivate.h"
36 * @short_description: A container which overlays widgets on top of each other
39 * GtkOverlay is a container which contains a single main child, on top
40 * of which it can place <firstterm>overlay</firstterm> widgets. The
41 * position of each overlay widget is determined by its #GtkWidget::halign
42 * and #GtkWidget::valign properties. E.g. a widget with both alignments
43 * set to %GTK_ALIGN_START will be placed at the top left corner of the
44 * main widget, whereas an overlay with halign set to %GTK_ALIGN_CENTER
45 * and valign set to %GTK_ALIGN_END will be placed a the bottom edge of
46 * the main widget, horizontally centered. The position can be adjusted
47 * by setting the margin properties of the child to non-zero values.
49 * More complicated placement of overlays is possible by connecting
50 * to the #GtkOverlay::get-child-position signal.
52 * <refsect2 id="GtkOverlay-BUILDER-UI">
53 * <title>GtkOverlay as GtkBuildable</title>
55 * The GtkOverlay implementation of the GtkBuildable interface
56 * supports placing a child as an overlay by specifying "overlay" as
57 * the "type" attribute of a <tag class="starttag">child</tag> element.
62 struct _GtkOverlayPrivate
67 typedef struct _GtkOverlayChild GtkOverlayChild;
69 struct _GtkOverlayChild
80 static guint signals[LAST_SIGNAL] = { 0 };
82 static void gtk_overlay_buildable_init (GtkBuildableIface *iface);
84 G_DEFINE_TYPE_WITH_CODE (GtkOverlay, gtk_overlay, GTK_TYPE_BIN,
85 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
86 gtk_overlay_buildable_init))
89 gtk_overlay_create_child_window (GtkOverlay *overlay,
92 GtkWidget *widget = GTK_WIDGET (overlay);
93 GtkAllocation allocation;
95 GdkWindowAttr attributes;
98 gtk_widget_get_allocation (child, &allocation);
100 attributes.window_type = GDK_WINDOW_CHILD;
101 attributes.wclass = GDK_INPUT_OUTPUT;
102 attributes.width = allocation.width;
103 attributes.height = allocation.height;
104 attributes.x = allocation.x;
105 attributes.y = allocation.y;
106 attributes_mask = GDK_WA_X | GDK_WA_Y;
107 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
109 window = gdk_window_new (gtk_widget_get_window (widget),
110 &attributes, attributes_mask);
111 gdk_window_set_user_data (window, overlay);
112 gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
114 gtk_widget_set_parent_window (child, window);
120 gtk_overlay_child_allocate (GtkOverlayChild *child,
121 GtkAllocation *allocation)
123 gint left, right, top, bottom;
124 GtkAllocation child_allocation;
126 /* put the margins outside the window; also arrange things
127 * so that the adjusted child allocation still ends up at 0, 0
129 left = gtk_widget_get_margin_left (child->widget);
130 right = gtk_widget_get_margin_right (child->widget);
131 top = gtk_widget_get_margin_top (child->widget);
132 bottom = gtk_widget_get_margin_bottom (child->widget);
134 child_allocation.x = - left;
135 child_allocation.y = - top;
136 child_allocation.width = allocation->width;
137 child_allocation.height = allocation->height;
139 allocation->x += left;
140 allocation->y += top;
141 allocation->width -= left + right;
142 allocation->height -= top + bottom;
145 gdk_window_move_resize (child->window,
146 allocation->x, allocation->y,
147 allocation->width, allocation->height);
149 gtk_widget_size_allocate (child->widget, &child_allocation);
153 gtk_overlay_get_preferred_width (GtkWidget *widget,
157 GtkBin *bin = GTK_BIN (widget);
166 child = gtk_bin_get_child (bin);
167 if (child && gtk_widget_get_visible (child))
168 gtk_widget_get_preferred_width (child, minimum, natural);
172 gtk_overlay_get_preferred_height (GtkWidget *widget,
176 GtkBin *bin = GTK_BIN (widget);
185 child = gtk_bin_get_child (bin);
186 if (child && gtk_widget_get_visible (child))
187 gtk_widget_get_preferred_height (child, minimum, natural);
191 gtk_overlay_size_allocate (GtkWidget *widget,
192 GtkAllocation *allocation)
194 GtkOverlay *overlay = GTK_OVERLAY (widget);
195 GtkOverlayPrivate *priv = overlay->priv;
197 GtkWidget *main_widget;
199 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->size_allocate (widget, allocation);
201 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
202 if (!main_widget || !gtk_widget_get_visible (main_widget))
205 gtk_widget_size_allocate (main_widget, allocation);
207 for (children = priv->children; children; children = children->next)
209 GtkOverlayChild *child;
213 child = children->data;
215 if (gtk_widget_get_mapped (GTK_WIDGET (overlay)))
217 if (gtk_widget_get_visible (child->widget))
218 gdk_window_show (child->window);
219 else if (gdk_window_is_visible (child->window))
220 gdk_window_hide (child->window);
223 if (!gtk_widget_get_visible (child->widget))
226 g_signal_emit (overlay, signals[GET_CHILD_POSITION],
227 0, child->widget, &alloc, &result);
229 alloc.x += allocation->x;
230 alloc.y += allocation->y;
232 gtk_overlay_child_allocate (child, &alloc);
237 effective_align (GtkAlign align,
238 GtkTextDirection direction)
242 case GTK_ALIGN_START:
243 return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START;
245 return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END;
252 gtk_overlay_get_child_position (GtkOverlay *overlay,
254 GtkAllocation *alloc)
256 GtkWidget *main_widget;
257 GtkAllocation main_alloc;
260 GtkTextDirection direction;
262 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
264 /* special-case scrolled windows */
265 if (GTK_IS_SCROLLED_WINDOW (main_widget))
267 GtkWidget *grandchild;
270 grandchild = gtk_bin_get_child (GTK_BIN (main_widget));
271 gtk_widget_translate_coordinates (grandchild, main_widget, 0, 0, &x, &y);
275 main_alloc.width = gtk_widget_get_allocated_width (grandchild);
276 main_alloc.height = gtk_widget_get_allocated_height (grandchild);
282 main_alloc.width = gtk_widget_get_allocated_width (main_widget);
283 main_alloc.height = gtk_widget_get_allocated_height (main_widget);
286 gtk_widget_get_preferred_size (widget, NULL, &req);
288 alloc->x = main_alloc.x;
289 alloc->width = MIN (main_alloc.width, req.width);
291 direction = gtk_widget_get_direction (widget);
293 halign = gtk_widget_get_halign (widget);
294 switch (effective_align (halign, direction))
296 case GTK_ALIGN_START:
300 alloc->width = main_alloc.width;
302 case GTK_ALIGN_CENTER:
303 alloc->x += main_alloc.width / 2 - req.width / 2;
306 alloc->x += main_alloc.width - req.width;
310 alloc->y = main_alloc.y;
311 alloc->height = MIN (main_alloc.height, req.height);
313 switch (gtk_widget_get_valign (widget))
315 case GTK_ALIGN_START:
319 alloc->height = main_alloc.height;
321 case GTK_ALIGN_CENTER:
322 alloc->y += main_alloc.height / 2 - req.height / 2;
325 alloc->y += main_alloc.height - req.height;
333 gtk_overlay_realize (GtkWidget *widget)
335 GtkOverlay *overlay = GTK_OVERLAY (widget);
336 GtkOverlayPrivate *priv = overlay->priv;
337 GtkOverlayChild *child;
340 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->realize (widget);
342 for (children = priv->children; children; children = children->next)
344 child = children->data;
346 if (child->window == NULL)
347 child->window = gtk_overlay_create_child_window (overlay, child->widget);
352 gtk_overlay_unrealize (GtkWidget *widget)
354 GtkOverlay *overlay = GTK_OVERLAY (widget);
355 GtkOverlayPrivate *priv = overlay->priv;
356 GtkOverlayChild *child;
359 for (children = priv->children; children; children = children->next)
361 child = children->data;
363 gtk_widget_set_parent_window (child->widget, NULL);
364 gdk_window_set_user_data (child->window, NULL);
365 gdk_window_destroy (child->window);
366 child->window = NULL;
369 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unrealize (widget);
373 gtk_overlay_map (GtkWidget *widget)
375 GtkOverlay *overlay = GTK_OVERLAY (widget);
376 GtkOverlayPrivate *priv = overlay->priv;
377 GtkOverlayChild *child;
380 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->map (widget);
382 for (children = priv->children; children; children = children->next)
384 child = children->data;
386 if (child->window != NULL &&
387 gtk_widget_get_visible (child->widget) &&
388 gtk_widget_get_child_visible (child->widget))
389 gdk_window_show (child->window);
394 gtk_overlay_unmap (GtkWidget *widget)
396 GtkOverlay *overlay = GTK_OVERLAY (widget);
397 GtkOverlayPrivate *priv = overlay->priv;
398 GtkOverlayChild *child;
401 for (children = priv->children; children; children = children->next)
403 child = children->data;
405 if (child->window != NULL &&
406 gdk_window_is_visible (child->window))
407 gdk_window_hide (child->window);
410 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unmap (widget);
414 gtk_overlay_draw (GtkWidget *widget,
417 GtkOverlay *overlay = GTK_OVERLAY (widget);
418 GtkOverlayPrivate *priv = overlay->priv;
419 GtkOverlayChild *child;
422 for (children = priv->children; children; children = children->next)
424 child = children->data;
426 if (gtk_cairo_should_draw_window (cr, child->window))
429 gtk_cairo_transform_to_window (cr, widget, child->window);
430 gtk_render_background (gtk_widget_get_style_context (widget),
433 gdk_window_get_width (child->window),
434 gdk_window_get_height (child->window));
439 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->draw (widget, cr);
445 gtk_overlay_remove (GtkContainer *container,
448 GtkOverlayPrivate *priv = GTK_OVERLAY (container)->priv;
449 GtkOverlayChild *child;
452 for (children = priv->children; children; children = children->next)
454 child = children->data;
456 if (child->widget == widget)
458 if (child->window != NULL)
460 gdk_window_set_user_data (child->window, NULL);
461 gdk_window_destroy (child->window);
464 gtk_widget_unparent (widget);
466 priv->children = g_slist_delete_link (priv->children, children);
467 g_slice_free (GtkOverlayChild, child);
473 GTK_CONTAINER_CLASS (gtk_overlay_parent_class)->remove (container, widget);
477 gtk_overlay_forall (GtkContainer *overlay,
478 gboolean include_internals,
479 GtkCallback callback,
480 gpointer callback_data)
482 GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv;
483 GtkOverlayChild *child;
485 GtkWidget *main_widget;
487 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
489 (* callback) (main_widget, callback_data);
491 children = priv->children;
494 child = children->data;
495 children = children->next;
497 (* callback) (child->widget, callback_data);
502 gtk_overlay_class_init (GtkOverlayClass *klass)
504 GObjectClass *object_class = G_OBJECT_CLASS (klass);
505 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
506 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
508 widget_class->get_preferred_width = gtk_overlay_get_preferred_width;
509 widget_class->get_preferred_height = gtk_overlay_get_preferred_height;
510 widget_class->size_allocate = gtk_overlay_size_allocate;
511 widget_class->realize = gtk_overlay_realize;
512 widget_class->unrealize = gtk_overlay_unrealize;
513 widget_class->map = gtk_overlay_map;
514 widget_class->unmap = gtk_overlay_unmap;
515 widget_class->draw = gtk_overlay_draw;
517 container_class->remove = gtk_overlay_remove;
518 container_class->forall = gtk_overlay_forall;
520 klass->get_child_position = gtk_overlay_get_child_position;
523 * GtkOverlay::get-child-position:
524 * @overlay: the #GtkOverlay
525 * @widget: the child widget to position
526 * @allocation: (out): return location for the allocation
528 * The ::get-child-position signal is emitted to determine
529 * the position and size of any overlay child widgets. A
530 * handler for this signal should fill @allocation with
531 * the desired position and size for @widget, relative to
532 * the 'main' child of @overlay.
534 * The default handler for this signal uses the @widget's
535 * halign and valign properties to determine the position
536 * and gives the widget its natural size (except that an
537 * alignment of %GTK_ALIGN_FILL will cause the overlay to
538 * be full-width/height). If the main child is a
539 * #GtkScrolledWindow, the overlays are placed relative
542 * Return: %TRUE if the @allocation has been filled
544 signals[GET_CHILD_POSITION] =
545 g_signal_new (I_("get-child-position"),
546 G_TYPE_FROM_CLASS (object_class),
548 G_STRUCT_OFFSET (GtkOverlayClass, get_child_position),
549 _gtk_boolean_handled_accumulator, NULL,
550 _gtk_marshal_BOOLEAN__OBJECT_BOXED,
553 GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE);
555 g_type_class_add_private (object_class, sizeof (GtkOverlayPrivate));
559 gtk_overlay_init (GtkOverlay *overlay)
561 overlay->priv = G_TYPE_INSTANCE_GET_PRIVATE (overlay, GTK_TYPE_OVERLAY, GtkOverlayPrivate);
563 gtk_widget_set_has_window (GTK_WIDGET (overlay), FALSE);
567 gtk_overlay_buildable_add_child (GtkBuildable *buildable,
572 if (type && strcmp (type, "overlay") == 0)
573 gtk_overlay_add_overlay (GTK_OVERLAY (buildable), GTK_WIDGET (child));
575 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
577 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
581 gtk_overlay_buildable_init (GtkBuildableIface *iface)
583 iface->add_child = gtk_overlay_buildable_add_child;
589 * Creates a new #GtkOverlay.
591 * Returns: a new #GtkOverlay object.
596 gtk_overlay_new (void)
598 return g_object_new (GTK_TYPE_OVERLAY, NULL);
602 * gtk_overlay_add_overlay:
603 * @overlay: a #GtkOverlay
604 * @widget: a #GtkWidget to be added to the container
606 * Adds @widget to @overlay.
608 * The widget will be stacked on top of the main widget
609 * added with gtk_container_add().
611 * The position at which @widget is placed is determined
612 * from its #GtkWidget::halign and #GtkWidget::valign properties.
617 gtk_overlay_add_overlay (GtkOverlay *overlay,
620 GtkOverlayPrivate *priv = overlay->priv;
621 GtkOverlayChild *child;
623 g_return_if_fail (GTK_IS_OVERLAY (overlay));
624 g_return_if_fail (GTK_IS_WIDGET (widget));
626 child = g_slice_new0 (GtkOverlayChild);
627 child->widget = widget;
629 gtk_widget_set_parent (widget, GTK_WIDGET (overlay));
631 priv->children = g_slist_append (priv->children, child);
633 if (gtk_widget_get_realized (GTK_WIDGET (overlay)))
634 child->window = gtk_overlay_create_child_window (overlay, widget);