Hey Chris,

Attached is an updated patch that basically incorporates all the things
you mentioned in your last mail:

* Determines the visible rectangle, and intersects it with the required
area (this is, we copy only the visible area).
        - First by determining the visible area based on its size and its
parents.
        - Second, by looking for toplevel windows that intersect the window,
and excluding that area.

* Then compute the destination rectangle, by applying the
XAmount/YAmount values and intersecting it with the area passed to
ScrollWindow (thus we clip it as needed). This is the way Gdk does it.

* Finally, exclude the destination rectangle from the total requested
area, and pass it to AddExpose method.

With this patch everyting is working as expected, but I have some
issues:

* Region needs a Graphics instance to return its Bounds, which *could*
be expensive.
* After taking a look at the way scrolling is done in Gdk, it *seems*
that the Region implementation lack some of the functionality we need
(like, returning in a simple operation the Rectangles describing the
region).

Comments?

Carlos.
Index: XplatUIX11.cs
===================================================================
--- XplatUIX11.cs	(revisión: 93328)
+++ XplatUIX11.cs	(copia de trabajo)
@@ -4791,48 +4791,21 @@
 
 			gc = XCreateGC(DisplayHandle, hwnd.client_window, IntPtr.Zero, ref gc_values);
 
-			int src_x, src_y;
-			int dest_x, dest_y;
-			int width, height;
+			Rectangle visible_rect = GetTotalVisibleArea (hwnd.client_window);
+			visible_rect.Intersect (area);
 
-			if (YAmount > 0) {
-				src_y = area.Y;
-				height = area.Height - YAmount;
-				dest_y = area.Y + YAmount;
-			}
-			else {
-				src_y = area.Y - YAmount;
-				height = area.Height + YAmount;
-				dest_y = area.Y;
-			}
+			Rectangle dest_rect = visible_rect;
+			dest_rect.Y += YAmount;
+			dest_rect.X += XAmount;
+			dest_rect.Intersect (area);
 
-			if (XAmount > 0) {
-				src_x = area.X;
-				width = area.Width - XAmount;
-				dest_x = area.X + XAmount;
-			}
-			else {
-				src_x = area.X - XAmount;
-				width = area.Width + XAmount;
-				dest_x = area.X;
-			}
+			Point src = new Point (dest_rect.X - XAmount, dest_rect.Y - YAmount);
+			XCopyArea (DisplayHandle, hwnd.client_window, hwnd.client_window, gc, src.X, src.Y, 
+					dest_rect.Width, dest_rect.Height, dest_rect.X, dest_rect.Y);
 
-			XCopyArea(DisplayHandle, hwnd.client_window, hwnd.client_window, gc, src_x, src_y, width, height, dest_x, dest_y);
+			Rectangle dirty_area = GetDirtyArea (area, dest_rect, XAmount, YAmount);
+			AddExpose (hwnd, true, dirty_area.X, dirty_area.Y, dirty_area.Width, dirty_area.Height);
 
-			// Generate an expose for the area exposed by the horizontal scroll
-			// We don't use AddExpose since we're 
-			if (XAmount > 0) {
-				AddExpose(hwnd, true, area.X, area.Y, XAmount, area.Height);
-			} else if (XAmount < 0) {
-				AddExpose(hwnd, true, XAmount + area.X + area.Width, area.Y, -XAmount, area.Height);
-			}
-
-			// Generate an expose for the area exposed by the vertical scroll
-			if (YAmount > 0) {
-				AddExpose(hwnd, true, area.X, area.Y, area.Width, YAmount);
-			} else if (YAmount < 0) {
-				AddExpose(hwnd, true, area.X, YAmount + area.Y + area.Height, area.Width, -YAmount);
-			}
 			XFreeGC(DisplayHandle, gc);
 		}
 
@@ -4848,6 +4821,127 @@
 			ScrollWindow(handle, rect, XAmount, YAmount, with_children);
 		}
 
+		Rectangle GetDirtyArea (Rectangle total_area, Rectangle valid_area, int XAmount, int YAmount)
+		{
+			Rectangle dirty_area = total_area;
+
+			if (YAmount > 0)
+				dirty_area.Height = total_area.Height - valid_area.Height;
+			else if (YAmount < 0) {
+				dirty_area.Height = total_area.Height - valid_area.Height;
+				dirty_area.Y = total_area.Y + valid_area.Height;
+			}
+
+			if (XAmount > 0)
+				dirty_area.Width -= valid_area.Width;
+			else if (XAmount < 0) {
+				dirty_area.Width -= valid_area.Width;
+				dirty_area.X += valid_area.Width;
+			}
+
+			return dirty_area;
+		}
+
+		Rectangle GetTotalVisibleArea (IntPtr handle)
+		{
+			Control c = Control.FromHandle (handle);
+
+			Rectangle visible_area = c.ClientRectangle;
+			visible_area.Location = c.PointToScreen (Point.Empty);
+
+			for (Control parent = c.Parent; parent != null; parent = parent.Parent) {
+				Rectangle r = parent.ClientRectangle;
+				r.Location = parent.PointToScreen (Point.Empty);
+
+				visible_area.Intersect (r);
+			}
+
+			// If region is null, the entire area is visible.
+			// Get the area not obscured otherwise.
+			Region visible_region = GetVisibleRegion (c, visible_area);
+			if (visible_region != null) {
+				Graphics g = c.CreateGraphics ();
+				RectangleF rectf = visible_region.GetBounds (g);
+				visible_area = new Rectangle ((int) rectf.X, (int) rectf.Y,
+						(int) rectf.Width, (int) rectf.Height);
+
+				visible_region.Dispose ();
+				g.Dispose ();
+			}
+
+			visible_area.Location = c.PointToClient (visible_area.Location);
+			return visible_area;
+		}
+
+		// Obscured area by other toplevel windows
+		Region GetVisibleRegion (Control c, Rectangle visible_area)
+		{
+			Region visible_region = null;
+			IntPtr Root;
+			IntPtr Parent;
+			IntPtr Children;
+			int ChildCount;
+
+			Hwnd hwnd = Hwnd.GetObjectFromWindow (c.FindForm ().Handle);
+			IntPtr form_handle = hwnd.whole_window;
+
+			lock (XlibLock) {
+				XQueryTree (DisplayHandle, RootWindow, out Root, out Parent, out Children, out ChildCount);
+			}
+
+			int intptr_size = Marshal.SizeOf (typeof (int));
+			bool above = false;
+
+			for (int i = 0; i < ChildCount; i++) {
+				IntPtr window = new IntPtr (Marshal.ReadInt32 (Children, i * intptr_size));
+
+				XWindowAttributes win_attrs = new XWindowAttributes ();
+				lock (XlibLock) {
+					XGetWindowAttributes (DisplayHandle, window, ref win_attrs);
+				}
+
+				Rectangle win_area = new Rectangle (win_attrs.x, win_attrs.y, 
+						win_attrs.width, win_attrs.height);
+
+				if (win_attrs.map_state != MapState.IsViewable || !win_area.IntersectsWith (visible_area))
+					continue;
+
+				IntPtr SubChildren;
+				int SubChildCount;
+
+				if (above) {
+					if (visible_region == null)
+						visible_region = new Region (visible_area);
+
+					visible_region.Exclude (win_area);
+					continue;
+				}
+
+				lock (XlibLock) {
+					XQueryTree (DisplayHandle, window, out Root, out Parent, out SubChildren, out SubChildCount);
+				}
+
+				if (SubChildren != IntPtr.Zero) {
+					IntPtr sub_win = new IntPtr (Marshal.ReadInt32 (SubChildren));
+
+					lock (XlibLock) {
+						XFree (SubChildren);
+					}
+
+					if (sub_win == form_handle)
+						above = true;
+				}
+			}
+
+			if (Children != IntPtr.Zero) {
+				lock (XlibLock) {
+					XFree (Children);
+				}
+			}
+
+			return visible_region;
+		}
+
 		internal override void SendAsyncMethod (AsyncMethodData method) {
 			Hwnd	hwnd;
 			XEvent	xevent = new XEvent ();
_______________________________________________
Mono-winforms-list maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-winforms-list

Reply via email to