diff options
author | Ignacio Casal Quinteiro <icq@gnome.org> | 2011-05-19 18:53:05 +0200 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2011-06-12 23:27:18 -0400 |
commit | cfb2a45f9aef90e82d5811ff2f3075bec7a52aaa (patch) | |
tree | e3565062e98978eb9f174c3c9cbd4c76d4062b1e | |
parent | f50da3babeecc34763933d1e515cfc1ba1da065a (diff) | |
download | gtk+-cfb2a45f9aef90e82d5811ff2f3075bec7a52aaa.tar.gz |
overlay: add initial version of gtkoverlay
-rw-r--r-- | demos/gtk-demo/Makefile.am | 1 | ||||
-rw-r--r-- | demos/gtk-demo/overlay.c | 181 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-docs.sgml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk3-sections.txt | 26 | ||||
-rw-r--r-- | docs/reference/gtk/gtk3.types | 1 | ||||
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtk.symbols | 3 | ||||
-rw-r--r-- | gtk/gtkoverlay.c | 635 | ||||
-rw-r--r-- | gtk/gtkoverlay.h | 79 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/testoverlay.c | 447 |
12 files changed, 1383 insertions, 0 deletions
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 46b805a0bf..e4c45538df 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -30,6 +30,7 @@ demos = \ menus.c \ offscreen_window.c \ offscreen_window2.c \ + overlay.c \ panes.c \ pickers.c \ pixbufs.c \ diff --git a/demos/gtk-demo/overlay.c b/demos/gtk-demo/overlay.c new file mode 100644 index 0000000000..cbddd93cf6 --- /dev/null +++ b/demos/gtk-demo/overlay.c @@ -0,0 +1,181 @@ +/* Overlay + * + * Stack widgets in static positions over a main widget + */ + +#include <gtk/gtk.h> + +GtkWidget * +do_overlay (GtkWidget *do_widget) +{ + static GtkWidget *window = NULL; + + if (!window) + { + GtkWidget *view; + GtkWidget *sw; + GtkWidget *overlay; + GtkWidget *entry; + GtkWidget *label; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (do_widget)); + gtk_window_set_default_size (GTK_WINDOW (window), + 450, 450); + + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + + gtk_window_set_title (GTK_WINDOW (window), "Overlay"); + gtk_container_set_border_width (GTK_CONTAINER (window), 0); + + view = gtk_text_view_new (); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (sw), view); + + overlay = gtk_overlay_new (); + gtk_container_add (GTK_CONTAINER (overlay), sw); + gtk_container_add (GTK_CONTAINER (window), overlay); + + entry = gtk_entry_new (); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + gtk_widget_set_halign (entry, GTK_ALIGN_END); + gtk_widget_set_valign (entry, GTK_ALIGN_END); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_END); + gtk_widget_set_valign (label, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + gtk_widget_set_margin_left (label, 20); + gtk_widget_set_margin_right (label, 20); + gtk_widget_set_margin_top (label, 5); + gtk_widget_set_margin_bottom (label, 5); + + entry = gtk_entry_new (); + gtk_widget_set_halign (entry, GTK_ALIGN_START); + gtk_widget_set_valign (entry, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_START); + gtk_widget_set_valign (label, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + gtk_widget_set_margin_left (label, 20); + gtk_widget_set_margin_right (label, 20); + gtk_widget_set_margin_top (label, 5); + gtk_widget_set_margin_bottom (label, 5); + + entry = gtk_entry_new (); + gtk_widget_set_halign (entry, GTK_ALIGN_END); + gtk_widget_set_valign (entry, GTK_ALIGN_START); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_END); + gtk_widget_set_valign (label, GTK_ALIGN_START); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + gtk_widget_set_margin_left (label, 20); + gtk_widget_set_margin_right (label, 20); + gtk_widget_set_margin_top (label, 5); + gtk_widget_set_margin_bottom (label, 5); + + entry = gtk_entry_new (); + gtk_widget_set_halign (entry, GTK_ALIGN_START); + gtk_widget_set_valign (entry, GTK_ALIGN_START); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_START); + gtk_widget_set_valign (label, GTK_ALIGN_START); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + gtk_widget_set_margin_left (label, 20); + gtk_widget_set_margin_right (label, 20); + gtk_widget_set_margin_top (label, 5); + gtk_widget_set_margin_bottom (label, 5); + + entry = gtk_entry_new (); + gtk_widget_set_halign (entry, GTK_ALIGN_END); + gtk_widget_set_valign (entry, GTK_ALIGN_CENTER); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_END); + gtk_widget_set_valign (label, GTK_ALIGN_CENTER); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + gtk_widget_set_margin_left (label, 20); + gtk_widget_set_margin_right (label, 20); + gtk_widget_set_margin_top (label, 5); + gtk_widget_set_margin_bottom (label, 5); + + entry = gtk_entry_new (); + gtk_widget_set_halign (entry, GTK_ALIGN_START); + gtk_widget_set_valign (entry, GTK_ALIGN_CENTER); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_START); + gtk_widget_set_valign (label, GTK_ALIGN_CENTER); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + gtk_widget_set_margin_left (label, 20); + gtk_widget_set_margin_right (label, 20); + gtk_widget_set_margin_top (label, 5); + gtk_widget_set_margin_bottom (label, 5); + + entry = gtk_entry_new (); + gtk_widget_set_halign (entry, GTK_ALIGN_CENTER); + gtk_widget_set_valign (entry, GTK_ALIGN_START); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_CENTER); + gtk_widget_set_valign (label, GTK_ALIGN_START); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + gtk_widget_set_margin_left (label, 20); + gtk_widget_set_margin_right (label, 20); + gtk_widget_set_margin_top (label, 5); + gtk_widget_set_margin_bottom (label, 5); + + entry = gtk_entry_new (); + gtk_widget_set_halign (entry, GTK_ALIGN_CENTER); + gtk_widget_set_valign (entry, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_CENTER); + gtk_widget_set_valign (label, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + gtk_widget_set_margin_left (label, 10); + gtk_widget_set_margin_right (label, 10); + gtk_widget_set_margin_top (label, 5); + gtk_widget_set_margin_bottom (label, 5); + + entry = gtk_entry_new (); + gtk_widget_set_halign (entry, GTK_ALIGN_CENTER); + gtk_widget_set_valign (entry, GTK_ALIGN_CENTER); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), entry); + + label = gtk_label_new ("Hello world"); + gtk_widget_set_halign (label, GTK_ALIGN_CENTER); + gtk_widget_set_valign (label, GTK_ALIGN_CENTER); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), label); + + gtk_widget_show_all (overlay); + } + + if (!gtk_widget_get_visible (window)) + { + gtk_widget_show (window); + } + else + { + gtk_widget_destroy (window); + window = NULL; + } + + return window; +} diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml index 0f80b241e4..98e0706bbc 100644 --- a/docs/reference/gtk/gtk-docs.sgml +++ b/docs/reference/gtk/gtk-docs.sgml @@ -242,6 +242,7 @@ <xi:include href="xml/gtknotebook.xml" /> <xi:include href="xml/gtktable.xml" /> <xi:include href="xml/gtkexpander.xml" /> + <xi:include href="xml/gtkoverlay.xml" /> <xi:include href="xml/gtkorientable.xml" /> </chapter> diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index 72c1a6f2aa..f0654c3c16 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -7079,3 +7079,29 @@ GTK_LOCK_BUTTON_GET_CLASS gtk_lock_button_get_type GtkLockButtonPrivate </SECTION> + +<SECTION> +<FILE>gtkoverlay</FILE> +<TITLE>GtkOverlay</TITLE> +GtkOverlay + +gtk_overlay_new +gtk_overlay_set_relative_widget +gtk_overlay_get_relative_widget +gtk_overlay_add +gtk_overlay_set_offset +gtk_overlay_get_offset + +<SUBSECTION Standard> +GTK_TYPE_OVERLAY +GTK_OVERLAY +GTK_OVERLAY_CLASS +GTK_IS_OVERLAY +GTK_IS_OVERLAY_CLASS +GTK_OVERLAY_GET_CLASS +GtkOverlayClass + +<SUBSECTION Private> +gtk_overlay_get_type +GtkOverlayPrivate +</SECTION> diff --git a/docs/reference/gtk/gtk3.types b/docs/reference/gtk/gtk3.types index 2b9cd79a43..d0190b7057 100644 --- a/docs/reference/gtk/gtk3.types +++ b/docs/reference/gtk/gtk3.types @@ -105,6 +105,7 @@ gtk_notebook_get_type gtk_numerable_icon_get_type gtk_offscreen_window_get_type gtk_orientable_get_type +gtk_overlay_get_type gtk_page_setup_get_type gtk_page_setup_unix_dialog_get_type gtk_paned_get_type diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 493a80d2d6..70a35f4fbb 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -256,6 +256,7 @@ gtk_public_h_sources = \ gtknumerableicon.h \ gtkoffscreenwindow.h \ gtkorientable.h \ + gtkoverlay.h \ gtkpagesetup.h \ gtkpaned.h \ gtkpapersize.h \ @@ -592,6 +593,7 @@ gtk_base_c_sources = \ gtknumerableicon.c \ gtkoffscreenwindow.c \ gtkorientable.c \ + gtkoverlay.c \ gtkpagesetup.c \ gtkpaned.c \ gtkpango.c \ @@ -141,6 +141,7 @@ #include <gtk/gtknumerableicon.h> #include <gtk/gtkoffscreenwindow.h> #include <gtk/gtkorientable.h> +#include <gtk/gtkoverlay.h> #include <gtk/gtkpagesetup.h> #include <gtk/gtkpapersize.h> #include <gtk/gtkpaned.h> diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 3063cc7ecb..cde5ce5c5a 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -1625,6 +1625,9 @@ gtk_orientable_get_orientation gtk_orientable_get_type gtk_orientable_set_orientation gtk_orientation_get_type +gtk_overlay_add_overlay +gtk_overlay_get_type +gtk_overlay_new gtk_pack_direction_get_type gtk_pack_type_get_type gtk_page_orientation_get_type diff --git a/gtk/gtkoverlay.c b/gtk/gtkoverlay.c new file mode 100644 index 0000000000..40125babaa --- /dev/null +++ b/gtk/gtkoverlay.c @@ -0,0 +1,635 @@ +/* + * gtkoverlay.c + * This file is part of gtk + * + * Copyright (C) 2011 - Ignacio Casal Quinteiro, Mike Krüger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "gtkoverlay.h" +#include "gtkbuildable.h" +#include "gtkscrolledwindow.h" +#include "gtkmainprivate.h" +#include "gtkmarshalers.h" + +#include "gtkprivate.h" +#include "gtkintl.h" + +/** + * SECTION:gtkoverlay + * @short_description: A container which overlays widgets on top of each other + * @title: GtkOverlay + * + * GtkOverlay is a container which contains a single main child, on top + * of which it can place <firstterm>overlay</firstterm> widgets. The + * position of each overlay widget is determined by its #GtkWidget::halign + * and #GtkWidget::valign properties. E.g. a widget with both alignments + * set to %GTK_ALIGN_START will be placed at the top left corner of the + * main widget, whereas an overlay with halign set to %GTK_ALIGN_CENTER + * and valign set to %GTK_ALIGN_END will be placed a the bottom edge of + * the main widget, horizontally centered. The position can be adjusted + * by setting the margin properties of the child to non-zero values. + * + * More complicated placement of overlays is possible by connecting + * to the #GtkOverlay::get-child-position signal. + * + * <refsect2 id="GtkOverlay-BUILDER-UI"> + * <title>GtkOverlay as GtkBuildable</title> + * <para> + * The GtkOverlay implementation of the GtkBuildable interface + * supports placing a child as an overlay by specifying "overlay" as + * the "type" attribute of a <tag class="starttag">child</tag> element. + * </para> + * </refsect2> + */ + +struct _GtkOverlayPrivate +{ + GSList *children; +}; + +typedef struct _GtkOverlayChild GtkOverlayChild; + +struct _GtkOverlayChild +{ + GtkWidget *widget; + GdkWindow *window; +}; + +enum { + GET_CHILD_POSITION, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void gtk_overlay_buildable_init (GtkBuildableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkOverlay, gtk_overlay, GTK_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_overlay_buildable_init)) + +static GdkWindow * +gtk_overlay_create_child_window (GtkOverlay *overlay, + GtkWidget *child) +{ + GtkWidget *widget = GTK_WIDGET (overlay); + GtkAllocation allocation; + GdkWindow *window; + GdkWindowAttr attributes; + gint attributes_mask; + + gtk_widget_get_allocation (child, &allocation); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.width = allocation.width; + attributes.height = allocation.height; + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes_mask = GDK_WA_X | GDK_WA_Y; + attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; + + window = gdk_window_new (gtk_widget_get_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (window, overlay); + gtk_style_context_set_background (gtk_widget_get_style_context (widget), window); + + gtk_widget_set_parent_window (child, window); + + return window; +} + +static void +gtk_overlay_child_allocate (GtkOverlayChild *child, + GtkAllocation *allocation) +{ + gint left, right, top, bottom; + GtkAllocation child_allocation; + + /* put the margins outside the window; also arrange things + * so that the adjusted child allocation still ends up at 0, 0 + */ + left = gtk_widget_get_margin_left (child->widget); + right = gtk_widget_get_margin_right (child->widget); + top = gtk_widget_get_margin_top (child->widget); + bottom = gtk_widget_get_margin_bottom (child->widget); + + child_allocation.x = - left; + child_allocation.y = - top; + child_allocation.width = allocation->width; + child_allocation.height = allocation->height; + + allocation->x += left; + allocation->y += top; + allocation->width -= left + right; + allocation->height -= top + bottom; + + if (child->window) + gdk_window_move_resize (child->window, + allocation->x, allocation->y, + allocation->width, allocation->height); + + gtk_widget_size_allocate (child->widget, &child_allocation); +} + +static void +gtk_overlay_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkBin *bin = GTK_BIN (widget); + GtkWidget *child; + + if (minimum) + *minimum = 0; + + if (natural) + *natural = 0; + + child = gtk_bin_get_child (bin); + if (child && gtk_widget_get_visible (child)) + gtk_widget_get_preferred_width (child, minimum, natural); +} + +static void +gtk_overlay_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkBin *bin = GTK_BIN (widget); + GtkWidget *child; + + if (minimum) + *minimum = 0; + + if (natural) + *natural = 0; + + child = gtk_bin_get_child (bin); + if (child && gtk_widget_get_visible (child)) + gtk_widget_get_preferred_height (child, minimum, natural); +} + +static void +gtk_overlay_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkOverlay *overlay = GTK_OVERLAY (widget); + GtkOverlayPrivate *priv = overlay->priv; + GSList *children; + GtkWidget *main_widget; + + GTK_WIDGET_CLASS (gtk_overlay_parent_class)->size_allocate (widget, allocation); + + main_widget = gtk_bin_get_child (GTK_BIN (overlay)); + if (!main_widget || !gtk_widget_get_visible (main_widget)) + return; + + gtk_widget_size_allocate (main_widget, allocation); + + for (children = priv->children; children; children = children->next) + { + GtkOverlayChild *child; + GtkAllocation alloc; + gboolean result; + + child = children->data; + + if (gtk_widget_get_mapped (GTK_WIDGET (overlay))) + { + if (gtk_widget_get_visible (child->widget)) + gdk_window_show (child->window); + else if (gdk_window_is_visible (child->window)) + gdk_window_hide (child->window); + } + + if (!gtk_widget_get_visible (child->widget)) + continue; + + g_signal_emit (overlay, signals[GET_CHILD_POSITION], + 0, child->widget, &alloc, &result); + + alloc.x += allocation->x; + alloc.y += allocation->y; + + gtk_overlay_child_allocate (child, &alloc); + } +} + +static GtkAlign +effective_align (GtkAlign align, + GtkTextDirection direction) +{ + switch (align) + { + case GTK_ALIGN_START: + return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START; + case GTK_ALIGN_END: + return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END; + default: + return align; + } +} + +static gboolean +gtk_overlay_get_child_position (GtkOverlay *overlay, + GtkWidget *widget, + GtkAllocation *alloc) +{ + GtkWidget *main_widget; + GtkAllocation main_alloc; + GtkRequisition req; + GtkAlign halign; + GtkTextDirection direction; + + main_widget = gtk_bin_get_child (GTK_BIN (overlay)); + + /* special-case scrolled windows */ + if (GTK_IS_SCROLLED_WINDOW (main_widget)) + { + GtkWidget *grandchild; + gint x, y; + + grandchild = gtk_bin_get_child (GTK_BIN (main_widget)); + gtk_widget_translate_coordinates (grandchild, main_widget, 0, 0, &x, &y); + + main_alloc.x = x; + main_alloc.y = y; + main_alloc.width = gtk_widget_get_allocated_width (grandchild); + main_alloc.height = gtk_widget_get_allocated_height (grandchild); + } + else + { + main_alloc.x = 0; + main_alloc.y = 0; + main_alloc.width = gtk_widget_get_allocated_width (main_widget); + main_alloc.height = gtk_widget_get_allocated_height (main_widget); + } + + gtk_widget_get_preferred_size (widget, NULL, &req); + + alloc->x = main_alloc.x; + alloc->width = MIN (main_alloc.width, req.width); + + direction = gtk_widget_get_direction (widget); + + halign = gtk_widget_get_halign (widget); + switch (effective_align (halign, direction)) + { + case GTK_ALIGN_START: + /* nothing to do */ + break; + case GTK_ALIGN_FILL: + alloc->width = main_alloc.width; + break; + case GTK_ALIGN_CENTER: + alloc->x += main_alloc.width / 2 - req.width / 2; + break; + case GTK_ALIGN_END: + alloc->x += main_alloc.width - req.width; + break; + } + + alloc->y = main_alloc.y; + alloc->height = MIN (main_alloc.height, req.height); + + switch (gtk_widget_get_valign (widget)) + { + case GTK_ALIGN_START: + /* nothing to do */ + break; + case GTK_ALIGN_FILL: + alloc->height = main_alloc.height; + break; + case GTK_ALIGN_CENTER: + alloc->y += main_alloc.height / 2 - req.height / 2; + break; + case GTK_ALIGN_END: + alloc->y += main_alloc.height - req.height; + break; + } + + return TRUE; +} + +static void +gtk_overlay_realize (GtkWidget *widget) +{ + GtkOverlay *overlay = GTK_OVERLAY (widget); + GtkOverlayPrivate *priv = overlay->priv; + GtkOverlayChild *child; + GSList *children; + + GTK_WIDGET_CLASS (gtk_overlay_parent_class)->realize (widget); + + for (children = priv->children; children; children = children->next) + { + child = children->data; + + if (child->window == NULL) + child->window = gtk_overlay_create_child_window (overlay, child->widget); + } +} + +static void +gtk_overlay_unrealize (GtkWidget *widget) +{ + GtkOverlay *overlay = GTK_OVERLAY (widget); + GtkOverlayPrivate *priv = overlay->priv; + GtkOverlayChild *child; + GSList *children; + + for (children = priv->children; children; children = children->next) + { + child = children->data; + + gtk_widget_set_parent_window (child->widget, NULL); + gdk_window_set_user_data (child->window, NULL); + gdk_window_destroy (child->window); + child->window = NULL; + } + + GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unrealize (widget); +} + +static void +gtk_overlay_map (GtkWidget *widget) +{ + GtkOverlay *overlay = GTK_OVERLAY (widget); + GtkOverlayPrivate *priv = overlay->priv; + GtkOverlayChild *child; + GSList *children; + + GTK_WIDGET_CLASS (gtk_overlay_parent_class)->map (widget); + + for (children = priv->children; children; children = children->next) + { + child = children->data; + + if (child->window != NULL && + gtk_widget_get_visible (child->widget) && + gtk_widget_get_child_visible (child->widget)) + gdk_window_show (child->window); + } +} + +static void +gtk_overlay_unmap (GtkWidget *widget) +{ + GtkOverlay *overlay = GTK_OVERLAY (widget); + GtkOverlayPrivate *priv = overlay->priv; + GtkOverlayChild *child; + GSList *children; + + for (children = priv->children; children; children = children->next) + { + child = children->data; + + if (child->window != NULL && + gdk_window_is_visible (child->window)) + gdk_window_hide (child->window); + } + + GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unmap (widget); +} + +static gboolean +gtk_overlay_draw (GtkWidget *widget, + cairo_t *cr) +{ + GtkOverlay *overlay = GTK_OVERLAY (widget); + GtkOverlayPrivate *priv = overlay->priv; + GtkOverlayChild *child; + GSList *children; + + for (children = priv->children; children; children = children->next) + { + child = children->data; + + if (gtk_cairo_should_draw_window (cr, child->window)) + { + cairo_save (cr); + gtk_cairo_transform_to_window (cr, widget, child->window); + gtk_render_background (gtk_widget_get_style_context (widget), + cr, + 0, 0, + gdk_window_get_width (child->window), + gdk_window_get_height (child->window)); + cairo_restore (cr); + } + } + + GTK_WIDGET_CLASS (gtk_overlay_parent_class)->draw (widget, cr); + + return FALSE; +} + +static void +gtk_overlay_remove (GtkContainer *container, + GtkWidget *widget) +{ + GtkOverlayPrivate *priv = GTK_OVERLAY (container)->priv; + GtkOverlayChild *child; + GSList *children; + + for (children = priv->children; children; children = children->next) + { + child = children->data; + + if (child->widget == widget) + { + if (child->window != NULL) + { + gdk_window_set_user_data (child->window, NULL); + gdk_window_destroy (child->window); + } + + gtk_widget_unparent (widget); + + priv->children = g_slist_delete_link (priv->children, children); + g_slice_free (GtkOverlayChild, child); + + return; + } + } + + GTK_CONTAINER_CLASS (gtk_overlay_parent_class)->remove (container, widget); +} + +static void +gtk_overlay_forall (GtkContainer *overlay, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv; + GtkOverlayChild *child; + GSList *children; + GtkWidget *main_widget; + + main_widget = gtk_bin_get_child (GTK_BIN (overlay)); + if (main_widget) + (* callback) (main_widget, callback_data); + + children = priv->children; + while (children) + { + child = children->data; + children = children->next; + + (* callback) (child->widget, callback_data); + } +} + +static void +gtk_overlay_class_init (GtkOverlayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + widget_class->get_preferred_width = gtk_overlay_get_preferred_width; + widget_class->get_preferred_height = gtk_overlay_get_preferred_height; + widget_class->size_allocate = gtk_overlay_size_allocate; + widget_class->realize = gtk_overlay_realize; + widget_class->unrealize = gtk_overlay_unrealize; + widget_class->map = gtk_overlay_map; + widget_class->unmap = gtk_overlay_unmap; + widget_class->draw = gtk_overlay_draw; + + container_class->remove = gtk_overlay_remove; + container_class->forall = gtk_overlay_forall; + + klass->get_child_position = gtk_overlay_get_child_position; + + /** + * GtkOverlay::get-child-position: + * @overlay: the #GtkOverlay + * @widget: the child widget to position + * @allocation: (out): return location for the allocation + * + * The ::get-child-position signal is emitted to determine + * the position and size of any overlay child widgets. A + * handler for this signal should fill @allocation with + * the desired position and size for @widget, relative to + * the 'main' child of @overlay. + * + * The default handler for this signal uses the @widget's + * halign and valign properties to determine the position + * and gives the widget its natural size (except that an + * alignment of %GTK_ALIGN_FILL will cause the overlay to + * be full-width/height). If the main child is a + * #GtkScrolledWindow, the overlays are placed relative + * to its contents. + * + * Return: %TRUE if the @allocation has been filled + */ + signals[GET_CHILD_POSITION] = + g_signal_new (I_("get-child-position"), + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkOverlayClass, get_child_position), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__OBJECT_BOXED, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_WIDGET, + GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE); + + g_type_class_add_private (object_class, sizeof (GtkOverlayPrivate)); +} + +static void +gtk_overlay_init (GtkOverlay *overlay) +{ + overlay->priv = G_TYPE_INSTANCE_GET_PRIVATE (overlay, GTK_TYPE_OVERLAY, GtkOverlayPrivate); + + gtk_widget_set_has_window (GTK_WIDGET (overlay), FALSE); +} + +static void +gtk_overlay_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + if (type && strcmp (type, "overlay") == 0) + gtk_overlay_add_overlay (GTK_OVERLAY (buildable), GTK_WIDGET (child)); + else if (!type) + gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); + else + GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type); +} + +static void +gtk_overlay_buildable_init (GtkBuildableIface *iface) +{ + iface->add_child = gtk_overlay_buildable_add_child; +} + +/** + * gtk_overlay_new: + * + * Creates a new #GtkOverlay. + * + * Returns: a new #GtkOverlay object. + * + * Since: 3.2 + */ +GtkWidget * +gtk_overlay_new (void) +{ + return g_object_new (GTK_TYPE_OVERLAY, NULL); +} + +/** + * gtk_overlay_add_overlay: + * @overlay: a #GtkOverlay + * @widget: a #GtkWidget to be added to the container + * + * Adds @widget to @overlay. + * + * The widget will be stacked on top of the main widget + * added with gtk_container_add(). + * + * The position at which @widget is placed is determined + * from its #GtkWidget::halign and #GtkWidget::valign properties. + * + * Since: 3.2 + */ +void +gtk_overlay_add_overlay (GtkOverlay *overlay, + GtkWidget *widget) +{ + GtkOverlayPrivate *priv = overlay->priv; + GtkOverlayChild *child; + + g_return_if_fail (GTK_IS_OVERLAY (overlay)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + child = g_slice_new0 (GtkOverlayChild); + child->widget = widget; + + gtk_widget_set_parent (widget, GTK_WIDGET (overlay)); + + priv->children = g_slist_append (priv->children, child); + + if (gtk_widget_get_realized (GTK_WIDGET (overlay))) + child->window = gtk_overlay_create_child_window (overlay, widget); +} diff --git a/gtk/gtkoverlay.h b/gtk/gtkoverlay.h new file mode 100644 index 0000000000..baa0e57bcf --- /dev/null +++ b/gtk/gtkoverlay.h @@ -0,0 +1,79 @@ +/* + * gtkoverlay.h + * This file is part of gtk + * + * Copyright (C) 2011 - Ignacio Casal Quinteiro + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#ifndef __GTK_OVERLAY_H__ +#define __GTK_OVERLAY_H__ + +#include <gtk/gtkbin.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_OVERLAY (gtk_overlay_get_type ()) +#define GTK_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_OVERLAY, GtkOverlay)) +#define GTK_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_OVERLAY, GtkOverlayClass)) +#define GTK_IS_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_OVERLAY)) +#define GTK_IS_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_OVERLAY)) +#define GTK_OVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_OVERLAY, GtkOverlayClass)) + +typedef struct _GtkOverlay GtkOverlay; +typedef struct _GtkOverlayClass GtkOverlayClass; +typedef struct _GtkOverlayPrivate GtkOverlayPrivate; + +struct _GtkOverlay +{ + GtkBin parent; + + GtkOverlayPrivate *priv; +}; + +struct _GtkOverlayClass +{ + GtkBinClass parent_class; + + gboolean (*get_child_position) (GtkOverlay *overlay, + GtkWidget *widget, + GtkAllocation *allocation); + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); + void (*_gtk_reserved5) (void); + void (*_gtk_reserved6) (void); + void (*_gtk_reserved7) (void); + void (*_gtk_reserved8) (void); +}; + +GType gtk_overlay_get_type (void) G_GNUC_CONST; + +GtkWidget *gtk_overlay_new (void); +void gtk_overlay_add_overlay (GtkOverlay *overlay, + GtkWidget *widget); + +G_END_DECLS + +#endif /* __GTK_OVERLAY_H__ */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 3cbf6fab85..74d22649b2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -71,6 +71,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \ testoffscreen \ testoffscreenwindow \ testorientable \ + testoverlay \ testprint \ testrecentchooser \ testrecentchoosermenu \ @@ -177,6 +178,7 @@ testoffscreenwindow_DEPENDENCIES = $(TEST_DEPS) testappchooser_DEPENDENCIES = $(TEST_DEPS) testappchooserbutton_DEPENDENCIES = $(TEST_DEPS) testorientable_DEPENDENCIES = $(TEST_DEPS) +testoverlay_DEPENDENCIES = $(TEST_DEPS) testprint_DEPENDENCIES = $(TEST_DEPS) testrecentchooser_DEPENDENCIES = $(TEST_DEPS) testrecentchoosermenu_DEPENDENCIES = $(TEST_DEPS) @@ -266,6 +268,7 @@ testoffscreenwindow_LDADD = $(LDADDS) testappchooser_LDADD = $(LDADDS) testappchooserbutton_LDADD = $(LDADDS) testorientable_LDADD = $(LDADDS) +testoverlay_LDADD = $(LDADDS) testprint_LDADD = $(LDADDS) testrecentchooser_LDADD = $(LDADDS) testrecentchoosermenu_LDADD = $(LDADDS) @@ -441,6 +444,9 @@ testoffscreen_SOURCES = \ testoffscreenwindow_SOURCES = \ testoffscreenwindow.c +testoverlay_SOURCES = \ + testoverlay.c + testappchooser_SOURCES = \ testappchooser.c diff --git a/tests/testoverlay.c b/tests/testoverlay.c new file mode 100644 index 0000000000..d59032c33a --- /dev/null +++ b/tests/testoverlay.c @@ -0,0 +1,447 @@ +#include <string.h> +#include <gtk/gtk.h> + +/* test that margins and non-zero allocation x/y + * of the main widget are handled correctly + */ +static GtkWidget * +test_nonzerox (void) +{ + GtkWidget *win; + GtkWidget *grid; + GtkWidget *overlay; + GtkWidget *text; + GtkWidget *child; + GdkRGBA color; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (win), "Non-zero X"); + + grid = gtk_grid_new (); + g_object_set (grid, "margin", 5, NULL); + gtk_container_add (GTK_CONTAINER (win), grid); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Above"), 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Below"), 1, 2, 1, 1); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Left"), 0, 1, 1, 1); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Right"), 2, 1, 1, 1); + + overlay = gtk_overlay_new (); + gdk_rgba_parse (&color, "red"); + gtk_widget_override_background_color (overlay, 0, &color); + gtk_grid_attach (GTK_GRID (grid), overlay, 1, 1, 1, 1); + + text = gtk_text_view_new (); + gtk_widget_set_size_request (text, 200, 200); + gtk_widget_set_hexpand (text, TRUE); + gtk_widget_set_vexpand (text, TRUE); + gtk_container_add (GTK_CONTAINER (overlay), text); + + child = gtk_label_new ("I'm the overlay"); + gtk_widget_set_halign (child, GTK_ALIGN_START); + gtk_widget_set_valign (child, GTK_ALIGN_START); + g_object_set (child, "margin", 3, NULL); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child); + + child = gtk_label_new ("No, I'm the overlay"); + gtk_widget_set_halign (child, GTK_ALIGN_END); + gtk_widget_set_valign (child, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child); + g_object_set (child, "margin", 3, NULL); + + return win; +} + +static gboolean +get_child_position (GtkOverlay *overlay, + GtkWidget *widget, + GtkAllocation *alloc, + GtkWidget *relative) +{ + GtkRequisition req; + GtkWidget *child; + GtkAllocation main_alloc; + gint x, y; + + child = gtk_bin_get_child (GTK_BIN (overlay)); + + gtk_widget_translate_coordinates (relative, child, 0, 0, &x, &y); + main_alloc.x = x; + main_alloc.y = y; + main_alloc.width = gtk_widget_get_allocated_width (relative); + main_alloc.height = gtk_widget_get_allocated_height (relative); + + gtk_widget_get_preferred_size (widget, NULL, &req); + + alloc->x = main_alloc.x; + alloc->width = MIN (main_alloc.width, req.width); + if (gtk_widget_get_halign (widget) == GTK_ALIGN_END) + alloc->x += main_alloc.width - req.width; + + alloc->y = main_alloc.y; + alloc->height = MIN (main_alloc.height, req.height); + if (gtk_widget_get_valign (widget) == GTK_ALIGN_END) + alloc->y += main_alloc.height - req.height; + + return TRUE; +} + +/* test custom positioning */ +static GtkWidget * +test_relative (void) +{ + GtkWidget *win; + GtkWidget *grid; + GtkWidget *overlay; + GtkWidget *text; + GtkWidget *child; + GdkRGBA color; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (win), "Custom positioning"); + + overlay = gtk_overlay_new (); + gdk_rgba_parse (&color, "yellow"); + gtk_widget_override_background_color (overlay, 0, &color); + gtk_container_add (GTK_CONTAINER (win), overlay); + + grid = gtk_grid_new (); + gtk_container_add (GTK_CONTAINER (overlay), grid); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Above"), 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Below"), 1, 2, 1, 1); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Left"), 0, 1, 1, 1); + gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Right"), 2, 1, 1, 1); + + text = gtk_text_view_new (); + gtk_widget_set_size_request (text, 200, 200); + g_object_set (text, "margin", 5, NULL); + gtk_widget_set_hexpand (text, TRUE); + gtk_widget_set_vexpand (text, TRUE); + gtk_grid_attach (GTK_GRID (grid), text, 1, 1, 1, 1); + g_signal_connect (overlay, "get-child-position", + G_CALLBACK (get_child_position), text); + + child = gtk_label_new ("Top left overlay"); + gtk_widget_set_halign (child, GTK_ALIGN_START); + gtk_widget_set_valign (child, GTK_ALIGN_START); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child); + g_object_set (child, "margin", 1, NULL); + + child = gtk_label_new ("Bottom right overlay"); + gtk_widget_set_halign (child, GTK_ALIGN_END); + gtk_widget_set_valign (child, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child); + g_object_set (child, "margin", 1, NULL); + + return win; +} + +/* test GTK_ALIGN_FILL handling */ +static GtkWidget * +test_fullwidth (void) +{ + GtkWidget *win; + GtkWidget *overlay; + GtkWidget *text; + GtkWidget *child; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (win), "Full-width"); + + overlay = gtk_overlay_new (); + gtk_container_add (GTK_CONTAINER (win), overlay); + + text = gtk_text_view_new (); + gtk_widget_set_size_request (text, 200, 200); + gtk_widget_set_hexpand (text, TRUE); + gtk_widget_set_vexpand (text, TRUE); + gtk_container_add (GTK_CONTAINER (overlay), text); + + child = gtk_label_new ("Fullwidth top overlay"); + gtk_widget_set_halign (child, GTK_ALIGN_FILL); + gtk_widget_set_valign (child, GTK_ALIGN_START); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child); + g_object_set (child, "margin", 4, NULL); + + return win; +} + +/* test that scrolling works as expected */ +static GtkWidget * +test_scrolling (void) +{ + GtkWidget *win; + GtkWidget *overlay; + GtkWidget *sw; + GtkWidget *text; + GtkWidget *child; + GtkTextBuffer *buffer; + gchar *contents; + gsize len; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (win), "Scrolling"); + + overlay = gtk_overlay_new (); + gtk_container_add (GTK_CONTAINER (win), overlay); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_min_content_width (GTK_SCROLLED_WINDOW (sw), 200); + gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (sw), 200); + gtk_container_add (GTK_CONTAINER (overlay), sw); + + text = gtk_text_view_new (); + buffer = gtk_text_buffer_new (NULL); + if (!g_file_get_contents ("testoverlay.c", &contents, &len, NULL)) + { + contents = g_strdup ("Text should go here..."); + len = strlen (contents); + } + gtk_text_buffer_set_text (buffer, contents, len); + g_free (contents); + gtk_text_view_set_buffer (GTK_TEXT_VIEW (text), buffer); + + gtk_widget_set_hexpand (text, TRUE); + gtk_widget_set_vexpand (text, TRUE); + gtk_container_add (GTK_CONTAINER (sw), text); + + child = gtk_label_new ("This should be visible"); + gtk_widget_set_halign (child, GTK_ALIGN_CENTER); + gtk_widget_set_valign (child, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child); + g_object_set (child, "margin", 4, NULL); + + return win; +} + +static const gchar *buffer = +"<interface>" +" <object class='GtkWindow' id='window'>" +" <property name='title'>GtkBuilder support</property>" +" <child>" +" <object class='GtkOverlay' id='overlay'>" +" <child type='overlay'>" +" <object class='GtkLabel' id='overlay-child'>" +" <property name='label'>Witty remark goes here</property>" +" <property name='halign'>end</property>" +" <property name='valign'>end</property>" +" <property name='margin'>4</property>" +" </object>" +" </child>" +" <child>" +" <object class='GtkGrid' id='grid'>" +" <child>" +" <object class='GtkLabel' id='left'>" +" <property name='label'>Left</property>" +" </object>" +" <packing>" +" <property name='left_attach'>0</property>" +" <property name='top_attach'>0</property>" +" </packing>" +" </child>" +" <child>" +" <object class='GtkLabel' id='right'>" +" <property name='label'>Right</property>" +" </object>" +" <packing>" +" <property name='left_attach'>2</property>" +" <property name='top_attach'>0</property>" +" </packing>" +" </child>" +" <child>" +" <object class='GtkTextView' id='text'>" +" <property name='width-request'>200</property>" +" <property name='height-request'>200</property>" +" <property name='hexpand'>True</property>" +" <property name='vexpand'>True</property>" +" </object>" +" <packing>" +" <property name='left_attach'>1</property>" +" <property name='top_attach'>0</property>" +" </packing>" +" </child>" +" </object>" +" </child>" +" </object>" +" </child>" +" </object>" +"</interface>"; + +/* test that overlays can be constructed with GtkBuilder */ +static GtkWidget * +test_builder (void) +{ + GtkBuilder *builder; + GtkWidget *win; + GError *error; + + builder = gtk_builder_new (); + + error = NULL; + if (!gtk_builder_add_from_string (builder, buffer, -1, &error)) + { + g_warning ("%s", error->message); + g_error_free (error); + return NULL; + } + + win = (GtkWidget *)gtk_builder_get_object (builder, "window"); + g_object_ref (win); + + g_object_unref (builder); + + return win; +} + +static void +on_enter (GtkWidget *overlay, GdkEventCrossing *event, GtkWidget *child) +{ + if (event->window != gtk_widget_get_window (child)) + return; + + if (gtk_widget_get_halign (child) == GTK_ALIGN_START) + gtk_widget_set_halign (child, GTK_ALIGN_END); + else + gtk_widget_set_halign (child, GTK_ALIGN_START); + + gtk_widget_queue_resize (overlay); +} + +static GtkWidget * +test_chase (void) +{ + GtkWidget *win; + GtkWidget *overlay; + GtkWidget *sw; + GtkWidget *text; + GtkWidget *child; + GtkTextBuffer *buffer; + gchar *contents; + gsize len; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (win), "Chase"); + + overlay = gtk_overlay_new (); + gtk_widget_set_events (overlay, GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK); + gtk_container_add (GTK_CONTAINER (win), overlay); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_min_content_width (GTK_SCROLLED_WINDOW (sw), 200); + gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (sw), 200); + gtk_container_add (GTK_CONTAINER (overlay), sw); + + text = gtk_text_view_new (); + buffer = gtk_text_buffer_new (NULL); + if (!g_file_get_contents ("testoverlay.c", &contents, &len, NULL)) + { + contents = g_strdup ("Text should go here..."); + len = strlen (contents); + } + gtk_text_buffer_set_text (buffer, contents, len); + g_free (contents); + gtk_text_view_set_buffer (GTK_TEXT_VIEW (text), buffer); + + gtk_widget_set_hexpand (text, TRUE); + gtk_widget_set_vexpand (text, TRUE); + gtk_container_add (GTK_CONTAINER (sw), text); + + child = gtk_label_new ("Try to enter"); + gtk_widget_set_halign (child, GTK_ALIGN_START); + gtk_widget_set_valign (child, GTK_ALIGN_END); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child); + g_object_set (child, "margin", 4, NULL); + + g_signal_connect (overlay, "enter-notify-event", + G_CALLBACK (on_enter), child); + return win; +} + +static GtkWidget * +test_stacking (void) +{ + GtkWidget *win; + GtkWidget *overlay; + GtkWidget *main_child; + GtkWidget *label; + GtkWidget *child; + GtkWidget *grid; + GtkWidget *check1; + GtkWidget *check2; + GdkRGBA color; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (win), "Stacking"); + + grid = gtk_grid_new (); + overlay = gtk_overlay_new (); + main_child = gtk_event_box_new (); + gdk_rgba_parse (&color, "green"); + gtk_widget_override_background_color (main_child, 0, &color); + gtk_widget_set_hexpand (main_child, TRUE); + gtk_widget_set_vexpand (main_child, TRUE); + label = gtk_label_new ("Main child"); + child = gtk_label_new ("Overlay"); + gtk_widget_set_halign (child, GTK_ALIGN_END); + gtk_widget_set_valign (child, GTK_ALIGN_END); + + check1 = gtk_check_button_new_with_label ("Show main"); + g_object_bind_property (main_child, "visible", check1, "active", G_BINDING_BIDIRECTIONAL); + + check2 = gtk_check_button_new_with_label ("Show overlay"); + g_object_bind_property (child, "visible", check2, "active", G_BINDING_BIDIRECTIONAL); + gtk_container_add (GTK_CONTAINER (main_child), label); + gtk_container_add (GTK_CONTAINER (overlay), main_child); + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child); + gtk_grid_attach (GTK_GRID (grid), overlay, 1, 0, 1, 3); + gtk_container_add (GTK_CONTAINER (win), grid); + + gtk_grid_attach (GTK_GRID (grid), check1, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), check2, 0, 1, 1, 1); + child = gtk_label_new (""); + gtk_widget_set_vexpand (child, TRUE); + gtk_grid_attach (GTK_GRID (grid), child, 0, 2, 1, 1); + + return win; +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *win1; + GtkWidget *win2; + GtkWidget *win3; + GtkWidget *win4; + GtkWidget *win5; + GtkWidget *win6; + GtkWidget *win7; + + gtk_init (&argc, &argv); + + if (g_getenv ("RTL")) + gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL); + + win1 = test_nonzerox (); + gtk_widget_show_all (win1); + + win2 = test_relative (); + gtk_widget_show_all (win2); + + win3 = test_fullwidth (); + gtk_widget_show_all (win3); + + win4 = test_scrolling (); + gtk_widget_show_all (win4); + + win5 = test_builder (); + gtk_widget_show_all (win5); + + win6 = test_chase (); + gtk_widget_show_all (win6); + + win7 = test_stacking (); + gtk_widget_show_all (win7); + + gtk_main (); + + return 0; +} |