Layout related changes




Over the last week, I've been working to fix a large number
of problems that GtkLayout had. A few of them:

 - To avoid having to deal with some complexities with scrolling
   NO_WINDOW widgets, it tried to create a window to hold
   each such child. However this was very difficult to do
   within the framework of GTK+, and it was mostly broken.

 - Because of the extra windows, NO_WINDOW widgets didn't render
   properly. 

 - GtkLayout did queue resizes as expected, thus causing extra
   size allocations and redraws.

 - Queued redraws where not handled properly within GtkLayouts.

So it seemed that a major revision was in order. I decided
to get rid of the extra windows for NO_WINDOW widgets. This
however, required some changes in GDK and GTK+. 

The change in GDK needed in GTK+ was that to implement the
special resizing technique used for scrolling a GtkLayout
(see http://www.gtk.org/~otaylor/whitepapers/guffaw-scrolling.txt),
subwindow gravity had to be set on all child windows of
the layout window. Because these might not be direct children
of the layout if there were intermediate NO_WINDOW children,
a new mechanism was needed.

This was the addition of "child hooks" for GdkWindows. These
hooks are called every time a child is added to or removed
from a widget. This is considered an experimental facility
and probably should not be used outside of GtkLayout.

The second addition that was needed was a private widget
flag "IS_OFFSCREEN". Because the allocations of widgets
are stored as 16 bit quantities, and we are scrolling
widgets over a larger area, we have to unmap widgets
that go offscreen. To prevent unnecessary redraws being
queued on these offscreen windows as they are mapped
and unmapped, we set the IS_OFFSCREEN flag which disables
redraw queueing on a widget and its descendants.

Regarding GtkLayout, the main externally difference is 
that it in order to have clearing for NO_WINDOW widgets work
properly, it clears its backround itself on 
"draw" and "expose_event" signals, unless the 
APP_PAINTABLE flag is set.

So, GtkLayout based widgets, like the canvas, that paint
their background themeselves, if they want NO_WINDOW
children to work, need to:

 - call gtk_widget_set_app_paintable (widget, TRUE);

 - Make sure they draw some pixel over the complete
   area for a draw signal or expose event.

 - chain to the draw() and expose() handlers of their
   parent class so that the children get drawn
   recursively.

Currently the GnomeCanvas does not chain to the draw()
and expose() functions of GtkLayout. In this condition,
canvas's without widgets will work OK, canvases with
!NO_WINDOW widgets should mostly work OK, and 
canvases with NO_WINDOW widgets won't work. But they
didn't before either.

I do not believe this change breaks either source or binary 
backwards compatibility. Comments and tests of
this patch would be appreciated, especially if you
are doing complicated things with GtkLayouts.

Regards,
                                        Owen

diff -ur gtk+-raw/ChangeLog gtk+-newlayout/ChangeLog
--- gtk+-raw/ChangeLog	Sun Jan 24 17:08:49 1999
+++ gtk+-newlayout/ChangeLog	Sun Jan 24 17:08:18 1999
@@ -1,3 +1,49 @@
+Wed Jan 20 11:19:00 1999  Owen Taylor  <otaylor@redhat.com>
+
+	* gtk/gtklabel.c: Use floor() instead of truncating
+	to integer values so we get translation invariance.
+
+	* gtk/gtklayout.c (gtk_layout_size_allocate): Set upper
+	and lower values for adjustments in size_allocate().
+
+	* gdk/gdkprivate.h gdk/gdkwindow.c gdk/gdk.h gdktypes.h: 
+	Added "child hooks" for notification when a child
+	window is added to or removed from a given 
+	parent window. 
+
+	(Set with new function gdk_window_set_child_hooks()) 
+
+	This capability is somewhat experimental and is meant 
+	solely for the use of gtk/gtklayout.c.
+
+	* gdk/gdkwindow.c (gdk_window_internal_destroy): Set flags
+	indicating destroyed state before cleanup.
+
+	* gtk/gtkprivate.h gtk/gtkwidget.c: Add a new
+	private flag IS_OFFSCREEN. If set, this indicates
+	to GTK+ that the widget is not to be considered
+	viewable regardless of its map state. Queued draws
+	on offscreen widgets are suppressed.
+
+	Added new function static gtk_widget_is_offscreen() to
+	check this flag on a widget and its ancestors.
+
+	* gtk/gtklayout.[ch]: Major revisions.
+
+	- Use child hooks to set static gravity on all child
+	windows, and this avoid having to create a window
+	for NO_WINDOW children.
+
+	- Adjust allocations of children as we scroll them
+	so queued draws work correctly.
+
+	- Don't allocate our children directly in a put()
+	or move(); just queue a resize() like every other
+	widget.
+
+	* gtk/testgtk.c: Make the arrows on the scrollbars
+	work, create a larger and more demanding test.
+	
 Sun Jan 24 12:17:39 1999  Owen Taylor  <otaylor@redhat.com>
 
 	* gdk/gdkcolor.c (gdk_colormap_real_destroy): Fix 
diff -ur gtk+-raw/gdk/gdk.h gtk+-newlayout/gdk/gdk.h
--- gtk+-raw/gdk/gdk.h	Sun Jan 24 17:09:06 1999
+++ gtk+-newlayout/gdk/gdk.h	Wed Jan 20 11:22:11 1999
@@ -228,6 +228,17 @@
 gboolean gdk_window_is_viewable    (GdkWindow *window);
 
 /*
+ * Set up hooks that are called when a child window is added
+ * or removed from given window. This capability is somewhat
+ * experimental and is meant solely for the use of 
+ * gtk/gtklayout.c
+ */
+void gdk_window_set_child_hooks   (GdkWindow     *window,
+				   GdkChildHooks *hooks,
+				   gpointer       data,
+				   GDestroyNotify notify);
+
+/*
  * The following function adds a global filter for all client
  * messages of type message_type
  */
diff -ur gtk+-raw/gdk/gdkprivate.h gtk+-newlayout/gdk/gdkprivate.h
--- gtk+-raw/gdk/gdkprivate.h	Sun Jan 24 17:09:07 1999
+++ gtk+-newlayout/gdk/gdkprivate.h	Wed Jan 20 11:18:50 1999
@@ -70,6 +70,8 @@
   GList *filters;
   GdkColormap *colormap;
   GList *children;
+
+  GdkChildHooks *child_hooks;
 };
 
 struct _GdkImagePrivate
diff -ur gtk+-raw/gdk/gdktypes.h gtk+-newlayout/gdk/gdktypes.h
--- gtk+-raw/gdk/gdktypes.h	Sun Jan 24 17:09:08 1999
+++ gtk+-newlayout/gdk/gdktypes.h	Mon Jan 18 10:26:46 1999
@@ -86,6 +86,8 @@
 typedef struct _GdkDeviceInfo	    GdkDeviceInfo;
 typedef struct _GdkTimeCoord	    GdkTimeCoord;
 typedef struct _GdkRegion	    GdkRegion;
+typedef struct _GdkChildHooks       GdkChildHooks;
+
 typedef void (*GdkEventFunc) (GdkEvent *event,
 			      gpointer	data);
 
@@ -1249,6 +1251,15 @@
   GdkPixmap *status_pixmap;
   GdkColormap *status_colormap;
 };
+
+/* Hooks set on parent window to be informed about child windows
+ */
+
+struct _GdkChildHooks {
+  void (*add_child) (GdkWindow *parent, GdkWindow *child, gpointer data);
+  void (*remove_child) (GdkWindow *parent, GdkWindow *child, gpointer data);
+};
+
 
 #ifdef __cplusplus
 }
diff -ur gtk+-raw/gdk/gdkwindow.c gtk+-newlayout/gdk/gdkwindow.c
--- gtk+-raw/gdk/gdkwindow.c	Sun Jan 24 17:09:09 1999
+++ gtk+-newlayout/gdk/gdkwindow.c	Sun Jan 24 12:34:00 1999
@@ -44,6 +44,14 @@
 #include <X11/extensions/shape.h>
 #endif
 
+typedef struct _GdkChildHooksPrivate GdkChildHooksPrivate;
+
+struct _GdkChildHooksPrivate {
+  GdkChildHooks  hooks;
+  gpointer       data;
+  GDestroyNotify notify;
+};
+
 const int gdk_event_mask_table[20] =
 {
   ExposureMask,
@@ -274,9 +282,6 @@
 
   private->parent = parent;
 
-  if (parent_private)
-    parent_private->children = g_list_prepend (parent_private->children, window);
-
   private->xdisplay = parent_display;
   private->destroyed = FALSE;
   private->mapped = FALSE;
@@ -300,6 +305,7 @@
   private->height = (attributes->height > 1) ? (attributes->height) : (1);
   private->window_type = attributes->window_type;
   private->extension_events = FALSE;
+  private->child_hooks = NULL;
 
   private->filters = NULL;
   private->children = NULL;
@@ -409,6 +415,15 @@
 				  (attributes->cursor) :
 				  NULL));
 
+  if (parent_private)
+    {
+      parent_private->children = g_list_prepend (parent_private->children, window);
+      if (parent_private->child_hooks)
+	parent_private->child_hooks->add_child (parent,
+						window,
+						((GdkChildHooksPrivate *)(parent_private->child_hooks))->data);
+    }
+
   switch (private->window_type)
     {
     case GDK_WINDOW_DIALOG:
@@ -524,6 +539,7 @@
   private->destroyed = FALSE;
   private->mapped = (attrs.map_state != IsUnmapped);
   private->extension_events = 0;
+  private->child_hooks = NULL;
 
   private->colormap = NULL;
 
@@ -568,9 +584,17 @@
     case GDK_WINDOW_FOREIGN:
       if (!private->destroyed)
 	{
+	  private->mapped = FALSE;
+	  private->destroyed = TRUE;
+
 	  if (private->parent)
 	    {
 	      GdkWindowPrivate *parent_private = (GdkWindowPrivate *)private->parent;
+	      if (parent_private->child_hooks)
+		parent_private->child_hooks->remove_child (private->parent,
+							   window,
+							   ((GdkChildHooksPrivate *)parent_private->child_hooks)->data);
+
 	      if (parent_private->children)
 		parent_private->children = g_list_remove (parent_private->children, window);
 	    }
@@ -597,6 +621,16 @@
 	  if (private->extension_events != 0)
 	    gdk_input_window_destroy (window);
 
+	  if (private->child_hooks)
+	    {
+	      GdkChildHooksPrivate *cur_hooks = (GdkChildHooksPrivate *)private->child_hooks;
+	      if (cur_hooks->data && cur_hooks->notify)
+		cur_hooks->notify (cur_hooks->data);
+
+	      g_free (cur_hooks);
+	      private->child_hooks = NULL;
+	    }
+
 	  if (private->filters)
 	    {
 	      tmp = private->filters;
@@ -640,9 +674,6 @@
 
 	  if (private->colormap)
 	    gdk_colormap_unref (private->colormap);
-
-	  private->mapped = FALSE;
-	  private->destroyed = TRUE;
 	}
       break;
 
@@ -876,9 +907,19 @@
   window_private->parent = new_parent;
 
   if (old_parent_private)
-    old_parent_private->children = g_list_remove (old_parent_private->children, window);
-  parent_private->children = g_list_prepend (parent_private->children, window);
+    {
+      old_parent_private->children = g_list_remove (old_parent_private->children, window);
+      if (old_parent_private->child_hooks)
+	old_parent_private->child_hooks->remove_child ((GdkWindow *)old_parent_private,
+						       window,
+						       ((GdkChildHooksPrivate *)old_parent_private->child_hooks)->data);
+    }
   
+  parent_private->children = g_list_prepend (parent_private->children, window);
+  if (parent_private->child_hooks)
+    parent_private->child_hooks->add_child (new_parent,
+					    window,
+					    ((GdkChildHooksPrivate *)(parent_private->child_hooks))->data);
 }
 
 void
@@ -2538,6 +2579,58 @@
 
   return TRUE;
 }
+
+/*************************************************************
+ * gdk_window_set_child_hooks:
+ *     Set up hooks to be called when a child is added or
+ *     removed from the given window.
+ *   arguments:
+ *     window: Parent window
+ *     hooks:  The callback hooks
+ *     data:   Callback data
+ *     notify: Destroy notification for callback data
+ *   results:
+ *************************************************************/
+
+void 
+gdk_window_set_child_hooks   (GdkWindow     *window,
+			      GdkChildHooks *hooks,
+			      gpointer       data,
+			      GDestroyNotify notify)
+{
+  GdkWindowPrivate *private;
+  GdkChildHooksPrivate *cur_hooks;
+  
+  g_return_if_fail (window != NULL);
+
+  private = (GdkWindowPrivate *)window;
+  cur_hooks = (GdkChildHooksPrivate *)private->child_hooks;
+
+  if (cur_hooks)
+    {
+      if (cur_hooks->data && cur_hooks->notify)
+	cur_hooks->notify (cur_hooks->data);
+    }
+  
+  if (hooks)
+    {
+      if (!cur_hooks)
+	{
+	  cur_hooks = g_new (GdkChildHooksPrivate, 1);
+	  private->child_hooks = (GdkChildHooks *)cur_hooks;
+	}
+
+      cur_hooks->hooks = *hooks;
+      cur_hooks->data = data;
+      cur_hooks->notify = notify;
+    }
+  else if (private->child_hooks)
+    {
+      g_free (private->child_hooks);
+      private->child_hooks = NULL;
+    }
+}
+
 
 void          
 gdk_drawable_set_data (GdkDrawable   *drawable,
diff -ur gtk+-raw/gtk/gtklabel.c gtk+-newlayout/gtk/gtklabel.c
--- gtk+-raw/gtk/gtklabel.c	Sun Jan 24 17:09:20 1999
+++ gtk+-newlayout/gtk/gtklabel.c	Sun Jan 24 17:07:39 1999
@@ -15,6 +15,7 @@
  * License along with this library; if not, write to the Free
  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+#include <math.h>
 #include <string.h>
 #include "gtklabel.h"
 #include "gdk/gdkkeysyms.h"
@@ -926,14 +927,14 @@
       gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
       gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
       
-      x = widget->allocation.x + misc->xpad +
-	(widget->allocation.width - label->max_width - 2 * misc->xpad) 
-	* misc->xalign + 0.5;
-      
-      y = (widget->allocation.y
-	   + (widget->allocation.height
-	      - widget->requisition.height) * misc->yalign
-	   + misc->ypad + 0.5);
+      x = floor (widget->allocation.x + misc->xpad +
+		 ((widget->allocation.width - label->max_width - 2 * misc->xpad) 
+		  * misc->xalign) + 0.5);
+      
+      y = floor (widget->allocation.y
+		 + (widget->allocation.height
+		    - widget->requisition.height) * misc->yalign
+		 + misc->ypad + 0.5);
       for (word = label->words; word; word = word->next)
 	{
 	  gchar save = word->beginning[word->length];
diff -ur gtk+-raw/gtk/gtklayout.c gtk+-newlayout/gtk/gtklayout.c
--- gtk+-raw/gtk/gtklayout.c	Sun Jan 24 17:09:20 1999
+++ gtk+-newlayout/gtk/gtklayout.c	Sun Jan 24 17:07:39 1999
@@ -23,8 +23,25 @@
 
 #include "gtklayout.h"
 #include "gtksignal.h"
+#include "gtkprivate.h"
 #include "gdk/gdkx.h"
 
+typedef struct _GtkLayoutAdjData GtkLayoutAdjData;
+typedef struct _GtkLayoutChild   GtkLayoutChild;
+
+struct _GtkLayoutAdjData {
+  gint dx;
+  gint dy;
+};
+
+struct _GtkLayoutChild {
+  GtkWidget *widget;
+  gint x;
+  gint y;
+};
+
+#define IS_ONSCREEN(x,y) ((x >= G_MINSHORT) && (x <= G_MAXSHORT) && \
+                          (y >= G_MINSHORT) && (y <= G_MAXSHORT))
 
 static void     gtk_layout_class_init         (GtkLayoutClass *class);
 static void     gtk_layout_init               (GtkLayout      *layout);
@@ -51,13 +68,20 @@
 					       GtkAdjustment *hadj,
 					       GtkAdjustment *vadj);
 
-static void     gtk_layout_realize_child      (GtkLayout      *layout,
-					       GtkLayoutChild *child);
 static void     gtk_layout_position_child     (GtkLayout      *layout,
-					       GtkLayoutChild *child,
-					       gboolean        force_allocate);
+					       GtkLayoutChild *child);
+static void     gtk_layout_allocate_child     (GtkLayout      *layout,
+					       GtkLayoutChild *child);
 static void     gtk_layout_position_children  (GtkLayout      *layout);
 
+static void     gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
+						       gpointer   cb_data);
+static void     gtk_layout_adjust_allocations         (GtkLayout *layout,
+					               gint       dx,
+						       gint       dy);
+
+
+
 static void     gtk_layout_expose_area        (GtkLayout      *layout,
 					       gint            x, 
 					       gint            y, 
@@ -74,11 +98,25 @@
 
 static gboolean gtk_layout_gravity_works      (void);
 static void     gtk_layout_set_static_gravity (GdkWindow *win,
-					       gboolean   op);
+					       gboolean   is_parent,
+					       gboolean   on);
+
+static void     gtk_layout_add_child_cb    (GdkWindow *parent, 
+					    GdkWindow *child, 
+					    gpointer   data);
+static void     gtk_layout_remove_child_cb (GdkWindow *parent, 
+					    GdkWindow *child, 
+					    gpointer   data);
+
 
 static GtkWidgetClass *parent_class = NULL;
 static gboolean gravity_works;
 
+static GdkChildHooks child_hooks = {
+  gtk_layout_add_child_cb,
+  gtk_layout_remove_child_cb
+};
+
 /* Public interface
  */
   
@@ -209,24 +247,33 @@
   child = g_new (GtkLayoutChild, 1);
 
   child->widget = child_widget;
-  child->window = NULL;
   child->x = x;
   child->y = y;
   child->widget->requisition.width = 0;
   child->widget->requisition.height = 0;
-  child->mapped = FALSE;
 
   layout->children = g_list_append (layout->children, child);
   
   gtk_widget_set_parent (child_widget, GTK_WIDGET (layout));
+  if (GTK_WIDGET_REALIZED (layout))
+    gtk_widget_set_parent_window (child->widget, layout->bin_window);
 
-  gtk_widget_size_request (child->widget, &child->widget->requisition);
-  
-  if (GTK_WIDGET_REALIZED (layout) &&
-      !GTK_WIDGET_REALIZED (child_widget))
-    gtk_layout_realize_child (layout, child);
+  if (!IS_ONSCREEN (x, y))
+    GTK_PRIVATE_SET_FLAG (child_widget, GTK_IS_OFFSCREEN);
 
-  gtk_layout_position_child (layout, child, TRUE);
+  if (GTK_WIDGET_VISIBLE (layout))
+    {
+      if (GTK_WIDGET_REALIZED (layout) &&
+	  !GTK_WIDGET_REALIZED (child_widget))
+	gtk_widget_realize (child_widget);
+      
+      if (GTK_WIDGET_MAPPED (layout) &&
+	  !GTK_WIDGET_MAPPED (child_widget))
+	gtk_widget_map (child_widget);
+    }
+
+  if (GTK_WIDGET_VISIBLE (child_widget) && GTK_WIDGET_VISIBLE (layout))
+    gtk_widget_queue_resize (child_widget);
 }
 
 void           
@@ -245,15 +292,18 @@
   while (tmp_list)
     {
       child = tmp_list->data;
+      tmp_list = tmp_list->next;
+
       if (child->widget == child_widget)
 	{
 	  child->x = x;
 	  child->y = y;
-	  
-	  gtk_layout_position_child (layout, child, TRUE);
+
+	  if (GTK_WIDGET_VISIBLE (child_widget) && GTK_WIDGET_VISIBLE (layout))
+	    gtk_widget_queue_resize (child_widget);
+
 	  return;
 	}
-      tmp_list = tmp_list->next;
     }
 }
 
@@ -417,14 +467,15 @@
 
   attributes.x = 0;
   attributes.y = 0;
-  attributes.event_mask = gtk_widget_get_events (widget);
+  attributes.event_mask = GDK_EXPOSURE_MASK | 
+                          gtk_widget_get_events (widget);
 
   layout->bin_window = gdk_window_new (widget->window,
 					&attributes, attributes_mask);
   gdk_window_set_user_data (layout->bin_window, widget);
 
   if (gravity_works)
-    gtk_layout_set_static_gravity (layout->bin_window, TRUE);
+    gtk_layout_set_static_gravity (layout->bin_window, TRUE, TRUE);
 
   widget->style = gtk_style_attach (widget->style, widget->window);
   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
@@ -433,15 +484,15 @@
   gdk_window_add_filter (widget->window, gtk_layout_main_filter, layout);
   gdk_window_add_filter (layout->bin_window, gtk_layout_filter, layout);
 
+  gdk_window_set_child_hooks (layout->bin_window, &child_hooks, NULL, NULL);
+
   tmp_list = layout->children;
   while (tmp_list)
     {
       GtkLayoutChild *child = tmp_list->data;
-
-      if (GTK_WIDGET_VISIBLE (child->widget))
-	gtk_layout_realize_child (layout, child);
-	
       tmp_list = tmp_list->next;
+
+      gtk_widget_set_parent_window (child->widget, layout->bin_window);
     }
 }
 
@@ -462,17 +513,14 @@
   while (tmp_list)
     {
       GtkLayoutChild *child = tmp_list->data;
+      tmp_list = tmp_list->next;
 
-      if (child->mapped && GTK_WIDGET_VISIBLE (child->widget))
+      if (GTK_WIDGET_VISIBLE (child->widget))
 	{
-	  if (!GTK_WIDGET_MAPPED (child->widget))
+	  if (!GTK_WIDGET_MAPPED (child->widget) && 
+	      !GTK_WIDGET_IS_OFFSCREEN (child->widget))
 	    gtk_widget_map (child->widget);
-	  
-	  if (child->window)
-	    gdk_window_show (child->window);
 	}
-
-      tmp_list = tmp_list->next;
     }
 
   gdk_window_show (layout->bin_window);
@@ -482,7 +530,6 @@
 static void 
 gtk_layout_unrealize (GtkWidget *widget)
 {
-  GList *tmp_list;
   GtkLayout *layout;
 
   g_return_if_fail (widget != NULL);
@@ -490,41 +537,14 @@
 
   layout = GTK_LAYOUT (widget);
 
-  tmp_list = layout->children;
-
   gdk_window_set_user_data (layout->bin_window, NULL);
   gdk_window_destroy (layout->bin_window);
   layout->bin_window = NULL;
 
-  while (tmp_list)
-    {
-      GtkLayoutChild *child = tmp_list->data;
-
-      if (child->window)
-	{
-	  gdk_window_set_user_data (child->window, NULL);
-	  gdk_window_destroy (child->window);
-	  child->window = NULL;
-	}
-	
-      tmp_list = tmp_list->next;
-    }
-
   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
 }
 
-static void 
-gtk_layout_draw (GtkWidget *widget, GdkRectangle *area)
-{
-  GtkLayout *layout;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_LAYOUT (widget));
-
-  layout = GTK_LAYOUT (widget);
-}
-
 static void     
 gtk_layout_size_request (GtkWidget     *widget,
 			 GtkRequisition *requisition)
@@ -545,9 +565,9 @@
   while (tmp_list)
     {
       GtkLayoutChild *child = tmp_list->data;
-      gtk_widget_size_request (child->widget, &child->widget->requisition);
-      
       tmp_list = tmp_list->next;
+
+      gtk_widget_size_request (child->widget, &child->widget->requisition);
     }
 }
 
@@ -570,9 +590,10 @@
   while (tmp_list)
     {
       GtkLayoutChild *child = tmp_list->data;
-      gtk_layout_position_child (layout, child, TRUE);
-      
       tmp_list = tmp_list->next;
+
+      gtk_layout_position_child (layout, child);
+      gtk_layout_allocate_child (layout, child);
     }
 
   if (GTK_WIDGET_REALIZED (widget))
@@ -587,36 +608,73 @@
 
   layout->hadjustment->page_size = allocation->width;
   layout->hadjustment->page_increment = allocation->width / 2;
+  layout->hadjustment->lower = 0;
+  layout->hadjustment->upper = layout->width;
   gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
 
   layout->vadjustment->page_size = allocation->height;
   layout->vadjustment->page_increment = allocation->height / 2;
+  layout->vadjustment->lower = 0;
+  layout->vadjustment->upper = layout->height;
   gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
 }
 
+static void 
+gtk_layout_draw (GtkWidget *widget, GdkRectangle *area)
+{
+  GList *tmp_list;
+  GtkLayout *layout;
+  GdkRectangle child_area;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_LAYOUT (widget));
+
+  layout = GTK_LAYOUT (widget);
+
+  /* We don't have any way of telling themes about this properly,
+   * so we just assume a background pixmap
+   */
+  if (!GTK_WIDGET_APP_PAINTABLE (widget))
+    gdk_window_clear_area (layout->bin_window,
+			   area->x, area->y, area->width, area->height);
+  
+  tmp_list = layout->children;
+  while (tmp_list)
+    {
+      GtkLayoutChild *child = tmp_list->data;
+      tmp_list = tmp_list->next;
+
+      if (gtk_widget_intersect (child->widget, area, &child_area))
+	gtk_widget_draw (child->widget, &child_area);
+    }
+}
+
 static gint 
 gtk_layout_expose (GtkWidget *widget, GdkEventExpose *event)
 {
   GList *tmp_list;
   GtkLayout *layout;
+  GdkEventExpose child_event;
 
   g_return_val_if_fail (widget != NULL, FALSE);
   g_return_val_if_fail (GTK_IS_LAYOUT (widget), FALSE);
 
   layout = GTK_LAYOUT (widget);
 
-  if (event->window == layout->bin_window)
+  if (event->window != layout->bin_window)
     return FALSE;
   
   tmp_list = layout->children;
   while (tmp_list)
     {
       GtkLayoutChild *child = tmp_list->data;
-
-      if (event->window == child->window)
-	return gtk_widget_event (child->widget, (GdkEvent *)event);
-	
       tmp_list = tmp_list->next;
+
+      child_event = *event;
+      if (GTK_WIDGET_DRAWABLE (child->widget) &&
+	  GTK_WIDGET_NO_WINDOW (child->widget) &&
+	  gtk_widget_intersect (child->widget, &event->area, &child_event.area))
+	gtk_widget_event (child->widget, (GdkEvent*) &child_event);
     }
 
   return FALSE;
@@ -648,21 +706,14 @@
 
   if (tmp_list)
     {
-      if (child->window)
-	{
-	  /* FIXME: This will cause problems for reparenting NO_WINDOW
-	   * widgets out of a GtkLayout
-	   */
-	  gdk_window_set_user_data (child->window, NULL);
-	  gdk_window_destroy (child->window);
-	}
-
       gtk_widget_unparent (widget);
 
       layout->children = g_list_remove_link (layout->children, tmp_list);
       g_list_free_1 (tmp_list);
       g_free (child);
     }
+
+  GTK_PRIVATE_UNSET_FLAG (widget, GTK_IS_OFFSCREEN);
 }
 
 static void
@@ -695,140 +746,132 @@
  */
 
 static void
-gtk_layout_realize_child (GtkLayout *layout,
-			  GtkLayoutChild *child)
+gtk_layout_position_child (GtkLayout      *layout,
+			   GtkLayoutChild *child)
 {
-  GtkWidget *widget;
-  gint attributes_mask;
+  gint x;
+  gint y;
 
-  widget = GTK_WIDGET (layout);
-  
-  if (GTK_WIDGET_NO_WINDOW (child->widget))
+  x = child->x - layout->xoffset;
+  y = child->y - layout->yoffset;
+
+  if (IS_ONSCREEN (x,y))
     {
-      GdkWindowAttr attributes;
-      
-      gint x = child->x - layout->xoffset;
-      gint y = child->y - layout->xoffset;
+      if (GTK_WIDGET_MAPPED (layout) &&
+	  GTK_WIDGET_VISIBLE (child->widget))
+	{
+	  if (!GTK_WIDGET_MAPPED (child->widget))
+	    gtk_widget_map (child->widget);
+	}
 
-      attributes.window_type = GDK_WINDOW_CHILD;
-      attributes.x = x;
-      attributes.y = y;
-      attributes.width = child->widget->requisition.width;
-      attributes.height = child->widget->requisition.height;
-      attributes.wclass = GDK_INPUT_OUTPUT;
-      attributes.visual = gtk_widget_get_visual (widget);
-      attributes.colormap = gtk_widget_get_colormap (widget);
-      attributes.event_mask = GDK_EXPOSURE_MASK;
- 
-      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-      child->window =  gdk_window_new (layout->bin_window,
- 				       &attributes, attributes_mask);
-      gdk_window_set_user_data (child->window, widget);
+      if (GTK_WIDGET_IS_OFFSCREEN (child->widget))
+	GTK_PRIVATE_UNSET_FLAG (child->widget, GTK_IS_OFFSCREEN);
+    }
+  else
+    {
+      if (!GTK_WIDGET_IS_OFFSCREEN (child->widget))
+	GTK_PRIVATE_SET_FLAG (child->widget, GTK_IS_OFFSCREEN);
 
-      if (child->window)
-	gtk_style_set_background (widget->style, 
-				  child->window, 
-				  GTK_STATE_NORMAL);
+      if (GTK_WIDGET_MAPPED (child->widget))
+	gtk_widget_unmap (child->widget);
     }
+}
 
-  gtk_widget_set_parent_window (child->widget,
-				child->window ? child->window : layout->bin_window);
-  
-  gtk_widget_realize (child->widget);
+static void
+gtk_layout_allocate_child (GtkLayout      *layout,
+			   GtkLayoutChild *child)
+{
+  GtkAllocation allocation;
+
+  allocation.x = child->x - layout->xoffset;
+  allocation.y = child->y - layout->yoffset;
+  allocation.width = child->widget->requisition.width;
+  allocation.height = child->widget->requisition.height;
   
-  if (gravity_works)
-    gtk_layout_set_static_gravity (child->window ? child->window : child->widget->window, TRUE);
+  gtk_widget_size_allocate (child->widget, &allocation);
 }
 
 static void
-gtk_layout_position_child (GtkLayout      *layout,
-			   GtkLayoutChild *child,
-			   gboolean        force_allocate)
+gtk_layout_position_children (GtkLayout *layout)
 {
-  gint x;
-  gint y;
+  GList *tmp_list;
 
-  x = child->x - layout->xoffset;
-  y = child->y - layout->yoffset;
-  
-  if ((x >= G_MINSHORT) && (x <= G_MAXSHORT) &&
-      (y >= G_MINSHORT) && (y <= G_MAXSHORT))
+  tmp_list = layout->children;
+  while (tmp_list)
     {
-      if (!child->mapped)
-	{
-	  child->mapped = TRUE;
-
-	  if (GTK_WIDGET_MAPPED (layout) &&
-	      GTK_WIDGET_VISIBLE (child->widget))
-	    {
-	      if (child->window)
-		gdk_window_show (child->window);
-	      if (!GTK_WIDGET_MAPPED (child->widget))
-		gtk_widget_map (child->widget);
-	      
-	      child->mapped = TRUE;
-	      force_allocate = TRUE;
-	    }
-	}
+      GtkLayoutChild *child = tmp_list->data;
+      tmp_list = tmp_list->next;
       
-      if (force_allocate)
-	{
-	  GtkAllocation allocation;
+      gtk_layout_position_child (layout, child);
+    }
+}
 
-	  if (GTK_WIDGET_NO_WINDOW (child->widget))
-	    {
-	      if (child->window)
-		{
-		  gdk_window_move_resize (child->window,
-					  x, y, 
-					  child->widget->requisition.width,
-					  child->widget->requisition.height);
-		}
+static void
+gtk_layout_adjust_allocations_recurse (GtkWidget *widget,
+				       gpointer   cb_data)
+{
+  GtkLayoutAdjData *data = cb_data;
 
-	      allocation.x = 0;
-	      allocation.y = 0;
-	    }
-	  else
-	    {
-	      allocation.x = x;
-	      allocation.y = y;
-	    }
+  widget->allocation.x += data->dx;
+  widget->allocation.y += data->dy;
 
-	  allocation.width = child->widget->requisition.width;
-	  allocation.height = child->widget->requisition.height;
-	  
-	  gtk_widget_size_allocate (child->widget, &allocation);
-	}
-    }
-  else
-    {
-      if (child->mapped)
-	{
-	  child->mapped = FALSE;
-	  if (child->window)
-	    gdk_window_hide (child->window);
-	  else if (GTK_WIDGET_MAPPED (child->widget))
-	    gtk_widget_unmap (child->widget);
-	}
-    }
+  if (GTK_WIDGET_NO_WINDOW (widget) &&
+      GTK_IS_CONTAINER (widget))
+    gtk_container_forall (GTK_CONTAINER (widget), 
+			  gtk_layout_adjust_allocations_recurse,
+			  cb_data);
 }
 
 static void
-gtk_layout_position_children (GtkLayout *layout)
+gtk_layout_adjust_allocations (GtkLayout *layout,
+			       gint       dx,
+			       gint       dy)
 {
   GList *tmp_list;
+  GtkLayoutAdjData data;
+
+  data.dx = dx;
+  data.dy = dy;
 
   tmp_list = layout->children;
   while (tmp_list)
     {
-      gtk_layout_position_child (layout, tmp_list->data, FALSE);
-
+      GtkLayoutChild *child = tmp_list->data;
       tmp_list = tmp_list->next;
+      
+      child->widget->allocation.x += dx;
+      child->widget->allocation.y += dy;
+
+      if (GTK_WIDGET_NO_WINDOW (child->widget) &&
+	  GTK_IS_CONTAINER (child->widget))
+	gtk_container_forall (GTK_CONTAINER (child->widget), 
+			      gtk_layout_adjust_allocations_recurse,
+			      &data);
     }
 }
   
 /* Callbacks */
 
+/* Ensure that the children have the correct gravity
+ */
+static void 
+gtk_layout_add_child_cb (GdkWindow *parent, 
+			 GdkWindow *child, 
+			 gpointer   data)
+{
+  if (gravity_works)
+    gtk_layout_set_static_gravity (child, FALSE, TRUE);
+}
+
+static void 
+gtk_layout_remove_child_cb (GdkWindow *parent, 
+			    GdkWindow *child, 
+			    gpointer   data)
+{
+  if (gravity_works && !((GdkWindowPrivate *)child)->destroyed)
+    gtk_layout_set_static_gravity (child, FALSE, FALSE);    
+}
+
 /* Send a synthetic expose event to the widget
  */
 static void
@@ -926,6 +969,8 @@
       return;
     }
 
+  gtk_layout_adjust_allocations (layout, -dx, -dy);
+
   if (dx > 0)
     {
       if (gravity_works)
@@ -1111,7 +1156,7 @@
       break;
       
     case ConfigureNotify:
-      if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
+       if ((xevent->xconfigure.x != 0) || (xevent->xconfigure.y != 0))
 	{
 	  layout->configure_serial = xevent->xconfigure.serial;
 	  layout->scroll_x = xevent->xconfigure.x;
@@ -1167,17 +1212,27 @@
  * we don't have to use Xlib here.
  */
 static void
-gtk_layout_set_static_gravity (GdkWindow *win, gboolean on)
+gtk_layout_set_static_gravity (GdkWindow *win,
+			       gboolean   is_parent,
+			       gboolean   on)
 {
   XSetWindowAttributes xattributes;
+  gulong value_mask = 0;
+
+  if (is_parent)
+    {
+      xattributes.bit_gravity = on ? StaticGravity : ForgetGravity;
+      value_mask |= CWBitGravity;
+    }
+  else
+    {
+      xattributes.win_gravity = on ? StaticGravity : NorthWestGravity;
+      value_mask |= CWWinGravity;
+    }
 
-  xattributes.win_gravity = on ? StaticGravity : NorthWestGravity;
-  xattributes.bit_gravity = on ? StaticGravity : NorthWestGravity;
-  
   XChangeWindowAttributes (GDK_WINDOW_XDISPLAY (win),
 			   GDK_WINDOW_XWINDOW (win),
-			   CWBitGravity | CWWinGravity,
-			   &xattributes);
+			   value_mask,  &xattributes);
 }
 
 static gboolean
@@ -1209,8 +1264,8 @@
   attr.window_type = GDK_WINDOW_CHILD;
   child = gdk_window_new (parent, &attr, GDK_WA_X | GDK_WA_Y);
 
-  gtk_layout_set_static_gravity (parent, TRUE);
-  gtk_layout_set_static_gravity (child, TRUE);
+  gtk_layout_set_static_gravity (parent, TRUE, TRUE);
+  gtk_layout_set_static_gravity (child, FALSE, TRUE);
 
   gdk_window_resize (parent, 100, 110);
   gdk_window_move (parent, 0, -10);
diff -ur gtk+-raw/gtk/gtklayout.h gtk+-newlayout/gtk/gtklayout.h
--- gtk+-raw/gtk/gtklayout.h	Sun Jan 24 17:09:20 1999
+++ gtk+-newlayout/gtk/gtklayout.h	Wed Jan 20 10:30:17 1999
@@ -40,15 +40,6 @@
 
 typedef struct _GtkLayout        GtkLayout;
 typedef struct _GtkLayoutClass   GtkLayoutClass;
-typedef struct _GtkLayoutChild   GtkLayoutChild;
-
-struct _GtkLayoutChild {
-  GtkWidget *widget;
-  GdkWindow *window;	/* For NO_WINDOW widgets */
-  gint x;
-  gint y;
-  gboolean mapped : 1;
-};
 
 struct _GtkLayout {
   GtkContainer container;
@@ -99,19 +90,23 @@
 			                   guint          width,
 			                   guint          height);
 
-/* These disable and enable moving and repainting the scrolling window of the GtkLayout,
- * respectively.  If you want to update the layout's offsets but do not want it to
- * repaint itself, you should use these functions.
- */
-void           gtk_layout_freeze          (GtkLayout     *layout);
-void           gtk_layout_thaw            (GtkLayout     *layout);
-
 GtkAdjustment* gtk_layout_get_hadjustment (GtkLayout     *layout);
 GtkAdjustment* gtk_layout_get_vadjustment (GtkLayout     *layout);
 void           gtk_layout_set_hadjustment (GtkLayout     *layout,
 					   GtkAdjustment *adjustment);
 void           gtk_layout_set_vadjustment (GtkLayout     *layout,
 					   GtkAdjustment *adjustment);
+
+/* These disable and enable moving and repainting the scrolling window
+ * of the GtkLayout, respectively.  If you want to update the layout's
+ * offsets but do not want it to repaint itself, you should use these
+ * functions.
+ *
+ * - I don't understand these are supposed to work, so I suspect
+ * - they don't now.                    OWT 1/20/98
+ */
+void           gtk_layout_freeze          (GtkLayout     *layout);
+void           gtk_layout_thaw            (GtkLayout     *layout);
 
 #ifdef __cplusplus
 }
diff -ur gtk+-raw/gtk/gtkprivate.h gtk+-newlayout/gtk/gtkprivate.h
--- gtk+-raw/gtk/gtkprivate.h	Sun Jan 24 17:09:23 1999
+++ gtk+-newlayout/gtk/gtkprivate.h	Tue Jan 19 14:28:35 1999
@@ -39,7 +39,8 @@
   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_IN_REPARENT       = 1 <<  6,
+  PRIVATE_GTK_IS_OFFSCREEN      = 1 <<  7
 } GtkPrivateFlags;
 
 /* Macros for extracting a widgets private_flags from GtkWidget.
@@ -52,6 +53,7 @@
 #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)
+#define GTK_WIDGET_IS_OFFSCREEN(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_IS_OFFSCREEN) != 0)
 
 /* Macros for setting and clearing private widget flags.
  * we use a preprocessor string concatenation here for a clear
diff -ur gtk+-raw/gtk/gtkwidget.c gtk+-newlayout/gtk/gtkwidget.c
--- gtk+-raw/gtk/gtkwidget.c	Sun Jan 24 17:09:28 1999
+++ gtk+-newlayout/gtk/gtkwidget.c	Thu Jan 21 19:00:10 1999
@@ -171,6 +171,8 @@
 static void gtk_widget_set_style_recurse	 (GtkWidget	*widget,
 						  gpointer	 client_data);
 
+static gboolean gtk_widget_is_offscreen           (GtkWidget     *widget);
+
 static GtkWidgetAuxInfo* gtk_widget_aux_info_new     (void);
 static void		 gtk_widget_aux_info_destroy (GtkWidgetAuxInfo *aux_info);
 
@@ -1797,7 +1799,8 @@
   g_return_if_fail (widget != NULL);
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  if (widget->window && gdk_window_is_viewable (widget->window))
+  if (widget->window && gdk_window_is_viewable (widget->window) &&
+      !gtk_widget_is_offscreen (widget))
     gtk_widget_queue_draw_data (widget, x, y, width, height, NULL);
 }
 
@@ -1807,7 +1810,8 @@
   g_return_if_fail (widget != NULL);
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  if (widget->window && gdk_window_is_viewable (widget->window))
+  if (widget->window && gdk_window_is_viewable (widget->window) &&
+      !gtk_widget_is_offscreen (widget))
     gtk_widget_queue_draw_data (widget, 0, 0, -1, -1, NULL);
 }
 
@@ -1823,7 +1827,8 @@
   g_return_if_fail (widget != NULL);
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  if (!(widget->window && gdk_window_is_viewable (widget->window)))
+  if (!(widget->window && gdk_window_is_viewable (widget->window)) ||
+      gtk_widget_is_offscreen (widget))
     return;
 
   /* Find the correct widget */
@@ -4529,6 +4534,29 @@
 	}
       gtk_widget_unref (widget);
     }
+}
+
+/*************************************************************
+ * gtk_widget_is_offscreen:
+ *     Check if a widget is "offscreen"
+ *   arguments:
+ *     widget: a widget
+ *   results:
+ *     TRUE if the widget or any of ancestors has the
+ *     PRIVATE_GTK_WIDGET_IS_OFFSCREEN set.
+ *************************************************************/
+
+static gboolean 
+gtk_widget_is_offscreen (GtkWidget *widget)
+{
+  while (widget)
+    {
+      if (GTK_WIDGET_IS_OFFSCREEN (widget))
+	return TRUE;
+      widget = widget->parent;
+    }
+
+  return FALSE;
 }
 
 /*****************************************
diff -ur gtk+-raw/gtk/testgtk.c gtk+-newlayout/gtk/testgtk.c
--- gtk+-raw/gtk/testgtk.c	Sun Jan 24 17:09:32 1999
+++ gtk+-newlayout/gtk/testgtk.c	Wed Jan 20 10:47:46 1999
@@ -8284,12 +8284,18 @@
       
       layout = gtk_layout_new (NULL, NULL);
       gtk_container_add (GTK_CONTAINER (scrolledwindow), layout);
+
+      /* We set step sizes here since GtkLayout does not set
+       * them itself.
+       */
+      GTK_LAYOUT (layout)->hadjustment->step_increment = 10.0;
+      GTK_LAYOUT (layout)->vadjustment->step_increment = 10.0;
       
       gtk_widget_set_events (layout, GDK_EXPOSURE_MASK);
       gtk_signal_connect (GTK_OBJECT (layout), "expose_event",
 			  GTK_SIGNAL_FUNC (layout_expose_handler), NULL);
       
-      gtk_layout_set_size (GTK_LAYOUT (layout), 1600, 64000);
+      gtk_layout_set_size (GTK_LAYOUT (layout), 1600, 128000);
       
       for (i=0 ; i < 16 ; i++)
 	for (j=0 ; j < 16 ; j++)
@@ -8304,7 +8310,7 @@
 			    j*100, i*100);
 	  }
 
-      for (i=16; i < 640; i++)
+      for (i=16; i < 1280; i++)
 	{
 	  sprintf(buf, "Button %d, %d", i, 0);
 	  if (i % 2)



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