From c5695e766b9c40219e9280af36c65998d30e6eb7 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Mon, 28 Jul 2014 02:44:57 +0000 Subject: Fine-tune scrolling, especially of text widgets with embedded windows, after removal of private API calls; performance is now better and within acceptable ranges. --- macosx/tkMacOSXDraw.c | 4 - macosx/tkMacOSXPrivate.h | 7 -- macosx/tkMacOSXScrlbr.c | 20 ----- macosx/tkMacOSXSubwindows.c | 9 --- macosx/tkMacOSXWindowEvent.c | 170 +------------------------------------------ 5 files changed, 1 insertion(+), 209 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 9ec4e9a..5512669 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1554,10 +1554,6 @@ TkScrollWindow( } } - /* Step 2: Redisplay all subviews */ - for (NSView *subview in [view subviews] ) { - [subview display]; - } } } diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 42287c7..4855635 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -363,12 +363,5 @@ VISIBILITY_HIDDEN keyEquivalentModifierMask:(NSUInteger)keyEquivalentModifierMask; @end -//Remove private API calls here: not necessary for systems >= 10.7 -#if 0 -/* From WebKit/WebKit/mac/WebCoreSupport/WebChromeClient.mm: */ -@interface NSWindow(TKGrowBoxRect) -- (NSRect)_growBoxRect; -@end -#endif #endif /* _TKMACPRIV */ diff --git a/macosx/tkMacOSXScrlbr.c b/macosx/tkMacOSXScrlbr.c index b8c73bd..0b4fa61 100644 --- a/macosx/tkMacOSXScrlbr.c +++ b/macosx/tkMacOSXScrlbr.c @@ -331,26 +331,6 @@ TkpDisplayScrollbar( frame = NSInsetRect(frame, scrollPtr->inset, scrollPtr->inset); frame.origin.y = viewHeight - (frame.origin.y + frame.size.height); - NSWindow *w = [view window]; - - //This uses a private API call that is no longer needed on systems >= 10.7. - #if 0 - if ([w showsResizeIndicator]) { - NSRect growBox = [view convertRect:[w _growBoxRect] fromView:nil]; - - if (NSIntersectsRect(growBox, frame)) { - if (scrollPtr->vertical) { - CGFloat y = frame.origin.y; - - frame.origin.y = growBox.origin.y + growBox.size.height; - frame.size.height -= frame.origin.y - y; - } else { - frame.size.width = growBox.origin.x - frame.origin.x; - } - TkMacOSXSetScrollbarGrow(winPtr, true); - } - } - #endif if (!NSEqualRects(frame, [scroller frame])) { [scroller setFrame:frame]; } diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index ac055e3..a08c56e 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -741,15 +741,6 @@ TkMacOSXUpdateClipRgn( } else if (winPtr->wmInfoPtr->attributes & kWindowResizableAttribute) { NSWindow *w = TkMacOSXDrawableWindow(winPtr->window); - - if (w) { - // This call to private API not needed on systems >= 10.7 - // bounds = NSRectToCGRect([w _growBoxRect]); - // bounds.origin.y = [w contentRectForFrameRect: - // [w frame]].size.height - bounds.size.height - - // bounds.origin.y; - // ChkErr(TkMacOSHIShapeDifferenceWithRect, rgn, &bounds); - } } macWin->aboveVisRgn = HIShapeCreateCopy(rgn); diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index e6e27a4..c458a89 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -764,10 +764,7 @@ Tk_MacOSXIsAppInFront(void) * Custom content view for Tk NSWindows, containing standard NSView subviews. * The goal is to emulate X11-style drawing in response to Expose events: * during the normal AppKit drawing cycle, we supress drawing of all subviews - * (using a technique adapted from WebKit's WebHTMLView) and instead send - * Expose events about the subviews that would be redrawn. Tk Expose event - * handling and drawing handlers then draw the subviews manually via their - * -displayRectIgnoringOpacity: + * and instead send Expose events about the subviews that would be redrawn. */ @interface TKContentView(TKWindowEvent) @@ -806,18 +803,6 @@ ExposeRestrictProc( NSCompositeSourceOver); #endif - NSWindow *w = [self window]; - - //remove private API calls here: not needed on systems >= 10.7 - #if 0 - if ([self isOpaque] && [w showsResizeIndicator]) { - NSRect bounds = [self convertRect:[w _growBoxRect] fromView:nil]; - - if ([self needsToDrawRect:bounds]) { - NSEraseRect(bounds); - } - } - #endif CGFloat height = [self bounds].size.height; HIMutableShapeRef drawShape = HIShapeCreateMutable(); @@ -950,159 +935,6 @@ ExposeRestrictProc( } @end - -/*Remove private/non-documented API calls. This is strongly discouraged by Apple and may lead to breakage in the future.*/ - -#if 0 -#pragma mark TKContentViewPrivate - -/* - * Technique adapted from WebKit/WebKit/mac/WebView/WebHTMLView.mm to supress - * normal AppKit subview drawing and make all drawing go through us. - * Overrides NSView internals. - */ - - -@interface TKContentView(TKContentViewPrivate) -- (id) initWithFrame: (NSRect) frame; -- (void) _setAsideSubviews; -- (void) _restoreSubviews; -@end - -@interface NSView(TKContentViewPrivate) -- (void) _recursiveDisplayRectIfNeededIgnoringOpacity: (NSRect) rect - isVisibleRect: (BOOL) isVisibleRect - rectIsVisibleRectForView: (NSView *) visibleView - topView: (BOOL) topView; -- (void) _recursiveDisplayAllDirtyWithLockFocus: (BOOL) needsLockFocus - visRect: (NSRect) visRect; -- (void) _recursive: (BOOL) recurse - displayRectIgnoringOpacity: (NSRect) displayRect - inContext: (NSGraphicsContext *) context topView: (BOOL) topView; -- (void) _lightWeightRecursiveDisplayInRect: (NSRect) visRect; -- (BOOL) _drawRectIfEmpty; -- (void) _drawRect: (NSRect) inRect clip: (BOOL) clip; -- (void) _setDrawsOwnDescendants: (BOOL) drawsOwnDescendants; -@end - -@implementation TKContentView(TKContentViewPrivate) - -- (id) initWithFrame: (NSRect) frame -{ - self = [super initWithFrame:frame]; - if (self) { - _savedSubviews = nil; - _subviewsSetAside = NO; - [self _setDrawsOwnDescendants:YES]; - } - return self; -} - -- (void) _setAsideSubviews -{ -#ifdef TK_MAC_DEBUG - if (_subviewsSetAside || _savedSubviews) { - Tcl_Panic("TKContentView _setAsideSubviews called incorrectly"); - } -#endif - _savedSubviews = _subviews; - _subviews = nil; - _subviewsSetAside = YES; -} - -- (void) _restoreSubviews -{ -#ifdef TK_MAC_DEBUG - if (!_subviewsSetAside || _subviews) { - Tcl_Panic("TKContentView _restoreSubviews called incorrectly"); - } -#endif - _subviews = _savedSubviews; - _savedSubviews = nil; - _subviewsSetAside = NO; -} - -- (void) _recursiveDisplayRectIfNeededIgnoringOpacity: (NSRect) rect - isVisibleRect: (BOOL) isVisibleRect - rectIsVisibleRectForView: (NSView *) visibleView - topView: (BOOL) topView -{ - [self _setAsideSubviews]; - [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect - isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView - topView:topView]; - [self _restoreSubviews]; -} - -- (void) _recursiveDisplayAllDirtyWithLockFocus: (BOOL) needsLockFocus - visRect: (NSRect) visRect -{ - BOOL needToSetAsideSubviews = !_subviewsSetAside; - - if (needToSetAsideSubviews) { - [self _setAsideSubviews]; - } - [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus - visRect:visRect]; - if (needToSetAsideSubviews) { - [self _restoreSubviews]; - } -} - -- (void) _recursive: (BOOL) recurse - displayRectIgnoringOpacity: (NSRect) displayRect - inContext: (NSGraphicsContext *) context topView: (BOOL) topView -{ - [self _setAsideSubviews]; - [super _recursive:recurse - displayRectIgnoringOpacity:displayRect inContext:context - topView:topView]; - [self _restoreSubviews]; -} - -- (void) _lightWeightRecursiveDisplayInRect: (NSRect) visRect -{ - BOOL needToSetAsideSubviews = !_subviewsSetAside; - - if (needToSetAsideSubviews) { - [self _setAsideSubviews]; - } - [super _lightWeightRecursiveDisplayInRect:visRect]; - if (needToSetAsideSubviews) { - [self _restoreSubviews]; - } -} - -- (BOOL) _drawRectIfEmpty -{ - /* - * Our -drawRect manages subview drawing directly, so it needs to be called - * even if the area to be redrawn is completely obscured by subviews. - */ - - return YES; -} - -- (void) _drawRect: (NSRect) inRect clip: (BOOL) clip -{ -#ifdef TK_MAC_DEBUG_DRAWING - TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, - NSStringFromRect(inRect)); -#endif - BOOL subviewsWereSetAside = _subviewsSetAside; - - if (subviewsWereSetAside) { - [self _restoreSubviews]; - } - [super _drawRect:inRect clip:clip]; - if (subviewsWereSetAside) { - [self _setAsideSubviews]; - } -} - -@end -#endif - /* -- cgit v0.12 From 641b317cbd760a8d3676fff03a903c8c294410d0 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Fri, 1 Aug 2014 01:00:11 +0000 Subject: Fix for font configure crash on OS X, thanks to rob@bitkeeper.com for the patch --- generic/tkFont.c | 66 +++++++++++++++++++++++++-------------------------- macosx/tkMacOSXFont.c | 2 +- macosx/tkMacOSXMenu.c | 3 +++ 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/generic/tkFont.c b/generic/tkFont.c index cbc4cf4..a955c35 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -600,40 +600,40 @@ Tk_FontObjCmd( return result; } case FONT_CONFIGURE: { - int result; - const char *string; - Tcl_Obj *objPtr; - NamedFont *nfPtr; - Tcl_HashEntry *namedHashPtr; - - if (objc < 3) { - Tcl_WrongNumArgs(interp, 2, objv, "fontname ?-option value ...?"); - return TCL_ERROR; - } - string = Tcl_GetString(objv[2]); - namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string); + int result; + const char *string; + Tcl_Obj *objPtr; + NamedFont *nfPtr; + Tcl_HashEntry *namedHashPtr; + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "fontname ?-option value ...?"); + return TCL_ERROR; + } + string = Tcl_GetString(objv[2]); + namedHashPtr = Tcl_FindHashEntry(&fiPtr->namedTable, string); nfPtr = NULL; /* lint. */ - if (namedHashPtr != NULL) { - nfPtr = Tcl_GetHashValue(namedHashPtr); - } - if ((namedHashPtr == NULL) || nfPtr->deletePending) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "named font \"%s\" doesn't exist", string)); - Tcl_SetErrorCode(interp, "TK", "LOOKUP", "FONT", string, NULL); - return TCL_ERROR; - } - if (objc == 3) { - objPtr = NULL; - } else if (objc == 4) { - objPtr = objv[3]; - } else { - result = ConfigAttributesObj(interp, tkwin, objc - 3, objv + 3, - &nfPtr->fa); - UpdateDependentFonts(fiPtr, tkwin, namedHashPtr); - return result; - } - return GetAttributeInfoObj(interp, &nfPtr->fa, objPtr); - } + if (namedHashPtr != NULL) { + nfPtr = Tcl_GetHashValue(namedHashPtr); + } + if ((namedHashPtr == NULL) || nfPtr->deletePending) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "named font \"%s\" doesn't exist", string)); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "FONT", string, NULL); + return TCL_ERROR; + } + if (objc == 3) { + objPtr = NULL; + } else if (objc == 4) { + objPtr = objv[3]; + } else { + result = ConfigAttributesObj(interp, tkwin, objc - 3, objv + 3, + &nfPtr->fa); + UpdateDependentFonts(fiPtr, tkwin, namedHashPtr); + return result; + } + return GetAttributeInfoObj(interp, &nfPtr->fa, objPtr); + } case FONT_CREATE: { int skip = 3, i; const char *name; diff --git a/macosx/tkMacOSXFont.c b/macosx/tkMacOSXFont.c index d800ae5..4c8ac30 100644 --- a/macosx/tkMacOSXFont.c +++ b/macosx/tkMacOSXFont.c @@ -293,7 +293,7 @@ InitFont( [NSNumber numberWithInt:fmPtr->fixed ? 0 : 1], NSLigatureAttributeName, [NSNumber numberWithDouble:kern], NSKernAttributeName, nil]; - fontPtr->nsAttributes = TkMacOSXMakeUncollectableAndRetain(nsAttributes); + fontPtr->nsAttributes = TkMacOSXMakeUncollectableAndRetain(nsAttributes); #undef nCh } diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index 83ad47a..313a5cf 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -684,6 +684,9 @@ TkpConfigureMenuEntry( NSArray *itemArray = [submenu itemArray]; for (NSMenuItem *item in itemArray) { TkMenuEntry *submePtr = menuRefPtr->menuPtr->entries[i]; + /* Work around an apparent bug where itemArray can have + more items than the menu's entries[] array. */ + if (i >= menuRefPtr->menuPtr->numEntries) break; [item setEnabled: !(submePtr->state == ENTRY_DISABLED)]; i++; } -- cgit v0.12 From a53043ff3a732cffd15c303124f3fb397c9a5c18 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Fri, 1 Aug 2014 01:00:52 +0000 Subject: Fix for font configure crash on OS X, thanks to rob@bitkeeper.com for the patch --- macosx/tkMacOSXDraw.c | 3 +-- macosx/tkMacOSXMenu.c | 11 +++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 5512669..6ec3e59 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1542,7 +1542,7 @@ TkScrollWindow( 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; } @@ -1553,7 +1553,6 @@ TkScrollWindow( CFRelease(dstRgn); } } - } } diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index 7116050..380d3a7 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -683,15 +683,18 @@ TkpConfigureMenuEntry( int i = 0; NSArray *itemArray = [submenu itemArray]; for (NSMenuItem *item in itemArray) { - TkMenuEntry *submePtr = menuRefPtr->menuPtr->entries[i]; - [item setEnabled: !(submePtr->state == ENTRY_DISABLED)]; - i++; + TkMenuEntry *submePtr = menuRefPtr->menuPtr->entries[i]; + /* Work around an apparent bug where itemArray can have + more items than the menu's entries[] array. */ + if (i >= menuRefPtr->menuPtr->numEntries) break; + [item setEnabled: !(submePtr->state == ENTRY_DISABLED)]; + i++; } } - } } } + [menuItem setSubmenu:submenu]; return TCL_OK; -- cgit v0.12 From 07fd47d492c9a0c44ac5fcb2343b1d6973d1a072 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Mon, 4 Aug 2014 21:14:19 +0000 Subject: Further refinement of Mac OS X scrolling; thanks to Marc Culler for an additional patch. --- macosx/tkMacOSXDraw.c | 95 ++++++++++++++++----------------------------- macosx/tkMacOSXSubwindows.c | 14 ++++--- 2 files changed, 43 insertions(+), 66 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 6ec3e59..089097e 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1458,12 +1458,12 @@ XMaxRequestSize( * a damage region. * * Results: - * Returns 0 if the scroll genereated no additional damage. + * Returns 0 if the scroll generated no additional damage. * Otherwise, sets the region that needs to be repainted after * scrolling and returns 1. * * Side effects: - * Scrolls the bits in the window. + * Scrolls the rectangle in the window. * *---------------------------------------------------------------------- */ @@ -1480,86 +1480,59 @@ TkScrollWindow( Drawable drawable = Tk_WindowId(tkwin); MacDrawable *macDraw = (MacDrawable *) drawable; NSView *view = TkMacOSXDrawableView(macDraw); - CGRect visRect, srcRect, dstRect, scroll_src, scroll_dst; - HIShapeRef dmgRgn = NULL; - NSRect bounds; + CGRect dmgRect, dstRect, visRectBL, srcRectBL, dstRectBL; + NSRect visible, frame; + HIShapeRef dmgRgn = NULL, dstRgn; 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). */ - bounds = [view bounds]; - scroll_src = CGRectMake( - macDraw->xOff + x, - bounds.size.height - height - (macDraw->yOff + y), + srcRectBL = CGRectMake(frame.origin.x + x, + frame.origin.y + frame.size.height - height - y, width, height); - scroll_dst = CGRectOffset(scroll_src, dx, -dy); + dstRectBL = CGRectOffset(srcRectBL, dx, -dy); + /* Limit scrolling to the window content area. */ - visRect = NSRectToCGRect([view visibleRect]); - scroll_src = CGRectIntersection(scroll_src, visRect); - scroll_dst = CGRectIntersection(scroll_dst, visRect); + visRectBL = NSRectToCGRect(frame); + srcRectBL = CGRectIntersection(srcRectBL, visRectBL); + dstRectBL = CGRectIntersection(dstRectBL, visRectBL); - if ( !CGRectIsEmpty(scroll_src) && !CGRectIsEmpty(scroll_dst) ) { + if ( !CGRectIsEmpty(srcRectBL) && !CGRectIsEmpty(dstRectBL) ) { /* - * Mark the difference between source and destination as damaged. - * This region is described in the Tk coordinate system, after shifting by dy. + * Construct the damage region. We extend it to the top + * or bottom of the window, in order to avoid artifacts when + * scrolling. */ - srcRect = CGRectMake(x - macDraw->xOff, y - macDraw->yOff, - width, height); - dstRect = CGRectOffset(srcRect, dx, dy); - dmgRgn = HIShapeCreateMutableWithRect(&srcRect); - HIShapeRef dstRgn = HIShapeCreateWithRect(&dstRect); + 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); ChkErr(HIShapeDifference, dmgRgn, dstRgn, (HIMutableShapeRef) dmgRgn); CFRelease(dstRgn); /* 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 - * 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); - } - } + [view scrollRect:NSRectFromCGRect(srcRectBL) by:NSMakeSize(dx, -dy)]; } } 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..1e188e6 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. @@ -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 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 From fc2388482a673cedc13b2e7a060175259243a33f Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Mon, 4 Aug 2014 21:14:32 +0000 Subject: Further refinement of Mac OS X scrolling; thanks to Marc Culler for an additional patch. --- macosx/tkMacOSXDraw.c | 46 +++++---------------------------------------- macosx/tkMacOSXSubwindows.c | 14 +++++++++----- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 6eef09d..40400c2 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1460,12 +1460,12 @@ XMaxRequestSize( * a damage region. * * Results: - * Returns 0 if the scroll genereated no additional damage. + * Returns 0 if the scroll generated no additional damage. * Otherwise, sets the region that needs to be repainted after * scrolling and returns 1. * * Side effects: - * Scrolls the bits in the window. + * Scrolls the rectangle in the window. * *---------------------------------------------------------------------- */ @@ -1518,51 +1518,15 @@ 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 - * 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 9ba7100..e4af406 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. @@ -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 @@ -818,7 +818,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(). * @@ -913,7 +913,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); @@ -1071,7 +1071,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.) * *---------------------------------------------------------------------- @@ -1090,6 +1090,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 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 57ad1cd7057d0a9bd8ba4ca1465a9bc44e5b20e4 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Mon, 11 Aug 2014 03:12:28 +0000 Subject: Further refinement of scrolling; addresses artifacts in scrolling complex interfaces on OS X --- macosx/tkMacOSXDraw.c | 43 ++++++++++++++++++++++++++++++++++++++++--- macosx/tkMacOSXSubwindows.c | 14 +++++--------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 40400c2..6631b4f 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1460,12 +1460,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. * *---------------------------------------------------------------------- */ @@ -1518,9 +1518,46 @@ 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 + * 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 ) { diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index e4af406..9ba7100 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 @@ -818,7 +818,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(). * @@ -913,7 +913,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); @@ -1071,7 +1071,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.) * *---------------------------------------------------------------------- @@ -1090,10 +1090,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 From a5927e8d5f0b12747732ed2d4a6c58ac801314b9 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Thu, 14 Aug 2014 02:39:53 +0000 Subject: Allow Tk to post popup menus when Tk app is not frontmost --- macosx/tkMacOSXMenu.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index 380d3a7..e1cdc76 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -757,11 +757,19 @@ TkpPostMenu( * to be posted. */ int y) /* The global y-coordinate */ { - NSWindow *win = [NSApp keyWindow]; - if (!win) { + + + /* Get the object that holds this Tk Window.*/ + Tk_Window root; + root = Tk_MainWindow(interp); + if (root == NULL) { return TCL_ERROR; } - + + Drawable d = Tk_WindowId(root); + NSView *rootview = TkMacOSXGetRootControl(d); + NSWindow *win = [rootview window]; + inPostMenu = 1; int oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE); -- cgit v0.12 From ab19054c699a4edb7222aafe4595c79a139d4fe4 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Thu, 14 Aug 2014 02:41:52 +0000 Subject: Allow Tk to post popup menus when Tk app is not frontmost --- macosx/tkMacOSXMenu.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index 313a5cf..a8e9f2f 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -757,10 +757,18 @@ TkpPostMenu( * to be posted. */ int y) /* The global y-coordinate */ { - NSWindow *win = [NSApp keyWindow]; - if (!win) { + + + /* Get the object that holds this Tk Window.*/ + Tk_Window root; + root = Tk_MainWindow(interp); + if (root == NULL) { return TCL_ERROR; } + + Drawable d = Tk_WindowId(root); + NSView *rootview = TkMacOSXGetRootControl(d); + NSWindow *win = [rootview window]; inPostMenu = 1; -- cgit v0.12 From 4ccf614e946e9d871b37e6cd562a038f687e8d7a Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Sat, 16 Aug 2014 00:52:00 +0000 Subject: Fix for shimmering of buttons embedded when scrolled in text and canvas widgets; improvements in scrolling smoothness in text widget. Thanks to Marc Culler for patches. --- generic/tkTextDisp.c | 10 +++ macosx/tkMacOSXButton.c | 164 ++++++++++++++++++++++++++++++++++++++++++------ macosx/tkMacOSXDraw.c | 132 +++++++++++++++++--------------------- macosx/tkMacOSXInt.h | 3 +- 4 files changed, 214 insertions(+), 95 deletions(-) diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index 2516e1c..cd232bf 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -3968,6 +3968,16 @@ DisplayText( UpdateDisplayInfo(textPtr); dInfoPtr->dLinesInvalidated = 0; +#ifdef MAC_OSX_TK + /* + * Make sure that unmapped subwindows really have been unmapped. + * If the unmap request is pending as an idle request, the window + * can get redrawn on top of the widget. + */ + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} +#endif + + /* * See if it's possible to bring some parts of the screen up-to-date by * scrolling (copying from other parts of the screen). We have to be diff --git a/macosx/tkMacOSXButton.c b/macosx/tkMacOSXButton.c index f77a74e..9896971 100644 --- a/macosx/tkMacOSXButton.c +++ b/macosx/tkMacOSXButton.c @@ -7,6 +7,7 @@ * Copyright (c) 1996-1997 by Sun Microsystems, Inc. * Copyright 2001-2009, Apple Inc. * Copyright (c) 2006-2009 Daniel A. Steffen + * Copyright 2014 Marc Culler. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -23,9 +24,60 @@ #endif */ +static NSRect TkMacOSXGetButtonFrame(TkButton *butPtr); + +/* + * A subclass of NSButton with sanity checking: + * NSButtons created by Tk will have their tag set to a pointer to the TkButton + * which manages the NSButton. This allows a TkNSButton to be aware of the + * state of its Tk parent. This subclass overrides the drawRect method + * so that it will not draw itself unless the NSButton frame matches + * the frame which was installed by DisplayButton, and the TkButton is + * mapped. Also, it will not draw anything if the widget is completely + * outside of its container. + */ + +@interface TkNSButton: NSButton + +@end + +@implementation TkNSButton + + - (void)drawRect:(NSRect)dirtyRect + { + NSInteger tag = [self tag]; + if ( tag != -1) { + TkButton *butPtr = (TkButton *)tag; + MacDrawable* macWin = (MacDrawable *)butPtr; + NSRect Tkframe = TkMacOSXGetButtonFrame(butPtr); + Tk_Window tkwin = butPtr->tkwin; + /* Do not draw if the widget is misplaced or unmapped. */ + if ( NSIsEmptyRect(Tkframe) || + ! macWin->winPtr->flags & TK_MAPPED || + ! NSEqualRects(Tkframe, [self frame]) + ) { + return; + } + /* Do not draw if the widget is completely outside of its parent, or within 20 pixels of the lower border; this prevents buttons from being drawn on peer widgets as scrolling occurs. */ + if (tkwin) { + int parent_height = Tk_Height(Tk_Parent(tkwin)); + int widget_height = Tk_Height(tkwin); + int y = Tk_Y(tkwin); + if ( y > parent_height - 20 || y + widget_height < 0 ) { + return; + } + + } + } + [super drawRect:dirtyRect]; + } + +@end + + typedef struct MacButton { TkButton info; - NSButton *button; + TkNSButton *button; NSImage *image, *selectImage, *tristateImage; #if TK_MAC_BUTTON_USE_COMPATIBILITY_METRICS int fix; @@ -186,6 +238,45 @@ TkpDisplayButton( /* *---------------------------------------------------------------------- * + * TkpShiftButton -- + * + * Moves the frame of an NSButton (or TkNSButton) and in case the tag is + * set, also adjusts the xOff and yOff of the controlling TkButton's + * MacDrawable. This is used to avoid jitter when scrolling. + * + * Results: + * None + * + * Side effects: + * Moves the NSbutton after adjusting the associated MacDrawable. + * + *---------------------------------------------------------------------- + */ +void +TkpShiftButton( + NSButton *button, + NSPoint delta ) + { + NSPoint origin = [button frame].origin; + NSInteger tag = [button tag]; + if ( tag != -1) { + TkButton* butPtr = (TkButton *)tag; + TkWindow *winPtr = (TkWindow *) (butPtr->tkwin); + if (winPtr) { + MacDrawable *macWin = (MacDrawable *) winPtr->window; + macWin->xOff += delta.x; + macWin->yOff += delta.y; + } + } + origin.x += delta.x; + origin.y -= delta.y; + [button setFrameOrigin:origin]; + } + + +/* + *---------------------------------------------------------------------- + * * TkpComputeButtonGeometry -- * * After changes in a button's text or bitmap, this procedure @@ -218,7 +309,8 @@ TkpComputeButtonGeometry( case TYPE_CHECK_BUTTON: case TYPE_RADIO_BUTTON: if (!macButtonPtr->button) { - NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect]; + TkNSButton *button = [[TkNSButton alloc] initWithFrame:NSZeroRect]; + [button setTag:(NSInteger)butPtr]; macButtonPtr->button = TkMacOSXMakeUncollectable(button); } ComputeNativeButtonGeometry(butPtr); @@ -266,6 +358,52 @@ TkpButtonSetDefaults() /* *---------------------------------------------------------------------- * + * TkMacOSXGetButtonFrame -- + * + * Computes a frame for an NSButton that will correspond to where + * Tk thinks the button is located. + * + * Results: + * Returns an NSRect describing a frame for an NSButton. + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ +NSRect TkMacOSXGetButtonFrame( + TkButton *butPtr) +{ + MacButton *macButtonPtr = (MacButton *) butPtr; + Tk_Window tkwin = butPtr->tkwin; + TkWindow *winPtr = (TkWindow *) tkwin; + if (tkwin) { + MacDrawable *macWin = (MacDrawable *) winPtr->window; + NSView *view = TkMacOSXDrawableView(macWin); + CGFloat viewHeight = [view bounds].size.height; + NSRect frame = NSMakeRect(macWin->xOff, macWin->yOff, + Tk_Width(tkwin), Tk_Height(tkwin)); + +#if TK_MAC_BUTTON_USE_COMPATIBILITY_METRICS + if (tkMacOSXUseCompatibilityMetrics) { + BoundsFix boundsFix = boundsFixes[macButtonPtr->fix]; + frame = NSOffsetRect(frame, boundsFix.offsetX, boundsFix.offsetY); + frame.size.height -= boundsFix.shrinkH + NATIVE_BUTTON_EXTRA_H; + frame = NSInsetRect(frame, boundsFix.inset + NATIVE_BUTTON_INSET, + boundsFix.inset + NATIVE_BUTTON_INSET); + } +#endif + + frame.origin.y = viewHeight - (frame.origin.y + frame.size.height); + return frame; + } else { + return NSZeroRect; + } +} + +/* + *---------------------------------------------------------------------- + * * DisplayNativeButton -- * * This procedure is invoked to display a button widget. It is @@ -286,7 +424,7 @@ DisplayNativeButton( TkButton *butPtr) { MacButton *macButtonPtr = (MacButton *) butPtr; - NSButton *button = macButtonPtr->button; + TkNSButton *button = macButtonPtr->button; Tk_Window tkwin = butPtr->tkwin; TkWindow *winPtr = (TkWindow *) tkwin; MacDrawable *macWin = (MacDrawable *) winPtr->window; @@ -349,21 +487,8 @@ DisplayNativeButton( } else { [button setKeyEquivalent:@""]; } - frame = NSMakeRect(macWin->xOff, macWin->yOff, Tk_Width(tkwin), - Tk_Height(tkwin)); -#if TK_MAC_BUTTON_USE_COMPATIBILITY_METRICS - if (tkMacOSXUseCompatibilityMetrics) { - BoundsFix boundsFix = boundsFixes[macButtonPtr->fix]; - frame = NSOffsetRect(frame, boundsFix.offsetX, boundsFix.offsetY); - frame.size.height -= boundsFix.shrinkH + NATIVE_BUTTON_EXTRA_H; - frame = NSInsetRect(frame, boundsFix.inset + NATIVE_BUTTON_INSET, - boundsFix.inset + NATIVE_BUTTON_INSET); - } -#endif - frame.origin.y = viewHeight - (frame.origin.y + frame.size.height); - if (!NSEqualRects(frame, [button frame])) { - [button setFrame:frame]; - } + frame = TkMacOSXGetButtonFrame(butPtr); + [button setFrame:frame]; [button displayRectIgnoringOpacity:[button bounds]]; TkMacOSXRestoreDrawingContext(&dc); #ifdef TK_MAC_DEBUG_BUTTON @@ -396,7 +521,7 @@ ComputeNativeButtonGeometry( TkButton *butPtr) /* Button whose geometry may have changed. */ { MacButton *macButtonPtr = (MacButton *) butPtr; - NSButton *button = macButtonPtr->button; + TkNSButton *button = macButtonPtr->button; NSButtonCell *cell = [button cell]; NSButtonType type = -1; NSBezelStyle style = 0; @@ -1169,6 +1294,7 @@ ComputeUnixButtonGeometry( Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset); } + /* * Local Variables: * mode: objc diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index a9364fa..2d725a7 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -17,6 +17,7 @@ #include "tkMacOSXPrivate.h" #include "tkMacOSXDebug.h" #include "xbytes.h" +#include "tkButton.h" /* #ifdef TK_MAC_DEBUG @@ -1478,86 +1479,67 @@ 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; - NSRect bounds; + CGRect srcRect, dstRect; + HIShapeRef dmgRgn = NULL, extraRgn; + NSRect bounds, visRect, scrollSrc, scrollDst; + NSPoint delta = NSMakePoint(dx, dy); int result; - + if ( view ) { - /* Get the scroll area in NSView coordinates (origin at bottom left). */ - bounds = [view bounds]; - scroll_src = CGRectMake( - macDraw->xOff + x, - bounds.size.height - height - (macDraw->yOff + y), - width, height); - scroll_dst = CGRectOffset(scroll_src, dx, -dy); - /* Limit scrolling to the window content area. */ - visRect = NSRectToCGRect([view visibleRect]); - scroll_src = CGRectIntersection(scroll_src, visRect); - scroll_dst = CGRectIntersection(scroll_dst, visRect); - - if ( !CGRectIsEmpty(scroll_src) && !CGRectIsEmpty(scroll_dst) ) { - - /* - * Mark the difference between source and destination as damaged. - * This region is described in the Tk coordinate system, after shifting by dy. - */ - - 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(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); - } - } - } + /* Get the scroll area in NSView coordinates (origin at bottom left). */ + bounds = [view bounds]; + scrollSrc = NSMakeRect( + macDraw->xOff + x, + bounds.size.height - height - (macDraw->yOff + y), + width, height); + scrollDst = NSOffsetRect(scrollSrc, dx, -dy); + /* Limit scrolling to the window content area. */ + visRect = [view visibleRect]; + scrollSrc = NSIntersectionRect(scrollSrc, visRect); + scrollDst = NSIntersectionRect(scrollDst, visRect); + + if ( !NSIsEmptyRect(scrollSrc) && !NSIsEmptyRect(scrollDst) ) { + + /* + * Mark the difference between source and destination as damaged. + * This region is described in the Tk coordinate system. + */ + + srcRect = CGRectMake(x, y, width, height); + dstRect = CGRectOffset(srcRect, dx, dy); + dmgRgn = HIShapeCreateMutableWithRect(&srcRect); + extraRgn = HIShapeCreateWithRect(&dstRect); + ChkErr(HIShapeDifference, dmgRgn, extraRgn, (HIMutableShapeRef) dmgRgn); + CFRelease(extraRgn); + + /* Scroll the rectangle. */ + [view scrollRect:scrollSrc by:NSMakeSize(dx, -dy)]; + + /* + * Adjust the positions of the button subwindows that meet the scroll + * area. + */ + + for (NSView *subview in [view subviews] ) { + if ( [subview isKindOfClass:[NSButton class]] == YES ) { + NSRect subframe = [subview frame]; + if ( NSIntersectsRect(scrollSrc, subframe) || + NSIntersectsRect(scrollDst, subframe) ) { + TkpShiftButton((NSButton *)subview, delta ); + } + } + } + + /* Redisplay the scrolled area. */ + [view displayRect:scrollDst]; + + } } - + if ( dmgRgn == NULL ) { - dmgRgn = HIShapeCreateEmpty(); + dmgRgn = HIShapeCreateEmpty(); } TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); result = HIShapeIsEmpty(dmgRgn) ? 0 : 1; diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h index 813acce..249d5cf 100644 --- a/macosx/tkMacOSXInt.h +++ b/macosx/tkMacOSXInt.h @@ -24,6 +24,7 @@ #ifndef _TKMAC #include "tkMacOSX.h" +#import #endif /* @@ -196,7 +197,7 @@ MODULE_SCOPE void TkpClipDrawableToRect(Display *display, Drawable d, int x, int y, int width, int height); MODULE_SCOPE void TkpRetainRegion(TkRegion r); MODULE_SCOPE void TkpReleaseRegion(TkRegion r); - +MODULE_SCOPE void TkpShiftButton(NSButton *button, NSPoint delta); /* * Include the stubbed internal platform-specific API. */ -- cgit v0.12 From 42f946fb1df90d38c04c7b68cf1fd3aca9377d6f Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Sat, 16 Aug 2014 00:52:12 +0000 Subject: Fix for shimmering of buttons embedded when scrolled in text and canvas widgets; improvements in scrolling smoothness in text widget. Thanks to Marc Culler for patches. --- generic/tkTextDisp.c | 11 +++- macosx/tkMacOSXButton.c | 163 ++++++++++++++++++++++++++++++++++++++++++------ macosx/tkMacOSXDraw.c | 132 +++++++++++++++++---------------------- macosx/tkMacOSXInt.h | 3 +- 4 files changed, 213 insertions(+), 96 deletions(-) diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index aebdcc6..8ceb3fa 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -3970,6 +3970,15 @@ DisplayText( UpdateDisplayInfo(textPtr); dInfoPtr->dLinesInvalidated = 0; +#ifdef MAC_OSX_TK + /* + * Make sure that unmapped subwindows really have been unmapped. + * If the unmap request is pending as an idle request, the window + * can get redrawn on top of the widget. + */ + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} +#endif + /* * See if it's possible to bring some parts of the screen up-to-date by * scrolling (copying from other parts of the screen). We have to be @@ -4035,7 +4044,7 @@ DisplayText( */ if ((y + height) > dInfoPtr->maxY) { - height = dInfoPtr->maxY -y; + height = dInfoPtr->maxY - y; } oldY = dlPtr->oldY; if (y < dInfoPtr->y) { diff --git a/macosx/tkMacOSXButton.c b/macosx/tkMacOSXButton.c index 036624d..dd4cca7 100644 --- a/macosx/tkMacOSXButton.c +++ b/macosx/tkMacOSXButton.c @@ -7,6 +7,7 @@ * Copyright (c) 1996-1997 by Sun Microsystems, Inc. * Copyright 2001-2009, Apple Inc. * Copyright (c) 2006-2009 Daniel A. Steffen + * Copyright 2014 Marc Culler. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -23,9 +24,59 @@ #endif */ +static NSRect TkMacOSXGetButtonFrame(TkButton *butPtr); + +/* + * A subclass of NSButton with sanity checking: + * NSButtons created by Tk will have their tag set to a pointer to the TkButton + * which manages the NSButton. This allows a TkNSButton to be aware of the + * state of its Tk parent. This subclass overrides the drawRect method + * so that it will not draw itself unless the NSButton frame matches + * the frame which was installed by DisplayButton, and the TkButton is + * mapped. Also, it will not draw anything if the widget is completely + * outside of its container. + */ + +@interface TkNSButton: NSButton + +@end + +@implementation TkNSButton + + - (void)drawRect:(NSRect)dirtyRect + { + NSInteger tag = [self tag]; + if ( tag != -1) { + TkButton *butPtr = (TkButton *)tag; + MacDrawable* macWin = (MacDrawable *)butPtr; + NSRect Tkframe = TkMacOSXGetButtonFrame(butPtr); + Tk_Window tkwin = butPtr->tkwin; + /* Do not draw if the widget is misplaced or unmapped. */ + if ( NSIsEmptyRect(Tkframe) || + ! macWin->winPtr->flags & TK_MAPPED || + ! NSEqualRects(Tkframe, [self frame]) + ) { + return; + } + /* Do not draw if the widget is completely outside of its parent, or within 20 pixels of the lower border; this prevents buttons from being drawn on peer widgets as scrolling occurs. */ + if (tkwin) { + int parent_height = Tk_Height(Tk_Parent(tkwin)); + int widget_height = Tk_Height(tkwin); + int y = Tk_Y(tkwin); + if ( y > parent_height - 20 || y + widget_height < 0 ) { + return; + } + } + [super drawRect:dirtyRect]; + } + } + +@end + + typedef struct MacButton { TkButton info; - NSButton *button; + TkNSButton *button; NSImage *image, *selectImage, *tristateImage; #if TK_MAC_BUTTON_USE_COMPATIBILITY_METRICS int fix; @@ -186,6 +237,45 @@ TkpDisplayButton( /* *---------------------------------------------------------------------- * + * TkpShiftButton -- + * + * Moves the frame of an NSButton (or TkNSButton) and in case the tag is + * set, also adjusts the xOff and yOff of the controlling TkButton's + * MacDrawable. This is used to avoid jitter when scrolling. + * + * Results: + * None + * + * Side effects: + * Moves the NSbutton after adjusting the associated MacDrawable. + * + *---------------------------------------------------------------------- + */ +void +TkpShiftButton( + NSButton *button, + NSPoint delta ) + { + NSPoint origin = [button frame].origin; + NSInteger tag = [button tag]; + if ( tag != -1) { + TkButton* butPtr = (TkButton *)tag; + TkWindow *winPtr = (TkWindow *) (butPtr->tkwin); + if (winPtr) { + MacDrawable *macWin = (MacDrawable *) winPtr->window; + macWin->xOff += delta.x; + macWin->yOff += delta.y; + } + } + origin.x += delta.x; + origin.y -= delta.y; + [button setFrameOrigin:origin]; + } + + +/* + *---------------------------------------------------------------------- + * * TkpComputeButtonGeometry -- * * After changes in a button's text or bitmap, this procedure @@ -218,7 +308,8 @@ TkpComputeButtonGeometry( case TYPE_CHECK_BUTTON: case TYPE_RADIO_BUTTON: if (!macButtonPtr->button) { - NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect]; + TkNSButton *button = [[TkNSButton alloc] initWithFrame:NSZeroRect]; + [button setTag:(NSInteger)butPtr]; macButtonPtr->button = TkMacOSXMakeUncollectable(button); } ComputeNativeButtonGeometry(butPtr); @@ -266,6 +357,52 @@ TkpButtonSetDefaults() /* *---------------------------------------------------------------------- * + * TkMacOSXGetButtonFrame -- + * + * Computes a frame for an NSButton that will correspond to where + * Tk thinks the button is located. + * + * Results: + * Returns an NSRect describing a frame for an NSButton. + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ +NSRect TkMacOSXGetButtonFrame( + TkButton *butPtr) +{ + MacButton *macButtonPtr = (MacButton *) butPtr; + Tk_Window tkwin = butPtr->tkwin; + TkWindow *winPtr = (TkWindow *) tkwin; + if (tkwin) { + MacDrawable *macWin = (MacDrawable *) winPtr->window; + NSView *view = TkMacOSXDrawableView(macWin); + CGFloat viewHeight = [view bounds].size.height; + NSRect frame = NSMakeRect(macWin->xOff, macWin->yOff, + Tk_Width(tkwin), Tk_Height(tkwin)); + +#if TK_MAC_BUTTON_USE_COMPATIBILITY_METRICS + if (tkMacOSXUseCompatibilityMetrics) { + BoundsFix boundsFix = boundsFixes[macButtonPtr->fix]; + frame = NSOffsetRect(frame, boundsFix.offsetX, boundsFix.offsetY); + frame.size.height -= boundsFix.shrinkH + NATIVE_BUTTON_EXTRA_H; + frame = NSInsetRect(frame, boundsFix.inset + NATIVE_BUTTON_INSET, + boundsFix.inset + NATIVE_BUTTON_INSET); + } +#endif + + frame.origin.y = viewHeight - (frame.origin.y + frame.size.height); + return frame; + } else { + return NSZeroRect; + } +} + +/* + *---------------------------------------------------------------------- + * * DisplayNativeButton -- * * This procedure is invoked to display a button widget. It is @@ -286,7 +423,7 @@ DisplayNativeButton( TkButton *butPtr) { MacButton *macButtonPtr = (MacButton *) butPtr; - NSButton *button = macButtonPtr->button; + TkNSButton *button = macButtonPtr->button; Tk_Window tkwin = butPtr->tkwin; TkWindow *winPtr = (TkWindow *) tkwin; MacDrawable *macWin = (MacDrawable *) winPtr->window; @@ -349,21 +486,8 @@ DisplayNativeButton( } else { [button setKeyEquivalent:@""]; } - frame = NSMakeRect(macWin->xOff, macWin->yOff, Tk_Width(tkwin), - Tk_Height(tkwin)); -#if TK_MAC_BUTTON_USE_COMPATIBILITY_METRICS - if (tkMacOSXUseCompatibilityMetrics) { - BoundsFix boundsFix = boundsFixes[macButtonPtr->fix]; - frame = NSOffsetRect(frame, boundsFix.offsetX, boundsFix.offsetY); - frame.size.height -= boundsFix.shrinkH + NATIVE_BUTTON_EXTRA_H; - frame = NSInsetRect(frame, boundsFix.inset + NATIVE_BUTTON_INSET, - boundsFix.inset + NATIVE_BUTTON_INSET); - } -#endif - frame.origin.y = viewHeight - (frame.origin.y + frame.size.height); - if (!NSEqualRects(frame, [button frame])) { - [button setFrame:frame]; - } + frame = TkMacOSXGetButtonFrame(butPtr); + [button setFrame:frame]; [button displayRectIgnoringOpacity:[button bounds]]; TkMacOSXRestoreDrawingContext(&dc); #ifdef TK_MAC_DEBUG_BUTTON @@ -396,7 +520,7 @@ ComputeNativeButtonGeometry( TkButton *butPtr) /* Button whose geometry may have changed. */ { MacButton *macButtonPtr = (MacButton *) butPtr; - NSButton *button = macButtonPtr->button; + TkNSButton *button = macButtonPtr->button; NSButtonCell *cell = [button cell]; NSButtonType type = -1; NSBezelStyle style = 0; @@ -1169,6 +1293,7 @@ ComputeUnixButtonGeometry( Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset); } + /* * Local Variables: * mode: objc diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 3a3e288..ce8bf9b 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -17,6 +17,7 @@ #include "tkMacOSXPrivate.h" #include "tkMacOSXDebug.h" #include "xbytes.h" +#include "tkButton.h" /* #ifdef TK_MAC_DEBUG @@ -1479,86 +1480,67 @@ 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; - NSRect bounds; + CGRect srcRect, dstRect; + HIShapeRef dmgRgn = NULL, extraRgn; + NSRect bounds, visRect, scrollSrc, scrollDst; + NSPoint delta = NSMakePoint(dx, dy); int result; - + if ( view ) { - /* Get the scroll area in NSView coordinates (origin at bottom left). */ - bounds = [view bounds]; - scroll_src = CGRectMake( - macDraw->xOff + x, - bounds.size.height - height - (macDraw->yOff + y), - width, height); - scroll_dst = CGRectOffset(scroll_src, dx, -dy); - /* Limit scrolling to the window content area. */ - visRect = NSRectToCGRect([view visibleRect]); - scroll_src = CGRectIntersection(scroll_src, visRect); - scroll_dst = CGRectIntersection(scroll_dst, visRect); - - if ( !CGRectIsEmpty(scroll_src) && !CGRectIsEmpty(scroll_dst) ) { - - /* - * Mark the difference between source and destination as damaged. - * This region is described in the Tk coordinate system, after shifting by dy. - */ - - 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(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); - } - } - } + /* Get the scroll area in NSView coordinates (origin at bottom left). */ + bounds = [view bounds]; + scrollSrc = NSMakeRect( + macDraw->xOff + x, + bounds.size.height - height - (macDraw->yOff + y), + width, height); + scrollDst = NSOffsetRect(scrollSrc, dx, -dy); + /* Limit scrolling to the window content area. */ + visRect = [view visibleRect]; + scrollSrc = NSIntersectionRect(scrollSrc, visRect); + scrollDst = NSIntersectionRect(scrollDst, visRect); + + if ( !NSIsEmptyRect(scrollSrc) && !NSIsEmptyRect(scrollDst) ) { + + /* + * Mark the difference between source and destination as damaged. + * This region is described in the Tk coordinate system. + */ + + srcRect = CGRectMake(x, y, width, height); + dstRect = CGRectOffset(srcRect, dx, dy); + dmgRgn = HIShapeCreateMutableWithRect(&srcRect); + extraRgn = HIShapeCreateWithRect(&dstRect); + ChkErr(HIShapeDifference, dmgRgn, extraRgn, (HIMutableShapeRef) dmgRgn); + CFRelease(extraRgn); + + /* Scroll the rectangle. */ + [view scrollRect:scrollSrc by:NSMakeSize(dx, -dy)]; + + /* + * Adjust the positions of the button subwindows that meet the scroll + * area. + */ + + for (NSView *subview in [view subviews] ) { + if ( [subview isKindOfClass:[NSButton class]] == YES ) { + NSRect subframe = [subview frame]; + if ( NSIntersectsRect(scrollSrc, subframe) || + NSIntersectsRect(scrollDst, subframe) ) { + TkpShiftButton((NSButton *)subview, delta ); + } + } + } + + /* Redisplay the scrolled area. */ + [view displayRect:scrollDst]; + + } } - + if ( dmgRgn == NULL ) { - dmgRgn = HIShapeCreateEmpty(); + dmgRgn = HIShapeCreateEmpty(); } TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); result = HIShapeIsEmpty(dmgRgn) ? 0 : 1; diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h index 813acce..249d5cf 100644 --- a/macosx/tkMacOSXInt.h +++ b/macosx/tkMacOSXInt.h @@ -24,6 +24,7 @@ #ifndef _TKMAC #include "tkMacOSX.h" +#import #endif /* @@ -196,7 +197,7 @@ MODULE_SCOPE void TkpClipDrawableToRect(Display *display, Drawable d, int x, int y, int width, int height); MODULE_SCOPE void TkpRetainRegion(TkRegion r); MODULE_SCOPE void TkpReleaseRegion(TkRegion r); - +MODULE_SCOPE void TkpShiftButton(NSButton *button, NSPoint delta); /* * Include the stubbed internal platform-specific API. */ -- cgit v0.12