From 80bbd2bc6dedc72a2f69f46bd38afa499fc349c7 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Mon, 11 Aug 2014 03:06:55 +0000 Subject: Further refinement of scrolling; addresses artifacts in scrolling complex interfaces on OS X --- macosx/tkMacOSXDraw.c | 95 +++++++++++++++++++++++++++++---------------- macosx/tkMacOSXSubwindows.c | 14 +++---- 2 files changed, 66 insertions(+), 43 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 089097e..6ec3e59 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1458,12 +1458,12 @@ XMaxRequestSize( * a damage region. * * Results: - * Returns 0 if the scroll generated no additional damage. + * Returns 0 if the scroll genereated no additional damage. * Otherwise, sets the region that needs to be repainted after * scrolling and returns 1. * * Side effects: - * Scrolls the rectangle in the window. + * Scrolls the bits in the window. * *---------------------------------------------------------------------- */ @@ -1480,59 +1480,86 @@ TkScrollWindow( Drawable drawable = Tk_WindowId(tkwin); MacDrawable *macDraw = (MacDrawable *) drawable; NSView *view = TkMacOSXDrawableView(macDraw); - CGRect dmgRect, dstRect, visRectBL, srcRectBL, dstRectBL; - NSRect visible, frame; - HIShapeRef dmgRgn = NULL, dstRgn; + CGRect visRect, srcRect, dstRect, scroll_src, scroll_dst; + HIShapeRef dmgRgn = NULL; + NSRect bounds; int result; if ( view ) { - visible = [view visibleRect]; - - /* Find the frame for the Tk window within its NSview. */ - frame = NSMakeRect(Tk_X(tkwin), - Tk_Y(tkwin) + visible.size.height - Tk_Height(tkwin), - Tk_Width(tkwin), - Tk_Height(tkwin)); - /* Get the scroll area in NSView coordinates (origin at bottom left). */ - srcRectBL = CGRectMake(frame.origin.x + x, - frame.origin.y + frame.size.height - height - y, + bounds = [view bounds]; + scroll_src = CGRectMake( + macDraw->xOff + x, + bounds.size.height - height - (macDraw->yOff + y), width, height); - dstRectBL = CGRectOffset(srcRectBL, dx, -dy); - + scroll_dst = CGRectOffset(scroll_src, dx, -dy); /* Limit scrolling to the window content area. */ - visRectBL = NSRectToCGRect(frame); - srcRectBL = CGRectIntersection(srcRectBL, visRectBL); - dstRectBL = CGRectIntersection(dstRectBL, visRectBL); + visRect = NSRectToCGRect([view visibleRect]); + scroll_src = CGRectIntersection(scroll_src, visRect); + scroll_dst = CGRectIntersection(scroll_dst, visRect); - if ( !CGRectIsEmpty(srcRectBL) && !CGRectIsEmpty(dstRectBL) ) { + if ( !CGRectIsEmpty(scroll_src) && !CGRectIsEmpty(scroll_dst) ) { /* - * Construct the damage region. We extend it to the top - * or bottom of the window, in order to avoid artifacts when - * scrolling. + * Mark the difference between source and destination as damaged. + * This region is described in the Tk coordinate system, after shifting by dy. */ - if ( dy < 0 ) { /* extend to bottom */ - dmgRect = CGRectMake(x, y, Tk_Width(tkwin), Tk_Height(tkwin) - y); - } else { /* extend to top */ - dmgRect = CGRectMake(x , 0, Tk_Width(tkwin), Tk_Height(tkwin) + y); - } - dstRect = CGRectMake(x+dx, y+dy, width, height); - - dmgRgn = HIShapeCreateMutableWithRect(&dmgRect); - dstRgn = HIShapeCreateWithRect(&dstRect); + srcRect = CGRectMake(x - macDraw->xOff, y - macDraw->yOff, + width, height); + dstRect = CGRectOffset(srcRect, dx, dy); + dmgRgn = HIShapeCreateMutableWithRect(&srcRect); + HIShapeRef dstRgn = HIShapeCreateWithRect(&dstRect); ChkErr(HIShapeDifference, dmgRgn, dstRgn, (HIMutableShapeRef) dmgRgn); CFRelease(dstRgn); /* Scroll the rectangle. */ - [view scrollRect:NSRectFromCGRect(srcRectBL) by:NSMakeSize(dx, -dy)]; + [view scrollRect:NSRectFromCGRect(scroll_src) by:NSMakeSize(dx, -dy)]; + [view displayRect:NSRectFromCGRect(scroll_dst)]; + + /* + * When a Text widget contains embedded images, scrolling generates + * lots of artifacts involving multiple copies of the images + * displayed on top of each other. Extensive experimentation, with + * very little help from the Apple documentation, indicates that + * whenever an image is displayed it gets added as a subview, which + * then gets automatically redisplayed in its original location. + * + * We do two things to combat this. First, each subview that meets + * the scroll area is added as a damage rectangle. Second, we + * redisplay the subviews after the scroll. + */ + + /* + * Step 1: Find any subviews that meet the scroll area and mark + * them as damaged. Use Tk coordinates, shifted to account for the + * future scrolling. + */ + + for (NSView *subview in [view subviews] ) { + NSRect frame = [subview frame]; + CGRect subviewRect = CGRectMake( + frame.origin.x - macDraw->xOff + dx, + (bounds.size.height - frame.origin.y - frame.size.height) - macDraw->yOff + dy, + frame.size.width, frame.size.height); + // /* Rectangles with negative coordinates seem to cause trouble. */ + if (subviewRect.origin.y < 0 && subviewRect.origin.y + subviewRect.size.height > 0) { + subviewRect.origin.y = 0; + } + CGRect intersection = CGRectIntersection(srcRect, subviewRect); + if (! CGRectIsEmpty(intersection) ){ + dstRgn = HIShapeCreateWithRect(&subviewRect); + ChkErr(HIShapeUnion, dmgRgn, dstRgn, (HIMutableShapeRef) dmgRgn); + CFRelease(dstRgn); + } + } } } if ( dmgRgn == NULL ) { dmgRgn = HIShapeCreateEmpty(); } + //TkMacOSXInvalidateViewRegion(view, dmgRgn); TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); result = HIShapeIsEmpty(dmgRgn) ? 0 : 1; CFRelease(dmgRgn); diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 1e188e6..a08c56e 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -651,7 +651,7 @@ XConfigureWindow( * * TkMacOSXUpdateClipRgn -- * - * This function updates the clipping regions for a given window and all of + * This function updates the cliping regions for a given window and all of * its children. Once updated the TK_CLIP_INVALID flag in the subwindow * data structure is unset. The TK_CLIP_INVALID flag should always be * unset before any drawing is attempted. @@ -677,7 +677,7 @@ TkMacOSXUpdateClipRgn( macWin = winPtr->privatePtr; if (macWin && macWin->flags & TK_CLIP_INVALID) { TkWindow *win2Ptr; - + #ifdef TK_MAC_DEBUG_CLIP_REGIONS TkMacOSXDbgMsg("%s", winPtr->pathName); #endif @@ -817,7 +817,7 @@ TkMacOSXUpdateClipRgn( * * TkMacOSXVisableClipRgn -- * - * This function returns the Macintosh clipping region for the given + * This function returns the Macintosh cliping region for the given * window. The caller is responsible for disposing of the returned * region via TkDestroyRegion(). * @@ -912,7 +912,7 @@ TkMacOSXInvalidateWindow( * TK_PARENT_WINDOW */ { #ifdef TK_MAC_DEBUG_CLIP_REGIONS - TkMacOSXDbgMsg("%s", macWin->winPtr->pathName); + TkMacOSXDbgMsg("%s", winPtr->pathName); #endif if (macWin->flags & TK_CLIP_INVALID) { TkMacOSXUpdateClipRgn(macWin->winPtr); @@ -1070,7 +1070,7 @@ TkMacOSXGetRootControl( * None. * * Side effects: - * The clipping regions for the window and its children are marked invalid. + * The cliping regions for the window and its children are mark invalid. * (Make sure they are valid before drawing.) * *---------------------------------------------------------------------- @@ -1089,10 +1089,6 @@ TkMacOSXInvalClipRgns( * be marked. */ -#ifdef TK_MAC_DEBUG_CLIP_REGIONS - TkMacOSXDbgMsg("%s", winPtr->pathName); -#endif - if (!macWin || macWin->flags & TK_CLIP_INVALID) { return; } -- cgit v0.12 From acb6f7b259137644e34ba799e20432a0ced9be0a Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Mon, 11 Aug 2014 13:58:04 +0000 Subject: Fix typo's, debug statements, C++-comment. --- macosx/tkMacOSXDraw.c | 13 ++++++------- macosx/tkMacOSXSubwindows.c | 12 ++++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 6ec3e59..a9364fa 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -145,7 +145,7 @@ BitmapRepFromDrawableRect( NSBitmapImageRep *bitmap_rep=NULL; NSView *view=NULL; if ( mac_drawable->flags & TK_IS_PIXMAP ) { - /* + /* This means that the MacDrawable is functioning as a Tk Pixmap, so its view field is NULL. It's context field should point to a CGImage. */ @@ -1478,7 +1478,7 @@ TkScrollWindow( TkRegion damageRgn) /* Region to accumulate damage in. */ { Drawable drawable = Tk_WindowId(tkwin); - MacDrawable *macDraw = (MacDrawable *) drawable; + MacDrawable *macDraw = (MacDrawable *) drawable; NSView *view = TkMacOSXDrawableView(macDraw); CGRect visRect, srcRect, dstRect, scroll_src, scroll_dst; HIShapeRef dmgRgn = NULL; @@ -1489,7 +1489,7 @@ TkScrollWindow( /* Get the scroll area in NSView coordinates (origin at bottom left). */ bounds = [view bounds]; scroll_src = CGRectMake( - macDraw->xOff + x, + macDraw->xOff + x, bounds.size.height - height - (macDraw->yOff + y), width, height); scroll_dst = CGRectOffset(scroll_src, dx, -dy); @@ -1516,7 +1516,7 @@ TkScrollWindow( /* Scroll the rectangle. */ [view scrollRect:NSRectFromCGRect(scroll_src) by:NSMakeSize(dx, -dy)]; [view displayRect:NSRectFromCGRect(scroll_dst)]; - + /* * When a Text widget contains embedded images, scrolling generates * lots of artifacts involving multiple copies of the images @@ -1535,14 +1535,14 @@ TkScrollWindow( * them as damaged. Use Tk coordinates, shifted to account for the * future scrolling. */ - + for (NSView *subview in [view subviews] ) { NSRect frame = [subview frame]; CGRect subviewRect = CGRectMake( frame.origin.x - macDraw->xOff + dx, (bounds.size.height - frame.origin.y - frame.size.height) - macDraw->yOff + dy, frame.size.width, frame.size.height); - // /* Rectangles with negative coordinates seem to cause trouble. */ + /* Rectangles with negative coordinates seem to cause trouble. */ if (subviewRect.origin.y < 0 && subviewRect.origin.y + subviewRect.size.height > 0) { subviewRect.origin.y = 0; } @@ -1559,7 +1559,6 @@ TkScrollWindow( if ( dmgRgn == NULL ) { dmgRgn = HIShapeCreateEmpty(); } - //TkMacOSXInvalidateViewRegion(view, dmgRgn); TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); result = HIShapeIsEmpty(dmgRgn) ? 0 : 1; CFRelease(dmgRgn); diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index a08c56e..d557db3 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -651,7 +651,7 @@ XConfigureWindow( * * TkMacOSXUpdateClipRgn -- * - * This function updates the cliping regions for a given window and all of + * This function updates the clipping regions for a given window and all of * its children. Once updated the TK_CLIP_INVALID flag in the subwindow * data structure is unset. The TK_CLIP_INVALID flag should always be * unset before any drawing is attempted. @@ -817,7 +817,7 @@ TkMacOSXUpdateClipRgn( * * TkMacOSXVisableClipRgn -- * - * This function returns the Macintosh cliping region for the given + * This function returns the Macintosh clipping region for the given * window. The caller is responsible for disposing of the returned * region via TkDestroyRegion(). * @@ -912,7 +912,7 @@ TkMacOSXInvalidateWindow( * TK_PARENT_WINDOW */ { #ifdef TK_MAC_DEBUG_CLIP_REGIONS - TkMacOSXDbgMsg("%s", winPtr->pathName); + TkMacOSXDbgMsg("%s", macWin->winPtr->pathName); #endif if (macWin->flags & TK_CLIP_INVALID) { TkMacOSXUpdateClipRgn(macWin->winPtr); @@ -1070,7 +1070,7 @@ TkMacOSXGetRootControl( * None. * * Side effects: - * The cliping regions for the window and its children are mark invalid. + * The clipping regions for the window and its children are marked invalid. * (Make sure they are valid before drawing.) * *---------------------------------------------------------------------- @@ -1089,6 +1089,10 @@ TkMacOSXInvalClipRgns( * be marked. */ +#ifdef TK_MAC_DEBUG_CLIP_REGIONS + TkMacOSXDbgMsg("%s", winPtr->pathName); +#endif + if (!macWin || macWin->flags & TK_CLIP_INVALID) { return; } -- cgit v0.12