Re: Size allocation and redrawing issues



Attached is my attempt at implement what I described. Brief summary
of what it does:

 - Adds a function gdk_window_invalidate_maybe_recurse() for 
   use in "shallow invalidation" of a widget. (Windows belonging
   to the widget, but not to the widget's children)

 - Adds private flags GTK_ALLOC_NEEDED, GTK_REQUEST_NEEDED.
   These flags are set up on ancestors up to the resize container
   on queue_resize. Size requests only actually take place
   if GTK_REQUEST_NEEDED, size allocations only take place
   if GTK_ALLOC_NEEDED or the size changed.

 - Removes container->resize_widgets and the RESIZE_NEEDED flag
   since the above flags are sufficient to figure out what
   needs to be resized/reallocated. A lot of code is removed
   from gtkcontainer.c; gtk_container_resize_children() ends
   up being simply:

    gtk_widget_size_allocate (container, &container->allocation);

 - gtk_widget_set_redraw_on_alloc() is added as in my original
   mail for widgets that want to handle redrawing on resize
   themself.

 - Invalidation when a widget is resized or moved is "shallow" as 
   described above - only the windows that need to be invalidated
   are invalidated.

It's rather hard to benchmark, but testing I've done seems to
indicate that it is 30-40% faster at things like opaque resizing
windows, most of that coming from Soeren's suggestion of caching
size requests.

But beyond that, it achieves my goals of eliminating hysteresis,
and allowing widgets that want to handle resizes in a "smarter"
to do so.

Regards,
                                        Owen

Index: gdk/gdkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.c,v
retrieving revision 1.123
diff -u -p -r1.123 gdkwindow.c
--- gdk/gdkwindow.c	2001/10/24 14:15:49	1.123
+++ gdk/gdkwindow.c	2001/10/29 23:17:03
@@ -2152,10 +2152,12 @@ gdk_window_invalidate_rect   (GdkWindow 
 }
 
 /**
- * gdk_window_invalidate_region:
+ * gdk_window_invalidate_maybe_recurse:
  * @window: a #GdkWindow
  * @region: a #GdkRegion
- * @invalidate_children: %TRUE to also invalidate child windows 
+ * @child_func: function to use to decide if to recurse to a child,
+ *              %NULL means never recurse.
+ * @child_func_data: data passed to @child_func
  *
  * Adds @region to the update area for @window. The update area is the
  * region that needs to be redrawn, or "dirty region." The call
@@ -2169,16 +2171,16 @@ gdk_window_invalidate_rect   (GdkWindow 
  * normally there's no need to do that manually, you just need to
  * invalidate regions that you know should be redrawn.
  *
- * The @invalidate_children parameter controls whether the region of
+ * The @child_func parameter controls whether the region of
  * each child window that intersects @region will also be invalidated.
- * If %FALSE, then the update area for child windows will remain
- * unaffected.
- *
+ * Only children for whic @child_func returns TRUE will have the area
+ * invalidated.
  **/
 void
-gdk_window_invalidate_region (GdkWindow *window,
-			      GdkRegion *region,
-			      gboolean   invalidate_children)
+gdk_window_invalidate_maybe_recurse (GdkWindow *window,
+				     GdkRegion *region,
+				     gboolean (*child_func) (GdkWindow *, gpointer),
+				     gpointer   user_data)
 {
   GdkWindowObject *private = (GdkWindowObject *)window;
   GdkRegion *visible_region;
@@ -2233,7 +2235,7 @@ gdk_window_invalidate_region (GdkWindow 
 					   gdk_window_update_idle, NULL, NULL);
 	}
       
-      if (invalidate_children)
+      if (child_func)
 	{
 	  GList *tmp_list;
 	  
@@ -2242,8 +2244,8 @@ gdk_window_invalidate_region (GdkWindow 
 	    {
 	      GdkWindowObject *child = tmp_list->data;
 	      tmp_list = tmp_list->next;
-	      
-	      if (!child->input_only)
+
+	      if (!child->input_only && (*child_func) ((GdkWindow *)child, user_data))
 		{
 		  GdkRegion *child_region;
 		  gint x, y;
@@ -2263,6 +2265,48 @@ gdk_window_invalidate_region (GdkWindow 
     }
   
   gdk_region_destroy (visible_region);
+}
+
+static gboolean
+true_predicate (GdkWindow *window,
+		gpointer   user_data)
+{
+  return TRUE;
+}
+
+/**
+ * gdk_window_invalidate_region:
+ * @window: a #GdkWindow
+ * @region: a #GdkRegion
+ * @invalidate_children: %TRUE to also invalidate child windows 
+ *
+ * Adds @region to the update area for @window. The update area is the
+ * region that needs to be redrawn, or "dirty region." The call
+ * gdk_window_process_updates() sends one or more expose events to the
+ * window, which together cover the entire update area. An
+ * application would normally redraw the contents of @window in
+ * response to those expose events.
+ *
+ * GDK will call gdk_window_process_all_updates() on your behalf
+ * whenever your program returns to the main loop and becomes idle, so
+ * normally there's no need to do that manually, you just need to
+ * invalidate regions that you know should be redrawn.
+ *
+ * The @invalidate_children parameter controls whether the region of
+ * each child window that intersects @region will also be invalidated.
+ * If %FALSE, then the update area for child windows will remain
+ * unaffected. See gdk_window_invalidate_maybe_recurse if you need
+ * fine grained control over which children are invalidated.
+ **/
+void
+gdk_window_invalidate_region (GdkWindow *window,
+			      GdkRegion *region,
+			      gboolean   invalidate_children)
+{
+  gdk_window_invalidate_maybe_recurse (window, region,
+				       invalidate_children ?
+				         true_predicate : (gboolean (*) (GdkWindow *, gpointer))NULL,
+				       NULL);
 }
 
 /**
Index: gdk/gdkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.h,v
retrieving revision 1.33
diff -u -p -r1.33 gdkwindow.h
--- gdk/gdkwindow.h	2001/10/18 20:23:16	1.33
+++ gdk/gdkwindow.h	2001/10/29 23:17:03
@@ -491,12 +491,16 @@ void gdk_window_begin_move_drag   (GdkWi
                                    guint32        timestamp);
 
 /* Interface for dirty-region queueing */
-void       gdk_window_invalidate_rect     (GdkWindow    *window,
-					   GdkRectangle *rect,
-					   gboolean      invalidate_children);
-void       gdk_window_invalidate_region   (GdkWindow    *window,
-					   GdkRegion    *region,
-					   gboolean      invalidate_children);
+void       gdk_window_invalidate_rect           (GdkWindow    *window,
+					         GdkRectangle *rect,
+					         gboolean      invalidate_children);
+void       gdk_window_invalidate_region         (GdkWindow    *window,
+					         GdkRegion    *region,
+					         gboolean      invalidate_children);
+void       gdk_window_invalidate_maybe_recurse  (GdkWindow *window,
+						 GdkRegion *region,
+						 gboolean (*child_func) (GdkWindow *, gpointer),
+						 gpointer   user_data);
 GdkRegion *gdk_window_get_update_area     (GdkWindow    *window);
 
 void       gdk_window_freeze_updates      (GdkWindow    *window);
Index: gtk/gtkcontainer.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkcontainer.c,v
retrieving revision 1.91
diff -u -p -r1.91 gtkcontainer.c
--- gtk/gtkcontainer.c	2001/10/27 00:03:13	1.91
+++ gtk/gtkcontainer.c	2001/10/29 23:17:04
@@ -698,7 +698,6 @@ gtk_container_init (GtkContainer *contai
   container->need_resize = FALSE;
   container->resize_mode = GTK_RESIZE_PARENT;
   container->reallocate_redraws = FALSE;
-  container->resize_widgets = NULL;
 }
 
 static void
@@ -713,8 +712,6 @@ gtk_container_destroy (GtkObject *object
   
   if (GTK_CONTAINER_RESIZE_PENDING (container))
     _gtk_container_dequeue_resize_handler (container);
-  if (container->resize_widgets)
-    _gtk_container_clear_resize_widgets (container);
 
   /* do this before walking child widgets, to avoid
    * removing children from focus chain one by one.
@@ -900,28 +897,6 @@ _gtk_container_dequeue_resize_handler (G
 }
 
 void
-_gtk_container_clear_resize_widgets (GtkContainer *container)
-{
-  GSList *node;
-
-  g_return_if_fail (container != NULL);
-  g_return_if_fail (GTK_IS_CONTAINER (container));
-
-  node = container->resize_widgets;
-
-  while (node)
-    {
-      GtkWidget *widget = node->data;
-
-      GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_NEEDED);
-      node = node->next;
-    }
-  
-  g_slist_free (container->resize_widgets);
-  container->resize_widgets = NULL;
-}
-
-void
 gtk_container_set_resize_mode (GtkContainer  *container,
 			       GtkResizeMode  resize_mode)
 {
@@ -940,14 +915,8 @@ gtk_container_set_resize_mode (GtkContai
     {
       container->resize_mode = resize_mode;
       
-      if (resize_mode == GTK_RESIZE_IMMEDIATE)
-	gtk_container_check_resize (container);
-      else
-	{
-	  _gtk_container_clear_resize_widgets (container);
-	  gtk_widget_queue_resize (GTK_WIDGET (container));
-	}
-       g_object_notify (G_OBJECT (container), "resize_mode");
+      gtk_widget_queue_resize (GTK_WIDGET (container));
+      g_object_notify (G_OBJECT (container), "resize_mode");
     }
 }
 
@@ -987,7 +956,7 @@ gtk_container_get_resize_container (GtkC
   while (widget->parent)
     {
       widget = widget->parent;
-      if (GTK_IS_RESIZE_CONTAINER (widget) && !GTK_WIDGET_RESIZE_NEEDED (widget))
+      if (GTK_IS_RESIZE_CONTAINER (widget))
 	break;
     }
 
@@ -1034,21 +1023,22 @@ _gtk_container_queue_resize (GtkContaine
   g_return_if_fail (container != NULL);
   g_return_if_fail (GTK_IS_CONTAINER (container));
 
-  /* clear resize widgets for resize containers
-   * before aborting prematurely. this is especially
-   * important for toplevels which may need imemdiate
-   * processing or their resize handler to be queued.
-   */
-  if (GTK_IS_RESIZE_CONTAINER (container))
-    _gtk_container_clear_resize_widgets (container);
-  if (GTK_OBJECT_DESTROYED (container) ||
-      GTK_WIDGET_RESIZE_NEEDED (container))
-    return;
-  
   resize_container = gtk_container_get_resize_container (container);
   
   if (resize_container)
     {
+      GtkWidget *widget = GTK_WIDGET (container);
+      
+      while (!GTK_WIDGET_ALLOC_NEEDED (widget) || !GTK_WIDGET_REQUEST_NEEDED (widget))
+	{
+	  GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED);
+	  GTK_PRIVATE_SET_FLAG (widget, GTK_REQUEST_NEEDED);
+	  if (widget == GTK_WIDGET (resize_container))
+	    break;
+	  
+	  widget = widget->parent;
+	}
+      
       if (GTK_WIDGET_VISIBLE (resize_container) &&
 	  (GTK_WIDGET_TOPLEVEL (resize_container) || GTK_WIDGET_DRAWABLE (resize_container)))
 	{
@@ -1064,16 +1054,9 @@ _gtk_container_queue_resize (GtkContaine
 					   NULL);
 		  container_resize_queue = g_slist_prepend (container_resize_queue, resize_container);
 		}
-	      
-	      GTK_PRIVATE_SET_FLAG (container, GTK_RESIZE_NEEDED);
-	      resize_container->resize_widgets =
-		g_slist_prepend (resize_container->resize_widgets, container);
 	      break;
 
 	    case GTK_RESIZE_IMMEDIATE:
-	      GTK_PRIVATE_SET_FLAG (container, GTK_RESIZE_NEEDED);
-	      resize_container->resize_widgets =
-		g_slist_prepend (resize_container->resize_widgets, container);
 	      gtk_container_check_resize (resize_container);
 	      break;
 
@@ -1134,135 +1117,24 @@ gtk_container_real_check_resize (GtkCont
  *  queued a resize request. Which means that the allocation
  *  is not sufficient for the requisition of some child.
  *  We've already performed a size request at this point,
- *  so we simply need to run through the list of resize
- *  widgets and reallocate their sizes appropriately. We
- *  make the optimization of not performing reallocation
- *  for a widget who also has a parent in the resize widgets
- *  list. GTK_RESIZE_NEEDED is used for flagging those
- *  parents inside this function.
+ *  so we simply need to reallocate and let the allocation
+ *  trickle down via GTK_WIDGET_ALLOC_NEEDED flags. 
  */
 void
 gtk_container_resize_children (GtkContainer *container)
 {
   GtkWidget *widget;
-  GtkWidget *resize_container;
-  GSList *resize_widgets;
-  GSList *resize_containers;
-  GSList *node;
   
   /* resizing invariants:
    * toplevels have *always* resize_mode != GTK_RESIZE_PARENT set.
-   * containers with resize_mode==GTK_RESIZE_PARENT have to have resize_widgets
-   * set to NULL.
-   * containers that are flagged RESIZE_NEEDED must have resize_widgets set to
-   * NULL, or are toplevels (thus have ->parent set to NULL).
-   * widgets that are in some container->resize_widgets list must be flagged with
-   * RESIZE_NEEDED.
-   * widgets that have RESIZE_NEEDED set must be referenced in some
-   * GTK_IS_RESIZE_CONTAINER (container)->resize_widgets list.
    * containers that have an idle sizer pending must be flagged with
    * RESIZE_PENDING.
    */
-  
   g_return_if_fail (container != NULL);
   g_return_if_fail (GTK_IS_CONTAINER (container));
 
-  /* we first check out if we actually need to perform a resize,
-   * which is not the case if we got another container queued for
-   * a resize in our ancestry. also we can skip the whole
-   * resize_widgets checks if we are a toplevel and NEED_RESIZE.
-   * this code assumes that our allocation is sufficient for our
-   * requisition, since otherwise we would NEED_RESIZE.
-   */
-  resize_container = GTK_WIDGET (container);
-  while (resize_container)
-    {
-      if (GTK_WIDGET_RESIZE_NEEDED (resize_container))
-	break;
-      resize_container = resize_container->parent;
-    }
-  if (resize_container)
-    {
-      /* queue_resize and size_allocate both clear our
-       * resize_widgets list.
-       */
-      if (resize_container->parent)
-	_gtk_container_queue_resize (container);
-      else
-	gtk_widget_size_allocate (GTK_WIDGET (container),
-				  &GTK_WIDGET (container)->allocation);
-      return;
-    }
-
-  resize_container = GTK_WIDGET (container);
-
-  /* we now walk the ancestry for all resize widgets as long
-   * as they are our children and as long as their allocation
-   * is insufficient, since we don't need to reallocate below that.
-   */
-  resize_widgets = container->resize_widgets;
-  container->resize_widgets = NULL;
-  for (node = resize_widgets; node; node = node->next)
-    {
-      widget = node->data;
-
-      GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_NEEDED);
-
-      while (widget->parent != resize_container &&
-	     ((widget->allocation.width < widget->requisition.width) ||
-	      (widget->allocation.height < widget->requisition.height)))
-	widget = widget->parent;
-      
-      GTK_PRIVATE_SET_FLAG (widget, GTK_RESIZE_NEEDED);
-      node->data = widget;
-    }
-
-  /* for the newly setup resize_widgets list, we now walk each widget's
-   * ancestry to sort those widgets out that have RESIZE_NEEDED parents.
-   * we can safely stop the walk if we are the parent, since we checked
-   * our own ancestry already.
-   */
-  resize_containers = NULL;
-  for (node = resize_widgets; node; node = node->next)
-    {
-      GtkWidget *parent;
-
-      widget = node->data;
-      
-      if (!GTK_WIDGET_RESIZE_NEEDED (widget))
-	continue;
-      
-      parent = widget->parent;
-      
-      while (parent != resize_container)
-	{
-	  if (GTK_WIDGET_RESIZE_NEEDED (parent))
-	    {
-	      GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_NEEDED);
-	      widget = parent;
-	    }
-	  parent = parent->parent;
-	}
-      
-      if (!g_slist_find (resize_containers, widget))
-	{
-	  resize_containers = g_slist_prepend (resize_containers, widget);
-	  gtk_widget_ref (widget);
-	}
-    }
-  g_slist_free (resize_widgets);
-  
-  for (node = resize_containers; node; node = node->next)
-    {
-      widget = node->data;
-      
-      GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_NEEDED);
-
-      gtk_widget_size_allocate (widget, &widget->allocation);
-
-      gtk_widget_unref (widget);
-    }
-  g_slist_free (resize_containers);
+  widget = GTK_WIDGET (container);
+  gtk_widget_size_allocate (widget, &widget->allocation);
 }
 
 /**
@@ -1630,8 +1502,6 @@ get_allocation_coords (GtkContainer  *co
 		       GtkWidget     *widget,
 		       GdkRectangle  *allocation)
 {
-  GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
-
   *allocation = widget->allocation;
 
   return gtk_widget_translate_coordinates (widget, GTK_WIDGET (container),
Index: gtk/gtkcontainer.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkcontainer.h,v
retrieving revision 1.46
diff -u -p -r1.46 gtkcontainer.h
--- gtk/gtkcontainer.h	2001/09/24 16:54:41	1.46
+++ gtk/gtkcontainer.h	2001/10/29 23:17:04
@@ -63,10 +63,6 @@ struct _GtkContainer
   guint resize_mode : 2;
   guint reallocate_redraws : 1;
   guint has_focus_chain : 1;
-  
-  /* The list of children that requested a resize
-   */
-  GSList *resize_widgets;
 };
 
 struct _GtkContainerClass
Index: gtk/gtkprivate.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkprivate.h,v
retrieving revision 1.24
diff -u -p -r1.24 gtkprivate.h
--- gtk/gtkprivate.h	2001/10/29 07:06:36	1.24
+++ gtk/gtkprivate.h	2001/10/29 23:17:04
@@ -43,14 +43,16 @@ typedef enum
 {
   PRIVATE_GTK_USER_STYLE	= 1 <<  0,
   PRIVATE_GTK_RESIZE_PENDING	= 1 <<  2,
-  PRIVATE_GTK_RESIZE_NEEDED	= 1 <<  3,
   PRIVATE_GTK_LEAVE_PENDING	= 1 <<  4,
   PRIVATE_GTK_HAS_SHAPE_MASK	= 1 <<  5,
   PRIVATE_GTK_IN_REPARENT       = 1 <<  6,
   PRIVATE_GTK_DIRECTION_SET     = 1 <<  7,   /* If the reading direction is not DIR_NONE */
   PRIVATE_GTK_DIRECTION_LTR     = 1 <<  8,   /* If the reading direction is DIR_LTR */
   PRIVATE_GTK_ANCHORED          = 1 <<  9,   /* If widget has a GtkWindow ancestor */
-  PRIVATE_GTK_CHILD_VISIBLE     = 1 <<  10   /* If widget should be mapped when parent is mapped */
+  PRIVATE_GTK_CHILD_VISIBLE     = 1 <<  10,  /* If widget should be mapped when parent is mapped */
+  PRIVATE_GTK_REDRAW_ON_ALLOC   = 1 <<  11,  /* If we should queue a draw on the entire widget when it is reallocated */
+  PRIVATE_GTK_ALLOC_NEEDED      = 1 <<  12,  /* If we we should allocate even if the allocation is the same */
+  PRIVATE_GTK_REQUEST_NEEDED    = 1 <<  13   /* Whether we need to call gtk_widget_size_request */
 } GtkPrivateFlags;
 
 /* Macros for extracting a widgets private_flags from GtkWidget.
@@ -58,7 +60,6 @@ typedef enum
 #define GTK_PRIVATE_FLAGS(wid)            (GTK_WIDGET (wid)->private_flags)
 #define GTK_WIDGET_USER_STYLE(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_USER_STYLE) != 0)
 #define GTK_CONTAINER_RESIZE_PENDING(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_RESIZE_PENDING) != 0)
-#define GTK_WIDGET_RESIZE_NEEDED(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_RESIZE_NEEDED) != 0)
 #define GTK_WIDGET_LEAVE_PENDING(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_LEAVE_PENDING) != 0)
 #define GTK_WIDGET_HAS_SHAPE_MASK(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_HAS_SHAPE_MASK) != 0)
 #define GTK_WIDGET_IN_REPARENT(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_IN_REPARENT) != 0)
@@ -66,6 +67,9 @@ typedef enum
 #define GTK_WIDGET_DIRECTION_LTR(obj)     ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_DIRECTION_LTR) != 0)
 #define GTK_WIDGET_ANCHORED(obj)          ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_ANCHORED) != 0)
 #define GTK_WIDGET_CHILD_VISIBLE(obj)     ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_CHILD_VISIBLE) != 0)
+#define GTK_WIDGET_REDRAW_ON_ALLOC(obj)   ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_REDRAW_ON_ALLOC) != 0)
+#define GTK_WIDGET_ALLOC_NEEDED(obj)      ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_ALLOC_NEEDED) != 0)
+#define GTK_WIDGET_REQUEST_NEEDED(obj)    ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_REQUEST_NEEDED) != 0)
 
 /* Macros for setting and clearing private widget flags.
  * we use a preprocessor string concatenation here for a clear
Index: gtk/gtksizegroup.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtksizegroup.c,v
retrieving revision 1.3
diff -u -p -r1.3 gtksizegroup.c
--- gtk/gtksizegroup.c	2001/10/26 23:47:38	1.3
+++ gtk/gtksizegroup.c	2001/10/29 23:17:04
@@ -20,6 +20,7 @@
 
 #include "gtkcontainer.h"
 #include "gtkintl.h"
+#include "gtkprivate.h"
 #include "gtksignal.h"
 #include "gtksizegroup.h"
 
@@ -116,8 +117,8 @@ add_widget_to_closure (GtkWidget       *
 static void
 real_queue_resize (GtkWidget *widget)
 {
-  if (GTK_IS_RESIZE_CONTAINER (widget))
-    _gtk_container_clear_resize_widgets (GTK_CONTAINER (widget));
+  GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED);
+  GTK_PRIVATE_SET_FLAG (widget, GTK_REQUEST_NEEDED);
   
   if (widget->parent)
     _gtk_container_queue_resize (GTK_CONTAINER (widget->parent));
@@ -476,12 +477,23 @@ get_base_dimension (GtkWidget        *wi
     }
 }
 
+static void
+do_size_request (GtkWidget *widget)
+{
+  if (GTK_WIDGET_REQUEST_NEEDED (widget))
+    {
+      gtk_widget_ensure_style (widget);
+      gtk_signal_emit_by_name (GTK_OBJECT (widget), "size_request", &widget->requisition);
+      
+      GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);
+    }
+}
+
 static gint
 compute_base_dimension (GtkWidget        *widget,
 			GtkSizeGroupMode  mode)
 {
-  gtk_widget_ensure_style (widget);
-  gtk_signal_emit_by_name (GTK_OBJECT (widget), "size_request", &widget->requisition);
+  do_size_request (widget);
 
   return get_base_dimension (widget, mode);
 }
@@ -658,9 +670,8 @@ _gtk_size_group_compute_requisition (Gtk
     }
   else
     {
-      gtk_widget_ensure_style (widget);
-      gtk_signal_emit_by_name (GTK_OBJECT (widget), "size_request", &widget->requisition);
-
+      do_size_request (widget);
+      
       if (requisition)
 	get_fast_child_requisition (widget, requisition);
     }
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.260
diff -u -p -r1.260 gtkwidget.c
--- gtk/gtkwidget.c	2001/10/22 04:34:41	1.260
+++ gtk/gtkwidget.c	2001/10/29 23:17:04
@@ -1248,6 +1248,8 @@ gtk_widget_init (GtkWidget *widget)
 			(composite_child_stack ? GTK_COMPOSITE_CHILD : 0) |
 			GTK_DOUBLE_BUFFERED);
 
+  GTK_PRIVATE_SET_FLAG (widget, GTK_REDRAW_ON_ALLOC);
+
   widget->style = gtk_widget_get_default_style ();
   g_object_ref (widget->style);
 }
@@ -1412,7 +1414,6 @@ gtk_widget_unparent (GtkWidget *widget)
 {
   GObjectNotifyQueue *nqueue;
   GtkWidget *toplevel;
-  GtkWidget *ancestor;
   GtkWidget *old_parent;
   
   g_return_if_fail (GTK_IS_WIDGET (widget));
@@ -1469,64 +1470,6 @@ gtk_widget_unparent (GtkWidget *widget)
   else
     toplevel = NULL;
 
-  if (GTK_IS_RESIZE_CONTAINER (widget))
-    _gtk_container_clear_resize_widgets (GTK_CONTAINER (widget));
-  
-  /* Remove the widget and all its children from any ->resize_widgets list
-   * of all the parents in our branch. This code should move into gtkcontainer.c
-   * somwhen, since we mess around with ->resize_widgets, which is
-   * actually not of our business.
-   *
-   * Two ways to make this prettier:
-   *   Write a g_slist_conditional_remove (GSList, gboolean (*)(gpointer))
-   *   Change resize_widgets to a GList
-   */
-  ancestor = widget->parent;
-  while (ancestor)
-    {
-      GSList *slist;
-      GSList *prev;
-
-      if (!GTK_CONTAINER (ancestor)->resize_widgets)
-	{
-	  ancestor = ancestor->parent;
-	  continue;
-	}
-
-      prev = NULL;
-      slist = GTK_CONTAINER (ancestor)->resize_widgets;
-      while (slist)
-	{
-	  GtkWidget *child;
-	  GtkWidget *parent;
-	  GSList *last;
-
-	  last = slist;
-	  slist = last->next;
-	  child = last->data;
-	  
-	  parent = child;
-	  while (parent && (parent != widget))
-	    parent = parent->parent;
-	  
-	  if (parent == widget)
-	    {
-	      GTK_PRIVATE_UNSET_FLAG (child, GTK_RESIZE_NEEDED);
-	      
-	      if (prev)
-		prev->next = slist;
-	      else
-		GTK_CONTAINER (ancestor)->resize_widgets = slist;
-	      
-	      g_slist_free_1 (last);
-	    }
-	  else
-	    prev = last;
-	}
-
-      ancestor = ancestor->parent;
-    }
-
   gtk_widget_queue_clear_child (widget);
 
   /* Reset the width and height here, to force reallocation if we
@@ -1831,7 +1774,7 @@ gtk_widget_map (GtkWidget *widget)
       gtk_signal_emit (GTK_OBJECT (widget), widget_signals[MAP]);
 
       if (GTK_WIDGET_NO_WINDOW (widget))
-	gtk_widget_queue_draw (widget);
+	gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
     }
 }
 
@@ -1851,7 +1794,7 @@ gtk_widget_unmap (GtkWidget *widget)
   if (GTK_WIDGET_MAPPED (widget))
     {
       if (GTK_WIDGET_NO_WINDOW (widget))
-	gtk_widget_queue_clear_child (widget);
+	gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
       gtk_signal_emit (GTK_OBJECT (widget), widget_signals[UNMAP]);
     }
 }
@@ -2208,13 +2151,6 @@ gtk_widget_size_request (GtkWidget	*widg
 #endif /* G_ENABLE_DEBUG */
 
   _gtk_size_group_compute_requisition (widget, requisition);
-
-#if 0  
-  if (requisition)
-    gtk_widget_get_child_requisition (widget, requisition);
-
-  gtk_widget_unref (widget);
-#endif  
 }
 
 /**
@@ -2247,6 +2183,38 @@ gtk_widget_get_child_requisition (GtkWid
   _gtk_size_group_get_child_requisition (widget, requisition);
 }
 
+static gboolean
+invalidate_predicate (GdkWindow *window,
+		      gpointer   data)
+{
+  gpointer user_data;
+
+  gdk_window_get_user_data (window, &user_data);
+
+  return (user_data == data);
+}
+
+/* Invalidate @region in widget->window and all children
+ * of widget->window owned by widget. @region is in the
+ * same coordinates as widget->allocation and will be
+ * modified by this call.
+ */
+static void
+invalidate_widget_windows (GtkWidget *widget,
+			   GdkRegion *region)
+{
+  if (!GTK_WIDGET_NO_WINDOW (widget))
+    {
+      int x, y;
+      
+      gdk_window_get_position (widget->window, &x, &y);
+      gdk_region_offset (region, -x, -y);
+    }
+
+  gdk_window_invalidate_maybe_recurse (widget->window, region,
+				       invalidate_predicate, widget);
+}
+
 /**
  * gtk_widget_size_allocate:
  * @widget: a #GtkWidget
@@ -2261,11 +2229,18 @@ gtk_widget_size_allocate (GtkWidget	*wid
 			  GtkAllocation *allocation)
 {
   GtkWidgetAuxInfo *aux_info;
-  GtkAllocation real_allocation;
-  gboolean needs_draw = FALSE;
+  GdkRectangle real_allocation;
+  GdkRectangle old_allocation;
+  gboolean alloc_needed;
+  gboolean size_changed;
+  gboolean position_changed;
   
   g_return_if_fail (GTK_IS_WIDGET (widget));
-  
+
+  alloc_needed = GTK_WIDGET_ALLOC_NEEDED (widget);
+  GTK_PRIVATE_UNSET_FLAG (widget, GTK_ALLOC_NEEDED);
+
+  old_allocation = widget->allocation;
   real_allocation = *allocation;
   aux_info =_gtk_widget_get_aux_info (widget, FALSE);
   
@@ -2287,33 +2262,66 @@ gtk_widget_size_allocate (GtkWidget	*wid
   real_allocation.width = MAX (real_allocation.width, 1);
   real_allocation.height = MAX (real_allocation.height, 1);
 
-  if (GTK_WIDGET_NO_WINDOW (widget))
+  size_changed = (old_allocation.width != real_allocation.width ||
+		  old_allocation.height != real_allocation.height);
+  position_changed = (old_allocation.x != real_allocation.x ||
+		      old_allocation.y != real_allocation.y);
+
+  if (!alloc_needed && !size_changed && !position_changed)
+    return;
+  
+  gtk_signal_emit (GTK_OBJECT (widget), widget_signals[SIZE_ALLOCATE], &real_allocation);
+
+  if (GTK_WIDGET_MAPPED (widget))
     {
-      if (widget->allocation.x != real_allocation.x ||
-	  widget->allocation.y != real_allocation.y ||
-	  widget->allocation.width != real_allocation.width ||
-	  widget->allocation.height != real_allocation.height)
+      if (GTK_WIDGET_NO_WINDOW (widget) && position_changed)
 	{
-	  gtk_widget_queue_clear_child (widget);
-	  needs_draw = TRUE;
-	}
-    }
-  else if (widget->allocation.width != real_allocation.width ||
-	   widget->allocation.height != real_allocation.height)
-    {
-      needs_draw = TRUE;
-    }
+	  /* Invalidate union(old_allaction,widget->allocation) in widget->window
+	   */
+	  GdkRegion *invalidate = gdk_region_rectangle (&widget->allocation);
+	  gdk_region_union_with_rect (invalidate, &old_allocation);
 
-  if (GTK_IS_RESIZE_CONTAINER (widget))
-    _gtk_container_clear_resize_widgets (GTK_CONTAINER (widget));
+	  gdk_window_invalidate_region (widget->window, invalidate, FALSE);
+	  gdk_region_destroy (invalidate);
+	}
+      
+      if (size_changed)
+	{
+	  if (!GTK_WIDGET_REDRAW_ON_ALLOC (widget))
+	    {
+	      if (GTK_WIDGET_NO_WINDOW (widget) && !position_changed)
+		{
+		  /* Invalidate widget->allocation - old_allocation in widget->window
+		   */
+		  GdkRegion *invalidate = gdk_region_rectangle (&widget->allocation);
+		  GdkRegion *tmp = gdk_region_rectangle (&old_allocation);
+		  
+		  gdk_region_subtract (invalidate, tmp);
+		  gdk_region_destroy (tmp);
+		  
+		  gdk_window_invalidate_region (widget->window, invalidate, FALSE);
+		  gdk_region_destroy (invalidate);
+		}
+	    }
+	  else
+	    {
+	      /* Invalidate union(old_allaction,widget->allocation) in widget->window and descendents owned by widget
+	       */
+	      GdkRegion *invalidate = gdk_region_rectangle (&widget->allocation);
+	      gdk_region_union_with_rect (invalidate, &old_allocation);
 
-  gtk_signal_emit (GTK_OBJECT (widget), widget_signals[SIZE_ALLOCATE], &real_allocation);
+	      invalidate_widget_windows (widget, invalidate);
+	      gdk_region_destroy (invalidate);
+	    }
+	}
+    }
 
-  if (needs_draw)
+  if ((size_changed || position_changed) && widget->parent &&
+      GTK_WIDGET_REALIZED (widget->parent) && GTK_CONTAINER (widget->parent)->reallocate_redraws)
     {
-      gtk_widget_queue_draw (widget);
-      if (widget->parent && GTK_CONTAINER (widget->parent)->reallocate_redraws)
-	gtk_widget_queue_draw (widget->parent);
+      GdkRegion *invalidate = gdk_region_rectangle (&widget->parent->allocation);
+      invalidate_widget_windows (widget->parent, invalidate);
+      gdk_region_destroy (invalidate);
     }
 }
 
@@ -3453,6 +3461,32 @@ gtk_widget_set_double_buffered (GtkWidge
     GTK_WIDGET_SET_FLAGS (widget, GTK_DOUBLE_BUFFERED);
   else
     GTK_WIDGET_UNSET_FLAGS (widget, GTK_DOUBLE_BUFFERED);
+}
+
+/**
+ * gtk_widget_set_redraw_on_allocate:
+ * @widget: a #GtkWidget
+ * @redraw_on_allocate: if %TRUE, the entire widget will be redrawn
+ *   when it is allocated to a new size. Otherwise, only the
+ *   new portion of the widget will be redrawn.
+ *
+ * Sets whether a when a widgets size allocation changes, the entire
+ * widget is queued for drawing, or only the area that has been newly
+ * added to the widget are queued for drawing. By default, this
+ * setting is %TRUE and the entire widget is redrawn on every size
+ * change. If your widget leaves the upper left are unchanged when
+ * made bigger, turning this setting on will improve performance.
+ **/
+void
+gtk_widget_set_redraw_on_allocate (GtkWidget *widget,
+				   gboolean   redraw_on_allocate)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  if (redraw_on_allocate)
+    GTK_PRIVATE_SET_FLAG (widget, GTK_REDRAW_ON_ALLOC);
+  else
+    GTK_PRIVATE_UNSET_FLAG (widget, GTK_REDRAW_ON_ALLOC);
 }
 
 /**
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.158
diff -u -p -r1.158 gtkwindow.c
--- gtk/gtkwindow.c	2001/10/28 21:15:33	1.158
+++ gtk/gtkwindow.c	2001/10/29 23:17:04
@@ -4265,8 +4265,7 @@ gtk_window_move_resize (GtkWindow *windo
       
       /* And run the resize queue.
        */
-      if (container->resize_widgets)
-        gtk_container_resize_children (container);
+      gtk_container_resize_children (container);
     }
 }
 


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