[gtk+/overlay] overlay: add a signal for custom positioning



commit ddf813128f2ccc63e7c33acc9097ce68c0c65cbe
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Jun 11 23:17:55 2011 -0400

    overlay: add a signal for custom positioning
    
    This is a replacement for the earlier relative-widget functionality.

 gtk/gtkoverlay.c    |  231 ++++++++++++++++++++++++++++++++------------------
 gtk/gtkoverlay.h    |    4 +
 tests/testoverlay.c |   52 +++++++++---
 3 files changed, 192 insertions(+), 95 deletions(-)
---
diff --git a/gtk/gtkoverlay.c b/gtk/gtkoverlay.c
index 5d89da4..90d8a6e 100644
--- a/gtk/gtkoverlay.c
+++ b/gtk/gtkoverlay.c
@@ -25,6 +25,8 @@
 #include "gtkoverlay.h"
 #include "gtkbuildable.h"
 #include "gtkscrolledwindow.h"
+#include "gtkmainprivate.h"
+#include "gtkmarshalers.h"
 
 #include "gtkprivate.h"
 #include "gtkintl.h"
@@ -67,6 +69,13 @@ struct _GtkOverlayChild
   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,
@@ -137,21 +146,6 @@ gtk_overlay_child_allocate (GtkOverlayChild *child,
   gtk_widget_size_allocate (child->widget, &child_allocation);
 }
 
-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 void
 gtk_overlay_get_preferred_width (GtkWidget *widget,
                                  gint      *minimum,
@@ -196,46 +190,22 @@ gtk_overlay_size_allocate (GtkWidget     *widget,
 {
   GtkOverlay *overlay = GTK_OVERLAY (widget);
   GtkOverlayPrivate *priv = overlay->priv;
-  GtkOverlayChild *child;
-  GtkAllocation main_alloc;
   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 == NULL)
+  if (!main_widget || !gtk_widget_get_visible (main_widget))
     return;
 
   gtk_widget_size_allocate (main_widget, allocation);
 
-  /* 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 = allocation->x + x;
-      main_alloc.y = allocation->y + y;
-      main_alloc.width = gtk_widget_get_allocated_width (grandchild);
-      main_alloc.height = gtk_widget_get_allocated_height (grandchild);
-    }
-  else
-    {
-      main_alloc.x = allocation->x;
-      main_alloc.y = allocation->y;
-      main_alloc.width = allocation->width;
-      main_alloc.height = allocation->height;
-    }
-
   for (children = priv->children; children; children = children->next)
     {
-      GtkRequisition req;
+      GtkOverlayChild *child;
       GtkAllocation alloc;
-      GtkAlign halign;
-      GtkTextDirection direction;
+      gboolean result;
 
       child = children->data;
 
@@ -250,51 +220,110 @@ gtk_overlay_size_allocate (GtkWidget     *widget,
       if (!gtk_widget_get_visible (child->widget))
         continue;
 
-      gtk_widget_get_preferred_size (child->widget, NULL, &req);
+      g_signal_emit (overlay, signals[GET_CHILD_POSITION],
+                     0, child->widget, &alloc, &result);
 
-      alloc.x = main_alloc.x;
-      alloc.width = MIN (main_alloc.width, req.width);
+      alloc.x += allocation->x;
+      alloc.y += allocation->y;
 
-      direction = gtk_widget_get_direction (child->widget);
+      gtk_overlay_child_allocate (child, &alloc);
+    }
+}
 
-      halign = gtk_widget_get_halign (child->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;
-        }
+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;
+    }
+}
 
-      alloc.y = main_alloc.y;
-      alloc.height = MIN (main_alloc.height, req.height);
+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;
 
-      switch (gtk_widget_get_valign (child->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;
-        }
+  main_widget = gtk_bin_get_child (GTK_BIN (overlay));
 
-      gtk_overlay_child_allocate (child, &alloc);
+  /* 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
@@ -485,6 +514,41 @@ gtk_overlay_class_init (GtkOverlayClass *klass)
   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));
 }
 
@@ -539,9 +603,10 @@ gtk_overlay_new (void)
  * 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.
+ * 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
  */
diff --git a/gtk/gtkoverlay.h b/gtk/gtkoverlay.h
index 0a9740d..baa0e57 100644
--- a/gtk/gtkoverlay.h
+++ b/gtk/gtkoverlay.h
@@ -53,6 +53,10 @@ 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);
diff --git a/tests/testoverlay.c b/tests/testoverlay.c
index 7a64f33..d59032c 100644
--- a/tests/testoverlay.c
+++ b/tests/testoverlay.c
@@ -51,10 +51,41 @@ test_nonzerox (void)
   return win;
 }
 
-#if 0
-/* test that margins and non-zero allocation x/y
- * of the relative widget are handled correctly
- */
+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)
 {
@@ -66,7 +97,7 @@ test_relative (void)
   GdkRGBA color;
 
   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-  gtk_window_set_title (GTK_WINDOW (win), "Relative positioning");
+  gtk_window_set_title (GTK_WINDOW (win), "Custom positioning");
 
   overlay = gtk_overlay_new ();
   gdk_rgba_parse (&color, "yellow");
@@ -86,23 +117,23 @@ test_relative (void)
   gtk_widget_set_hexpand (text, TRUE);
   gtk_widget_set_vexpand (text, TRUE);
   gtk_grid_attach (GTK_GRID (grid), text, 1, 1, 1, 1);
-  gtk_overlay_set_relative_widget (GTK_OVERLAY (overlay), text);
+  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 (GTK_OVERLAY (overlay), child);
+  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 (GTK_OVERLAY (overlay), child);
+  gtk_overlay_add_overlay (GTK_OVERLAY (overlay), child);
   g_object_set (child, "margin", 1, NULL);
 
   return win;
 }
-#endif
 
 /* test GTK_ALIGN_FILL handling */
 static GtkWidget *
@@ -188,7 +219,6 @@ static const gchar *buffer =
 "    <property name='title'>GtkBuilder support</property>"
 "    <child>"
 "      <object class='GtkOverlay' id='overlay'>"
-"<!--        <property name='relative_widget'>text</property> -->"
 "        <child type='overlay'>"
 "          <object class='GtkLabel' id='overlay-child'>"
 "            <property name='label'>Witty remark goes here</property>"
@@ -393,10 +423,8 @@ main (int argc, char *argv[])
   win1 = test_nonzerox ();
   gtk_widget_show_all (win1);
 
-#if 0
   win2 = test_relative ();
   gtk_widget_show_all (win2);
-#endif
 
   win3 = test_fullwidth ();
   gtk_widget_show_all (win3);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]