[gtk+] Fix handling of the geometry widget



commit 88cf5470290cd03ada031641cb6ee1c90df75e7d
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Sat Oct 9 22:15:34 2010 -0400

    Fix handling of the geometry widget
    
    The geometry widget feature of gtk_window_set_geometry_hints() has
    never really worked right because the calculation that GTK+ did to
    compute the base size of the window only worked when the geometry
    widget had a larger minimum size than anything else in the window.
    
    Setup:
    * Move the GtkSizeGroup private functions to a new private header
      gtksizegroup-private.h
    * Add the possibilty to pass flags to _gtk_size_group_queue_resize(),
      with the flag GTK_QUEUE_RESIZE_INVALIDATE_ONLY to suppress adding
      the widget's toplevel to the resize queue.
    * _gtk_container_resize_invalidate() is added to implement that feature
    * _gtk_widget_override_size_request()/_gtk_widget_restore_size_request()
      allow temporarily forcing a large minimum size on the geometry
      widget without creating resize loops.
    
    GtkWindow:
    * Compute the extra width/height around the geometry widget
      correctly; print a warning if the computation fails.
    * Always make the minimum size at least the natural minimum
      size of the toplevel; GTK+ now fails badly with underallocation.
    * Always set the base size hint; we were failing to set it
      properly when the specified minimum size was overriden, but
      it's harmless to always set it.
    
    Tests:
    * New test 'testgeometry' that replaces the 'gridded geometry' test
      from testgtk. The new test is roughly similar but creates a bunch
      of windows showing different possibilities.
    * The testgtk test is removed. No need to have both.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=68668

 gtk/Makefile.am            |    1 +
 gtk/gtkcontainer.c         |   37 ++++++++-
 gtk/gtkcontainer.h         |    1 +
 gtk/gtkprivate.h           |    9 ++
 gtk/gtksizegroup-private.h |   49 +++++++++++
 gtk/gtksizegroup.c         |   50 +++++++-----
 gtk/gtksizegroup.h         |    9 --
 gtk/gtksizerequest.c       |    2 +-
 gtk/gtkwidget.c            |   85 +++++++++++++++---
 gtk/gtkwindow.c            |   82 +++++++++++++-----
 tests/Makefile.am          |    6 ++
 tests/testgeometry.c       |  203 ++++++++++++++++++++++++++++++++++++++++++++
 tests/testgtk.c            |  168 ------------------------------------
 13 files changed, 463 insertions(+), 239 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index fb040fd..29df0c9 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -382,6 +382,7 @@ gtk_private_h_sources =		\
 	gtkrecentchooserdefault.h \
 	gtkrecentchooserprivate.h \
 	gtkrecentchooserutils.h \
+	gtksizegroup-private.h	\
 	gtksocketprivate.h	\
 	gtktextbtree.h		\
 	gtktextbufferserialize.h\
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index 17473e5..75ac3f7 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -1609,8 +1609,9 @@ gtk_container_idle_sizer (gpointer data)
   return FALSE;
 }
 
-void
-_gtk_container_queue_resize (GtkContainer *container)
+static void
+_gtk_container_queue_resize_internal (GtkContainer *container,
+				      gboolean      invalidate_only)
 {
   GtkContainerPrivate *priv;
   GtkContainer *resize_container;
@@ -1637,7 +1638,7 @@ _gtk_container_queue_resize (GtkContainer *container)
       widget = parent;
     }
       
-  if (resize_container)
+  if (resize_container && !invalidate_only)
     {
       if (gtk_widget_get_visible (GTK_WIDGET (resize_container)) &&
           (gtk_widget_is_toplevel (GTK_WIDGET (resize_container)) ||
@@ -1677,6 +1678,36 @@ _gtk_container_queue_resize (GtkContainer *container)
     }
 }
 
+/**
+ * _gtk_container_queue_resize:
+ * @container: a #GtkContainer
+ *
+ * Determines the "resize container" in the hierarchy above this container
+ * (typically the toplevel, but other containers can be set as resize
+ * containers with gtk_container_set_resize_mode()), marks the container
+ * and all parents up to and including the resize container as needing
+ * to have sizes recompted, and if necessary adds the resize container
+ * to the queue of containers that will be resized out at idle.
+ */
+void
+_gtk_container_queue_resize (GtkContainer *container)
+{
+  _gtk_container_queue_resize_internal (container, FALSE);
+}
+
+/**
+ * _gtk_container_resize_invalidate:
+ * @container: a #GtkContainer
+ *
+ * Invalidates cached sizes like _gtk_container_queue_resize() but doesn't
+ * actually queue the resize container for resize.
+ */
+void
+_gtk_container_resize_invalidate (GtkContainer *container)
+{
+  _gtk_container_queue_resize_internal (container, TRUE);
+}
+
 void
 gtk_container_check_resize (GtkContainer *container)
 {
diff --git a/gtk/gtkcontainer.h b/gtk/gtkcontainer.h
index 607baf6..57a80ae 100644
--- a/gtk/gtkcontainer.h
+++ b/gtk/gtkcontainer.h
@@ -210,6 +210,7 @@ void    gtk_container_class_handle_border_width (GtkContainerClass *klass);
 
 /* Non-public methods */
 void	_gtk_container_queue_resize	     (GtkContainer *container);
+void	_gtk_container_resize_invalidate     (GtkContainer *container);
 void    _gtk_container_clear_resize_widgets   (GtkContainer *container);
 gchar*	_gtk_container_child_composite_name   (GtkContainer *container,
 					      GtkWidget	   *child);
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index c0f14b4..d8fd0c0 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -55,6 +55,15 @@ gboolean     _gtk_widget_get_height_request_needed (GtkWidget *widget);
 void         _gtk_widget_set_height_request_needed (GtkWidget *widget,
                                                     gboolean   height_request_needed);
 
+void _gtk_widget_override_size_request (GtkWidget *widget,
+					int        width,
+					int        height,
+					int       *old_width,
+					int       *old_height);
+void _gtk_widget_restore_size_request  (GtkWidget *widget,
+					int        old_width,
+					int        old_height);
+
 #ifdef G_OS_WIN32
 
 const gchar *_gtk_get_datadir ();
diff --git a/gtk/gtksizegroup-private.h b/gtk/gtksizegroup-private.h
new file mode 100644
index 0000000..3f80d3f
--- /dev/null
+++ b/gtk/gtksizegroup-private.h
@@ -0,0 +1,49 @@
+/* GTK - The GIMP Toolkit
+ * gtksizegroup-private.h:
+ * Copyright (C) 2000-2010 Red Hat Software
+ *
+ * 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.
+ */
+
+#ifndef __GTK_SIZE_GROUP_PRIVATE_H__
+#define __GTK_SIZE_GROUP_PRIVATE_H__
+
+#include <gtk/gtksizegroup.h>
+
+/**
+ * GtkQueueResizeFlags:
+ * @GTK_QUEUE_RESIZE_INVALIDATE_ONLY: invalidate all cached sizes
+ *  as we would normally do when a widget is queued for resize,
+ *  but don't actually add the toplevel resize container to the
+ *  resize queue. Useful if we want to change the size of a widget
+ *  see how that would affect the overall layout, then restore
+ *  the old size.
+ *
+ * Flags that affect the operation of queueing a widget for resize.
+ */
+typedef enum
+{
+  GTK_QUEUE_RESIZE_INVALIDATE_ONLY = 1 << 0
+} GtkQueueResizeFlags;
+
+void _gtk_size_group_bump_requisition (GtkWidget           *widget,
+                                       GtkSizeGroupMode     mode,
+                                       gint                *minimum,
+                                       gint                *natural);
+void _gtk_size_group_queue_resize     (GtkWidget           *widget,
+                                       GtkQueueResizeFlags  flags);
+
+#endif /* __GTK_SIZE_GROUP_PRIVATE_H__ */
diff --git a/gtk/gtksizegroup.c b/gtk/gtksizegroup.c
index 1663f97..62ce9ea 100644
--- a/gtk/gtksizegroup.c
+++ b/gtk/gtksizegroup.c
@@ -23,7 +23,7 @@
 #include "gtkcontainer.h"
 #include "gtkintl.h"
 #include "gtkprivate.h"
-#include "gtksizegroup.h"
+#include "gtksizegroup-private.h"
 #include "gtkbuildable.h"
 
 
@@ -182,19 +182,27 @@ add_widget_to_closure (GtkWidget       *widget,
 }
 
 static void
-real_queue_resize (GtkWidget *widget)
+real_queue_resize (GtkWidget          *widget,
+		   GtkQueueResizeFlags flags)
 {
-  GtkWidget *parent;
+  GtkWidget *container;
 
   _gtk_widget_set_alloc_needed (widget, TRUE);
   _gtk_widget_set_width_request_needed (widget, TRUE);
   _gtk_widget_set_height_request_needed (widget, TRUE);
 
-  parent = gtk_widget_get_parent (widget);
-  if (parent)
-    _gtk_container_queue_resize (GTK_CONTAINER (parent));
-  else if (gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
-    _gtk_container_queue_resize (GTK_CONTAINER (widget));
+  container = gtk_widget_get_parent (widget);
+  if (!container &&
+      gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
+    container = widget;
+
+  if (container)
+    {
+      if (flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY)
+	_gtk_container_resize_invalidate (GTK_CONTAINER (container));
+      else
+	_gtk_container_queue_resize (GTK_CONTAINER (container));
+    }
 }
 
 static void
@@ -214,8 +222,9 @@ reset_group_sizes (GSList *groups)
 }
 
 static void
-queue_resize_on_widget (GtkWidget *widget,
-			gboolean   check_siblings)
+queue_resize_on_widget (GtkWidget          *widget,
+			gboolean            check_siblings,
+			GtkQueueResizeFlags flags)
 {
   GtkWidget *parent = widget;
   GSList *tmp_list;
@@ -228,7 +237,7 @@ queue_resize_on_widget (GtkWidget *widget,
       
       if (widget == parent && !check_siblings)
 	{
-	  real_queue_resize (widget);
+	  real_queue_resize (widget, flags);
           parent = gtk_widget_get_parent (parent);
 	  continue;
 	}
@@ -237,7 +246,7 @@ queue_resize_on_widget (GtkWidget *widget,
       if (!widget_groups)
 	{
 	  if (widget == parent)
-	    real_queue_resize (widget);
+	    real_queue_resize (widget, flags);
 
           parent = gtk_widget_get_parent (parent);
 	  continue;
@@ -258,14 +267,14 @@ queue_resize_on_widget (GtkWidget *widget,
 	  if (tmp_list->data == parent)
 	    {
 	      if (widget == parent)
-		real_queue_resize (parent);
+		real_queue_resize (parent, flags);
 	    }
 	  else if (tmp_list->data == widget)
             {
               g_warning ("A container and its child are part of this SizeGroup");
             }
 	  else
-	    queue_resize_on_widget (tmp_list->data, FALSE);
+	    queue_resize_on_widget (tmp_list->data, FALSE, flags);
 
 	  tmp_list = tmp_list->next;
 	}
@@ -288,14 +297,14 @@ queue_resize_on_widget (GtkWidget *widget,
 	  if (tmp_list->data == parent)
 	    {
 	      if (widget == parent)
-		real_queue_resize (parent);
+		real_queue_resize (parent, flags);
 	    }
 	  else if (tmp_list->data == widget)
             {
               g_warning ("A container and its child are part of this SizeGroup");
             }
 	  else
-	    queue_resize_on_widget (tmp_list->data, FALSE);
+	    queue_resize_on_widget (tmp_list->data, FALSE, flags);
 
 	  tmp_list = tmp_list->next;
 	}
@@ -308,12 +317,12 @@ queue_resize_on_widget (GtkWidget *widget,
 }
 
 static void
-queue_resize_on_group (GtkSizeGroup *size_group)
+queue_resize_on_group (GtkSizeGroup       *size_group)
 {
   GtkSizeGroupPrivate *priv = size_group->priv;
 
   if (priv->widgets)
-    queue_resize_on_widget (priv->widgets->data, TRUE);
+    queue_resize_on_widget (priv->widgets->data, TRUE, 0);
 }
 
 static void
@@ -806,11 +815,12 @@ _gtk_size_group_bump_requisition (GtkWidget        *widget,
  * Queue a resize on a widget, and on all other widgets grouped with this widget.
  **/
 void
-_gtk_size_group_queue_resize (GtkWidget *widget)
+_gtk_size_group_queue_resize (GtkWidget           *widget,
+			      GtkQueueResizeFlags  flags)
 {
   initialize_size_group_quarks ();
 
-  queue_resize_on_widget (widget, TRUE);
+  queue_resize_on_widget (widget, TRUE, flags);
 }
 
 typedef struct {
diff --git a/gtk/gtksizegroup.h b/gtk/gtksizegroup.h
index d778f08..ff1b0b1 100644
--- a/gtk/gtksizegroup.h
+++ b/gtk/gtksizegroup.h
@@ -92,15 +92,6 @@ void             gtk_size_group_remove_widget (GtkSizeGroup     *size_group,
 					       GtkWidget        *widget);
 GSList *         gtk_size_group_get_widgets   (GtkSizeGroup     *size_group);
 
-
-
-void _gtk_size_group_bump_requisition      (GtkWidget        *widget,
-					    GtkSizeGroupMode  mode,
-					    gint             *minimum,
-					    gint             *natural);
-void _gtk_size_group_queue_resize          (GtkWidget        *widget);
-
-
 G_END_DECLS
 
 #endif /* __GTK_SIZE_GROUP_H__ */
diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c
index a30b241..c3d0398 100644
--- a/gtk/gtksizerequest.c
+++ b/gtk/gtksizerequest.c
@@ -23,7 +23,7 @@
 
 #include <config.h>
 #include "gtksizerequest.h"
-#include "gtksizegroup.h"
+#include "gtksizegroup-private.h"
 #include "gtkdebug.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index e3acd78..779a003 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -40,7 +40,7 @@
 #include "gtkrc.h"
 #include "gtkselection.h"
 #include "gtksettings.h"
-#include "gtksizegroup.h"
+#include "gtksizegroup-private.h"
 #include "gtkwidget.h"
 #include "gtkwindow.h"
 #include "gtkbindings.h"
@@ -563,9 +563,10 @@ static void             gtk_widget_real_adjust_size_request     (GtkWidget
 static void             gtk_widget_real_adjust_size_allocation  (GtkWidget         *widget,
                                                                  GtkAllocation     *allocation);
 
-static void gtk_widget_set_usize_internal (GtkWidget *widget,
-					   gint       width,
-					   gint       height);
+static void gtk_widget_set_usize_internal (GtkWidget          *widget,
+					   gint                width,
+					   gint                height,
+					   GtkQueueResizeFlags flags);
 
 static void gtk_widget_add_events_internal (GtkWidget *widget,
                                             GdkDevice *device,
@@ -3022,10 +3023,10 @@ gtk_widget_set_property (GObject         *object,
       gtk_container_add (GTK_CONTAINER (g_value_get_object (value)), widget);
       break;
     case PROP_WIDTH_REQUEST:
-      gtk_widget_set_usize_internal (widget, g_value_get_int (value), -2);
+      gtk_widget_set_usize_internal (widget, g_value_get_int (value), -2, 0);
       break;
     case PROP_HEIGHT_REQUEST:
-      gtk_widget_set_usize_internal (widget, -2, g_value_get_int (value));
+      gtk_widget_set_usize_internal (widget, -2, g_value_get_int (value), 0);
       break;
     case PROP_VISIBLE:
       gtk_widget_set_visible (widget, g_value_get_boolean (value));
@@ -4207,7 +4208,7 @@ gtk_widget_queue_resize (GtkWidget *widget)
   if (gtk_widget_get_realized (widget))
     gtk_widget_queue_shallow_draw (widget);
       
-  _gtk_size_group_queue_resize (widget);
+  _gtk_size_group_queue_resize (widget, 0);
 }
 
 /**
@@ -4224,7 +4225,7 @@ gtk_widget_queue_resize_no_redraw (GtkWidget *widget)
 {
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  _gtk_size_group_queue_resize (widget);
+  _gtk_size_group_queue_resize (widget, 0);
 }
 
 /**
@@ -8643,9 +8644,10 @@ gtk_widget_error_bell (GtkWidget *widget)
 }
 
 static void
-gtk_widget_set_usize_internal (GtkWidget *widget,
-			       gint       width,
-			       gint       height)
+gtk_widget_set_usize_internal (GtkWidget          *widget,
+			       gint                width,
+			       gint                height,
+			       GtkQueueResizeFlags flags)
 {
   GtkWidgetAuxInfo *aux_info;
   gboolean changed = FALSE;
@@ -8656,19 +8658,26 @@ gtk_widget_set_usize_internal (GtkWidget *widget,
   
   if (width > -2 && aux_info->width != width)
     {
-      g_object_notify (G_OBJECT (widget), "width-request");
+      if ((flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY) == 0)
+	g_object_notify (G_OBJECT (widget), "width-request");
       aux_info->width = width;
       changed = TRUE;
     }
   if (height > -2 && aux_info->height != height)
     {
-      g_object_notify (G_OBJECT (widget), "height-request");  
+      if ((flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY) == 0)
+	g_object_notify (G_OBJECT (widget), "height-request");
       aux_info->height = height;
       changed = TRUE;
     }
   
   if (gtk_widget_get_visible (widget) && changed)
-    gtk_widget_queue_resize (widget);
+    {
+      if ((flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY) == 0)
+	gtk_widget_queue_resize (widget);
+      else
+	_gtk_size_group_queue_resize (widget, GTK_QUEUE_RESIZE_INVALIDATE_ONLY);
+    }
 
   g_object_thaw_notify (G_OBJECT (widget));
 }
@@ -8728,7 +8737,7 @@ gtk_widget_set_size_request (GtkWidget *widget,
   if (height == 0)
     height = 1;
   
-  gtk_widget_set_usize_internal (widget, width, height);
+  gtk_widget_set_usize_internal (widget, width, height, 0);
 }
 
 
@@ -8765,6 +8774,52 @@ gtk_widget_get_size_request (GtkWidget *widget,
 }
 
 /**
+ * _gtk_widget_override_size_request:
+ * @widget: a #GtkWidget
+ * @width: new forced minimum width
+ * @height: new forced minimum height
+ * @old_width: location to store previous forced minimum width
+ * @old_width: location to store previous forced minumum height
+ *
+ * Temporarily establishes a forced minimum size for a widget; this
+ * is used by GtkWindow when calculating the size to add to the
+ * window's geometry widget. Cached sizes for the widget and its
+ * parents are invalidated, so that subsequent calls to the size
+ * negotiation machinery produce the overriden result, but the
+ * widget is not queued for relayout or redraw. The old size must
+ * be restored with _gtk_widget_restore_size_request() or things
+ * will go screwy.
+ */
+void
+_gtk_widget_override_size_request (GtkWidget *widget,
+				   int        width,
+				   int        height,
+				   int       *old_width,
+				   int       *old_height)
+{
+  gtk_widget_get_size_request (widget, old_width, old_height);
+  gtk_widget_set_usize_internal (widget, width, height,
+				 GTK_QUEUE_RESIZE_INVALIDATE_ONLY);
+}
+
+/**
+ * _gtk_widget_restore_size_request:
+ * @widget: a #GtkWidget
+ * @old_width: saved forced minimum size
+ * @old_height: saved forced minimum size
+ *
+ * Undoes the operation of_gtk_widget_override_size_request().
+ */
+void
+_gtk_widget_restore_size_request (GtkWidget *widget,
+				  int        old_width,
+				  int        old_height)
+{
+  gtk_widget_set_usize_internal (widget, old_width, old_height,
+				 GTK_QUEUE_RESIZE_INVALIDATE_ONLY);
+}
+
+/**
  * gtk_widget_set_events:
  * @widget: a #GtkWidget
  * @events: event mask
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 4103ef6..571484e 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -47,6 +47,7 @@
 #include "gtkicontheme.h"
 #include "gtkmarshalers.h"
 #include "gtkplug.h"
+#include "gtkprivate.h"
 #include "gtkbuildable.h"
 
 #ifdef GDK_WINDOWING_X11
@@ -7021,28 +7022,52 @@ gtk_window_compute_hints (GtkWindow   *window,
   
   if (geometry_info && geometry_info->widget)
     {
-      GtkRequisition requisition;
-      GtkRequisition child_requisition;
-
-      /* FIXME: This really isn't right. It gets the min size wrong and forces
-       * callers to do horrible hacks like set a huge usize on the child requisition
-       * to get the base size right. We really want to find the answers to:
+      /* If the geometry widget is set, then the hints really apply to that
+       * widget. This is pretty much meaningless unless the window layout
+       * is such that the rest of the window adds fixed size borders to
+       * the geometry widget. Our job is to figure the size of the borders;
+       * We do that by asking how big the toplevel would be if the
+       * geometry widget was *really big*.
        *
-       *  - If the geometry widget was infinitely big, how much extra space
-       *    would be needed for the stuff around it.
+       *  +----------+
+       *  |AAAAAAAAA | At small sizes, the minimum sizes of widgets
+       *  |GGGGG    B| in the border can confuse things
+       *  |GGGGG    B|
+       *  |         B|
+       *  +----------+
        *
-       *  - If the geometry widget was infinitely small, how big would the
-       *    window still have to be.
-       *
-       * Finding these answers would be a bit of a mess here. (Bug #68668)
+       *  +-----------+
+       *  |AAAAAAAAA  | When the geometry widget is large, things are
+       *  |GGGGGGGGGGB| clearer.
+       *  |GGGGGGGGGGB|
+       *  |GGGGGGGGGG |
+       *  +-----------+
        */
-      gtk_widget_get_preferred_size (geometry_info->widget,
-                                     &child_requisition, NULL);
+#define TEMPORARY_SIZE 10000 /* 10,000 pixels should be bigger than real widget sizes */
+      GtkRequisition requisition;
+      int current_width, current_height;
 
+      _gtk_widget_override_size_request (geometry_info->widget,
+					 TEMPORARY_SIZE, TEMPORARY_SIZE,
+					 &current_width, &current_height);
       gtk_widget_get_preferred_size (widget,
                                      &requisition, NULL);
-      extra_width = requisition.width - child_requisition.width;
-      extra_height = requisition.height - child_requisition.height;
+      _gtk_widget_restore_size_request (geometry_info->widget,
+					current_width, current_height);
+
+      extra_width = requisition.width - TEMPORARY_SIZE;
+      extra_height = requisition.height - TEMPORARY_SIZE;
+
+      if (extra_width < 0 || extra_width < 0)
+	{
+	  g_warning("Toplevel size doesn't seem to directly depend on the "
+		    "size of the geometry widget from gtk_window_set_geometry_hints(). "
+		    "The geometry widget might not be in the window, or it might not "
+		    "be packed into the window appropriately");
+	  extra_width = MAX(extra_width, 0);
+	  extra_height = MAX(extra_height, 0);
+	}
+#undef TEMPORARY_SIZE
     }
 
   /* We don't want to set GDK_HINT_POS in here, we just set it
@@ -7055,27 +7080,38 @@ gtk_window_compute_hints (GtkWindow   *window,
       new_geometry->base_width += extra_width;
       new_geometry->base_height += extra_height;
     }
-  else if (!(*new_flags & GDK_HINT_MIN_SIZE) &&
-	   (*new_flags & GDK_HINT_RESIZE_INC) &&
-	   ((extra_width != 0) || (extra_height != 0)))
+  else
     {
+      /* For simplicity, we always set the base hint, even when we
+       * don't expect it to have any visible effect.
+       */
       *new_flags |= GDK_HINT_BASE_SIZE;
-      
+
       new_geometry->base_width = extra_width;
       new_geometry->base_height = extra_height;
+
+      /* As for X, if BASE_SIZE is not set but MIN_SIZE is set, then the
+       * base size is the minimum size */
+      if (*new_flags & GDK_HINT_MIN_SIZE)
+	{
+	  if (new_geometry->min_width > 0)
+	    new_geometry->base_width += new_geometry->min_width;
+	  if (new_geometry->min_height > 0)
+	    new_geometry->base_height += new_geometry->min_height;
+	}
     }
-  
+
   if (*new_flags & GDK_HINT_MIN_SIZE)
     {
       if (new_geometry->min_width < 0)
 	new_geometry->min_width = requisition.width;
       else
-        new_geometry->min_width += extra_width;
+        new_geometry->min_width = MAX (requisition.width, new_geometry->min_width + extra_width);
 
       if (new_geometry->min_height < 0)
 	new_geometry->min_height = requisition.height;
       else
-	new_geometry->min_height += extra_height;
+	new_geometry->min_height = MAX (requisition.height, new_geometry->min_height + extra_height);
     }
   else
     {
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4fa4321..b607ad5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -47,6 +47,7 @@ noinst_PROGRAMS =  $(TEST_PROGS)	\
 	testfilechooser			\
 	testfilechooserbutton		\
 	testframe			\
+	testgeometry			\
 	testgtk				\
 	testheightforwidth		\
 	testiconview			\
@@ -136,6 +137,7 @@ testerrors_DEPENDENCIES = $(TEST_DEPS)
 testfilechooser_DEPENDENCIES = $(TEST_DEPS)
 testfilechooserbutton_DEPENDENCIES = $(TEST_DEPS)
 testframe_DEPENDENCIES = $(TEST_DEPS)
+testgeometry_DEPENDENCIES = $(TEST_DEPS)
 testgtk_DEPENDENCIES = $(TEST_DEPS)
 testinput_DEPENDENCIES = $(TEST_DEPS)
 testimage_DEPENDENCIES = $(TEST_DEPS)
@@ -201,6 +203,7 @@ testerrors_LDADD = $(LDADDS)
 testfilechooser_LDADD = $(LDADDS)
 testfilechooserbutton_LDADD = $(LDADDS)
 testframe_LDADD = $(LDADDS)
+testgeometry_LDADD = $(LDADDS)
 testgtk_LDADD = $(LDADDS)
 testheightforwidth_LDADD = $(LDADDS)
 testicontheme_LDADD = $(LDADDS)
@@ -322,6 +325,9 @@ testbuttons_SOURCES = 		\
 testframe_SOURCES = 		\
 	testframe.c
 
+testgeometry_SOURCES = 		\
+	testgeometry.c
+
 testiconview_SOURCES = 		\
 	testiconview.c		\
 	prop-editor.c
diff --git a/tests/testgeometry.c b/tests/testgeometry.c
new file mode 100644
index 0000000..9d3f714
--- /dev/null
+++ b/tests/testgeometry.c
@@ -0,0 +1,203 @@
+/* testgeometry.c
+ * Author: Owen Taylor <otaylor redhat com>
+ * Copyright © 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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 <gtk/gtk.h>
+
+#define GRID_SIZE 20
+#define BORDER 6
+
+static int window_count = 0;
+const char *geometry_string;
+
+static void
+on_window_destroy (GtkWidget *widget)
+{
+  window_count--;
+  if (window_count == 0)
+    gtk_main_quit();
+}
+
+static gboolean
+on_drawing_area_draw (GtkWidget *drawing_area,
+		      cairo_t   *cr,
+		      gpointer   data)
+{
+  int width = gtk_widget_get_allocated_width (drawing_area);
+  int height = gtk_widget_get_allocated_height (drawing_area);
+  int x, y;
+  int border = 0;
+  GdkWindowHints mask = GPOINTER_TO_UINT(data);
+
+  cairo_set_source_rgb (cr, 1, 1, 1);
+  cairo_paint (cr);
+
+  if ((mask & GDK_HINT_BASE_SIZE) != 0)
+    border = BORDER;
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  for (y = 0; y < height - 2 * border; y += GRID_SIZE)
+    for (x = 0; x < width - 2 * border; x += GRID_SIZE)
+      if (((x + y) / GRID_SIZE) % 2 == 0)
+	{
+	  cairo_rectangle (cr, border + x, border + y, GRID_SIZE, GRID_SIZE);
+	  cairo_fill (cr);
+	}
+
+  if (border > 0)
+    {
+      cairo_set_source_rgb (cr, 0, 0, 1);
+      cairo_save (cr);
+      cairo_set_line_width (cr, border);
+      cairo_rectangle (cr,
+		       border / 2., border / 2., width - border, height - border);
+      cairo_stroke (cr);
+    }
+
+  return FALSE;
+}
+
+static void
+create_window (GdkWindowHints  mask)
+{
+  GtkWidget *window;
+  GtkWidget *drawing_area;
+  GtkWidget *table;
+  GtkWidget *label;
+  GdkGeometry geometry;
+  GString *label_text = g_string_new (NULL);
+  int border = 0;
+
+  if ((mask & GDK_HINT_RESIZE_INC) != 0)
+    g_string_append (label_text, "Gridded\n");
+  if ((mask & GDK_HINT_BASE_SIZE) != 0)
+    g_string_append (label_text, "Base\n");
+  if ((mask & GDK_HINT_MIN_SIZE) != 0)
+    {
+      g_string_append (label_text, "Minimum\n");
+      if ((mask & GDK_HINT_BASE_SIZE) == 0)
+	g_string_append (label_text, "(base=min)\n");
+    }
+  if ((mask & GDK_HINT_MAX_SIZE) != 0)
+    g_string_append (label_text, "Maximum\n");
+
+  if (label_text->len > 0)
+    g_string_erase (label_text, label_text->len - 1, 1);
+  else
+    g_string_append (label_text, "No Options");
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  g_signal_connect (window, "destroy",
+		    G_CALLBACK (on_window_destroy), NULL);
+
+  table = gtk_table_new (0, 0, FALSE);
+  gtk_container_set_border_width (GTK_CONTAINER (table), 10);
+
+  label = gtk_label_new (label_text->str);
+  gtk_table_attach (GTK_TABLE (table), label,
+		    0, 1,                  0, 1,
+		    GTK_EXPAND | GTK_FILL, GTK_FILL,
+		    0,                     0);
+
+  label = gtk_label_new ("A\nB\nC\nD\nE");
+  gtk_table_attach (GTK_TABLE (table), label,
+		    1, 2,              1, 2,
+		    GTK_FILL,          GTK_EXPAND | GTK_FILL,
+		    0,                 0);
+
+  drawing_area = gtk_drawing_area_new ();
+  g_signal_connect (drawing_area, "draw",
+		    G_CALLBACK (on_drawing_area_draw),
+		    GUINT_TO_POINTER (mask));
+  gtk_table_attach (GTK_TABLE (table), drawing_area,
+		    0, 1,                  1, 2,
+		    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+		    0,                     0);
+
+  gtk_container_add (GTK_CONTAINER (window), table);
+
+  if ((mask & GDK_HINT_BASE_SIZE) != 0)
+    {
+      border = BORDER;
+      geometry.base_width = border * 2;
+      geometry.base_height = border * 2;
+    }
+
+  if ((mask & GDK_HINT_RESIZE_INC) != 0)
+    {
+      geometry.width_inc = GRID_SIZE;
+      geometry.height_inc = GRID_SIZE;
+    }
+
+  if ((mask & GDK_HINT_MIN_SIZE) != 0)
+    {
+      geometry.min_width = 5 * GRID_SIZE + 2 * border;
+      geometry.min_height = 5 * GRID_SIZE + 2 * border;
+    }
+
+  if ((mask & GDK_HINT_MAX_SIZE) != 0)
+    {
+      geometry.max_width = 15 * GRID_SIZE + 2 * border;
+      geometry.max_height = 15 * GRID_SIZE + 2 * border;
+    }
+
+  /* Contents must be set up before gtk_window_parse_geometry() */
+  gtk_widget_show_all (table);
+
+  gtk_window_set_geometry_hints (GTK_WINDOW (window),
+				 drawing_area,
+				 &geometry,
+				 mask);
+
+  if ((mask & GDK_HINT_RESIZE_INC) != 0)
+    {
+      if (geometry_string)
+	gtk_window_parse_geometry (GTK_WINDOW (window), geometry_string);
+    }
+
+  gtk_widget_show (window);
+  window_count++;
+}
+
+int
+main(int argc, char **argv)
+{
+  GError *error;
+  GOptionEntry options[] = {
+    { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry_string, "Window geometry (only for gridded windows)", "GEOMETRY" },
+    { NULL }
+  };
+
+  if (!gtk_init_with_args (&argc, &argv, "", options, NULL, &error))
+    {
+      g_print ("Failed to parse args: %s\n", error->message);
+      g_error_free (error);
+      return 1;
+    }
+
+  create_window (GDK_HINT_MIN_SIZE);
+  create_window (GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE);
+  create_window (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+  create_window (GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE);
+  create_window (GDK_HINT_RESIZE_INC | GDK_HINT_MAX_SIZE);
+  create_window (GDK_HINT_RESIZE_INC | GDK_HINT_BASE_SIZE);
+  create_window (GDK_HINT_RESIZE_INC | GDK_HINT_BASE_SIZE | GDK_HINT_MIN_SIZE);
+
+  gtk_main ();
+}
diff --git a/tests/testgtk.c b/tests/testgtk.c
index 89d648b..17136cf 100644
--- a/tests/testgtk.c
+++ b/tests/testgtk.c
@@ -1645,173 +1645,6 @@ create_statusbar (GtkWidget *widget)
 }
 
 /*
- * Gridded geometry
- */
-#define GRID_SIZE 20
-#define DEFAULT_GEOMETRY "10x10"
-
-static gboolean
-gridded_geometry_draw (GtkWidget *widget,
-                       cairo_t   *cr)
-{
-  GtkStateType state;
-  GtkStyle *style;
-  int i, j, width, height;
-
-  style = gtk_widget_get_style (widget);
-  state = gtk_widget_get_state (widget);
-  width = gtk_widget_get_allocated_width (widget);
-  height = gtk_widget_get_allocated_height (widget);
-
-  gdk_cairo_set_source_color (cr, &style->base[state]);
-  cairo_paint (cr);
-
-  for (i = 0 ; i * GRID_SIZE < width; i++)
-    for (j = 0 ; j * GRID_SIZE < height; j++)
-      {
-	if ((i + j) % 2 == 0)
-	  cairo_rectangle (cr, i * GRID_SIZE, j * GRID_SIZE, GRID_SIZE, GRID_SIZE);
-      }
-
-  gdk_cairo_set_source_color (cr, &style->text[state]);
-  cairo_fill (cr);
-
-  return FALSE;
-}
-
-static void
-gridded_geometry_subresponse (GtkDialog *dialog,
-			      gint       response_id,
-			      gchar     *geometry_string)
-{
-  if (response_id == GTK_RESPONSE_NONE)
-    {
-      gtk_widget_destroy (GTK_WIDGET (dialog));
-    }
-  else
-    {
-      if (!gtk_window_parse_geometry (GTK_WINDOW (dialog), geometry_string))
-	{
-	  g_print ("Can't parse geometry string %s\n", geometry_string);
-	  gtk_window_parse_geometry (GTK_WINDOW (dialog), DEFAULT_GEOMETRY);
-	}
-    }
-}
-
-static void
-gridded_geometry_response (GtkDialog *dialog,
-			   gint       response_id,
-			   GtkEntry  *entry)
-{
-  if (response_id == GTK_RESPONSE_NONE)
-    {
-      gtk_widget_destroy (GTK_WIDGET (dialog));
-    }
-  else
-    {
-      gchar *geometry_string = g_strdup (gtk_entry_get_text (entry));
-      gchar *title = g_strdup_printf ("Gridded window at: %s", geometry_string);
-      GtkWidget *content_area;
-      GtkWidget *window;
-      GtkWidget *drawing_area;
-      GtkWidget *box;
-      GdkGeometry geometry;
-      
-      window = gtk_dialog_new_with_buttons (title,
-                                            NULL, 0,
-                                            "Reset", 1,
-                                            GTK_STOCK_CLOSE, GTK_RESPONSE_NONE,
-                                            NULL);
-
-      gtk_window_set_screen (GTK_WINDOW (window), 
-			     gtk_widget_get_screen (GTK_WIDGET (dialog)));
-      g_free (title);
-      g_signal_connect (window, "response",
-			G_CALLBACK (gridded_geometry_subresponse), geometry_string);
-
-      content_area = gtk_dialog_get_content_area (GTK_DIALOG (window));
-
-      box = gtk_vbox_new (FALSE, 0);
-      gtk_box_pack_start (GTK_BOX (content_area), box, TRUE, TRUE, 0);
-      
-      gtk_container_set_border_width (GTK_CONTAINER (box), 7);
-      
-      drawing_area = gtk_drawing_area_new ();
-      g_signal_connect (drawing_area, "draw",
-			G_CALLBACK (gridded_geometry_draw), NULL);
-      gtk_box_pack_start (GTK_BOX (box), drawing_area, TRUE, TRUE, 0);
-
-      /* Gross hack to work around bug 68668... if we set the size request
-       * large enough, then  the current
-       *
-       *   request_of_window - request_of_geometry_widget
-       *
-       * method of getting the base size works more or less works.
-       */
-      gtk_widget_set_size_request (drawing_area, 2000, 2000);
-
-      geometry.base_width = 0;
-      geometry.base_height = 0;
-      geometry.min_width = 2 * GRID_SIZE;
-      geometry.min_height = 2 * GRID_SIZE;
-      geometry.width_inc = GRID_SIZE;
-      geometry.height_inc = GRID_SIZE;
-
-      gtk_window_set_geometry_hints (GTK_WINDOW (window), drawing_area,
-				     &geometry,
-				     GDK_HINT_BASE_SIZE | GDK_HINT_MIN_SIZE | GDK_HINT_RESIZE_INC);
-
-      if (!gtk_window_parse_geometry (GTK_WINDOW (window), geometry_string))
-	{
-	  g_print ("Can't parse geometry string %s\n", geometry_string);
-	  gtk_window_parse_geometry (GTK_WINDOW (window), DEFAULT_GEOMETRY);
-	}
-
-      gtk_widget_show_all (window);
-    }
-}
-
-static void 
-create_gridded_geometry (GtkWidget *widget)
-{
-  static GtkWidget *window = NULL;
-  gpointer window_ptr;
-  GtkWidget *content_area;
-  GtkWidget *entry;
-  GtkWidget *label;
-
-  if (!window)
-    {
-      window = gtk_dialog_new_with_buttons ("Gridded Geometry",
-                                            NULL, 0,
-					    "Create", 1,
-                                            GTK_STOCK_CLOSE, GTK_RESPONSE_NONE,
-                                            NULL);
-      
-      gtk_window_set_screen (GTK_WINDOW (window),
-			     gtk_widget_get_screen (widget));
-
-      content_area = gtk_dialog_get_content_area (GTK_DIALOG (window));
-
-      label = gtk_label_new ("Geometry string:");
-      gtk_box_pack_start (GTK_BOX (content_area), label, FALSE, FALSE, 0);
-
-      entry = gtk_entry_new ();
-      gtk_entry_set_text (GTK_ENTRY (entry), DEFAULT_GEOMETRY);
-      gtk_box_pack_start (GTK_BOX (content_area), entry, FALSE, FALSE, 0);
-
-      g_signal_connect (window, "response",
-			G_CALLBACK (gridded_geometry_response), entry);
-      window_ptr = &window;
-      g_object_add_weak_pointer (G_OBJECT (window), window_ptr);
-
-      gtk_widget_show_all (window);
-    }
-  else
-    gtk_widget_destroy (window);
-}
-
-/*
  * GtkHandleBox
  */
 
@@ -10152,7 +9985,6 @@ struct {
   { "flipping", create_flipping },
   { "focus", create_focus },
   { "font selection", create_font_selection },
-  { "gridded geometry", create_gridded_geometry },
   { "handle box", create_handle_box },
   { "image", create_image },
   { "key lookup", create_key_lookup },



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