diff options
Diffstat (limited to 'macosx/tkMacOSXDraw.c')
| -rw-r--r-- | macosx/tkMacOSXDraw.c | 200 |
1 files changed, 109 insertions, 91 deletions
diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index ff35da3..e93f9cf 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -283,7 +283,8 @@ Tk_MacOSXGetCGContextForDrawable( * * TkMacOSXDrawCGImage -- * - * Draw CG image into drawable. + * Draw CG image into drawable. The entire image is used, and will + * be rescaled if its dimensions do not equal dstBounds.size. * * Results: * None. @@ -302,25 +303,11 @@ TkMacOSXDrawCGImage( CGImageRef image, unsigned long imageForeground, unsigned long imageBackground, - CGRect imageBounds, - CGRect srcBounds, CGRect dstBounds) { MacDrawable *macDraw = (MacDrawable *)d; if (macDraw && context && image) { - CGImageRef subImage = NULL; - - if (!CGRectEqualToRect(imageBounds, srcBounds)) { - if (!CGRectContainsRect(imageBounds, srcBounds)) { - TkMacOSXDbgMsg("Mismatch of sub CGImage bounds"); - } - subImage = CGImageCreateWithImageInRect(image, CGRectOffset( - srcBounds, -imageBounds.origin.x, -imageBounds.origin.y)); - if (subImage) { - image = subImage; - } - } dstBounds = CGRectOffset(dstBounds, macDraw->xOff, macDraw->yOff); if (CGImageIsMask(image)) { if (macDraw->flags & TK_IS_BW_PIXMAP) { @@ -369,9 +356,6 @@ TkMacOSXDrawCGImage( CGContextDrawImage(context, dstBounds, image); CGContextRestoreGState(context); #endif /* TK_MAC_DEBUG_IMAGE_DRAWING */ - if (subImage) { - CFRelease(subImage); - } } else { TkMacOSXDbgMsg("Drawing of empty CGImage requested"); } @@ -434,16 +418,16 @@ XDrawLines( } } - /* - * In the case of closed polylines, the first and last points are the - * same. We want miter or bevel join be rendered also at this point, - * this needs telling CoreGraphics that the path is closed. - */ + /* + * In the case of closed polylines, the first and last points are the + * same. We want miter or bevel join be rendered also at this point, + * this needs telling CoreGraphics that the path is closed. + */ - if ((points[0].x == points[npoints-1].x) && - (points[0].y == points[npoints-1].y)) { - CGContextClosePath(dc.context); - } + if ((points[0].x == points[npoints-1].x) && + (points[0].y == points[npoints-1].y)) { + CGContextClosePath(dc.context); + } CGContextStrokePath(dc.context); } TkMacOSXRestoreDrawingContext(&dc); @@ -1093,6 +1077,23 @@ XFillArcs( * Results: * Returns 0 if the scroll generated no additional damage. Otherwise, sets * the region that needs to be repainted after scrolling and returns 1. + * When drawRect was in use, this function used the now deprecated + * scrollRect method of NSView. With the current updateLayer + * implementation, using a CGImage as the view's backing layer, we are + * able to use XCopyArea. But both implementations are incomplete. + * They return a damage area which is just the source rectangle minus + * destination rectangle. Other platforms, e.g. Windows, where + * this function is essentially provided by the windowing system, + * are able to add to the damage region the bounding rectangles of + * all subwindows which meet the source rectangle, even if they are + * contained in the destination rectangle. The information needed + * to do that is not available in this module, as far as I know. + * + * In fact, the Text widget is the only one which calls this + * function, and textDisp.c compensates for this defect by using + * macOS-specific code. This is possible because access to the + * list of all embedded windows in a Text widget is available in + * that module. * * Side effects: * Scrolls the bits in the window. @@ -1103,36 +1104,21 @@ XFillArcs( int TkScrollWindow( Tk_Window tkwin, /* The window to be scrolled. */ - TCL_UNUSED(GC), /* GC for window to be scrolled. */ + GC gc, /* GC for window to be scrolled. */ int x, int y, /* Position rectangle to be scrolled. */ int width, int height, int dx, int dy, /* Distance rectangle should be moved. */ Region damageRgn) /* Region to accumulate damage in. */ { Drawable drawable = Tk_WindowId(tkwin); - MacDrawable *macDraw = (MacDrawable *)drawable; - TKContentView *view = (TKContentView *)TkMacOSXGetNSViewForDrawable(macDraw); HIShapeRef srcRgn, dstRgn; HIMutableShapeRef dmgRgn = HIShapeCreateMutable(); - NSRect bounds, viewSrcRect, srcRect, dstRect; + NSRect srcRect, dstRect; int result = 0; - if (view) { - - /* - * Get the scroll area in NSView coordinates (origin at bottom left). - */ - - bounds = [view bounds]; - viewSrcRect = NSMakeRect(macDraw->xOff + x, - bounds.size.height - height - (macDraw->yOff + y), - width, height); - - /* - * Scroll the rectangle. - */ - - [view scrollRect:viewSrcRect by:NSMakeSize(dx, -dy)]; + // Should behave more like TkScrollWindow on other platforms + if (XCopyArea(Tk_Display(tkwin), drawable, drawable, gc, x, y, + (unsigned)width, (unsigned)height, x+dx, y+dy) == Success) { /* * Compute the damage region, using Tk coordinates (origin at top left). @@ -1237,14 +1223,19 @@ TkMacOSXSetupDrawingContext( * Intersect the drawable's clipping region with the region stored in the * X GC. If the resulting region is empty, don't do any drawing. */ - +//#if 0 // disable clipping (almost works, but windows can open up blank) dc.clipRgn = TkMacOSXGetClipRgn(d); ClipToGC(d, gc, &dc.clipRgn); if (dc.clipRgn && HIShapeIsEmpty(dc.clipRgn)) { + /* + * Things are probably not set up for drawing yet. Request a call to + * updateLayer and return failure. + */ canDraw = false; + [view setNeedsDisplay:YES]; goto end; } - +//#endif //disable clipping /* * If the drawable already has a CGContext, use it. Otherwise, we must be * drawing to a window and we use the current context of its ContentView. @@ -1252,53 +1243,62 @@ TkMacOSXSetupDrawingContext( dc.context = TkMacOSXGetCGContextForDrawable(d); if (!dc.context) { - NSRect drawingBounds, currentBounds; dc.view = view; - dc.context = GET_CGCONTEXT; + dc.context = view.tkLayerBitmapContext; if (dc.clipRgn) { CGRect clipBounds; CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0, .ty = [view bounds].size.height}; HIShapeGetBounds(dc.clipRgn, &clipBounds); clipBounds = CGRectApplyAffineTransform(clipBounds, t); - drawingBounds = NSRectFromCGRect(clipBounds); - } else { - drawingBounds = [view bounds]; } /* - * We can only draw into the NSView which is the current focusView. - * When the current [NSView focusView] is nil, the CGContext for - * [NSGraphicsContext currentContext] is nil. Otherwise the current - * CGContext draws into the current focusView. An NSView is guaranteed - * to be the focusView when its drawRect or setFrame methods are - * running. Prior to OSX 10.14 it was also possible to call the - * lockFocus method to force an NSView to become the current focusView. - * But that method was deprecated in 10.14 and so is no longer used by - * Tk. Instead, if the view is not the current focusView then we add - * the drawing bounds to its dirty rectangle and return false. The - * part of the view inside the drawing bounds will get redrawn during - * the next call to its drawRect method. + * Workaround for an Apple bug. + * + * Without the block below, ttk frames, labelframes and labels do not + * get the correct background color on macOS 12.5 after the appearance + * changes. This function is only called when drawing, so we know that + * our view is the focus view. Even though the effective appearance of + * the view has been changed, the currentAppearance, i.e. the + * appearance that will be used for drawing, may not have been changed + * to match. + * + * Prior to macOS 12.0 the currentAppearance property of NSAppearance + * was settable. In macOS 12.0 currentAppearance was deprecated and + * replaced by the read-only property currentDrawingAppearance. The + * ttk color issues are fixed by setting the currentAppearance to + * the effectiveAppearance of the view. So we are forced to use this + * deprecated function until Apple fixes this. + * + * It is a mystery why this only affects the ttk widgets. A possible + * clue is that when drawing a ttk widget this function is called with + * a NULL gc, whereas the gc is non-null when it is called for drawing + * a Tk widget. This means that the CGContext setup below is not done + * for ttk widgets. Perhaps that setup triggers an update of the + * currentAppearance property, but that has not been verified. */ - if (view != [NSView focusView]) { - [view addTkDirtyRect:drawingBounds]; - canDraw = false; - goto end; - } - - /* - * Drawing will also fail when the view is the current focusView but - * the clipping rectangle set by drawRect does not contain the clipping - * region of our drawing context. (See bug [2a61eca3a8].) If part of - * the drawing bounds will be clipped then we draw whatever we can, but - * we also add the drawing bounds to the view's dirty rectangle so it - * will get redrawn in the next call to its drawRect method. - */ + if (@available(macOS 12.0, *)) { +#if MAC_OS_X_VERSION_MAX_ALLOWED > 120000 + NSAppearance *current = NSAppearance.currentDrawingAppearance; + NSAppearance *effective = view.effectiveAppearance; + if( current != effective) { + // printf("Appearances are out of sync!\n"); + // Deprecations be damned! + NSAppearance.currentAppearance = effective; + } +#endif + } else { + /* + *It is not clear if this is a problem before macos 12.0, but + * we might as well do the update anyway. + */ - currentBounds = NSRectFromCGRect(CGContextGetClipBoundingBox(dc.context)); - if (!NSContainsRect(currentBounds, drawingBounds)) { - [view addTkDirtyRect:drawingBounds]; +#if MAC_OS_X_VERSION_MIN_REQUIRED < 120000 +/* currentAppearance is not deprecated. */ + NSAppearance.currentAppearance = view.effectiveAppearance; +#endif } } @@ -1324,6 +1324,7 @@ TkMacOSXSetupDrawingContext( }; CGContextConcatCTM(dc.context, t); } +//#if 0 // disable clipping if (dc.clipRgn) { #ifdef TK_MAC_DEBUG_DRAWING @@ -1369,6 +1370,8 @@ TkMacOSXSetupDrawingContext( CGContextClipToRect(dc.context, r); } } +//#endif //disable clipping + if (gc) { static const CGLineCap cgCap[] = { [CapNotLast] = kCGLineCapButt, @@ -1475,6 +1478,13 @@ TkMacOSXRestoreDrawingContext( dcPtr->clipRgn = NULL; } + /* + * Mark the view as needing to be redisplayed, since we have drawn on its + * backing layer. + */ + + [dcPtr->view setNeedsDisplay:YES]; + #ifdef TK_MAC_DEBUG bzero(dcPtr, sizeof(TkMacOSXDrawingContext)); #endif @@ -1524,10 +1534,13 @@ TkMacOSXGetClipRgn( } if (macDraw->drawRgn) { + // The drawRgn is the visRgn intersected with a rectangle which + // may be smaller than the widget bounds. clipRgn = HIShapeCreateCopy(macDraw->drawRgn); } else if (macDraw->visRgn) { clipRgn = HIShapeCreateCopy(macDraw->visRgn); } + // A NULL clipRgn does not allow any drawing at all. return clipRgn; } @@ -1537,7 +1550,9 @@ TkMacOSXGetClipRgn( * Tk_ClipDrawableToRect -- * * Clip all drawing into the drawable d to the given rectangle. If width - * or height are negative, reset to no clipping. + * or height are negative, reset to no clipping. This is called by the + * Text widget to display each DLine, and by the Canvas widget when it + * is updating a sub rectangle in the canvas. * * Results: * None. @@ -1566,7 +1581,10 @@ Tk_ClipDrawableToRect( width, height); HIShapeRef drawRgn = HIShapeCreateWithRect(&clipRect); - if (macDraw->winPtr && macDraw->flags & TK_CLIP_INVALID) { + // When drawing a Text widget we can reuse the + // clipping region for different DLines, so we don't want to + // update unless necessary. + if (macDraw->winPtr && (macDraw->flags & TK_CLIP_INVALID)) { TkMacOSXUpdateClipRgn(macDraw->winPtr); } if (macDraw->visRgn) { @@ -1657,7 +1675,7 @@ TkMacOSXMakeStippleMap( * * On the Macintosh, this puts a 1 pixel border in the bgGC color between * the widget and the focus ring, except in the case where highlightWidth - * is 1, in which case the border is left out. + * is 0 or 1, in which case the border is left out. * * For proper Mac L&F, use highlightWidth of 3. * @@ -1672,19 +1690,19 @@ TkMacOSXMakeStippleMap( */ void -Tk_DrawHighlightBorder ( +Tk_DrawHighlightBorder( Tk_Window tkwin, GC fgGC, GC bgGC, int highlightWidth, Drawable drawable) { - if (highlightWidth == 1) { - TkDrawInsetFocusHighlight (tkwin, fgGC, highlightWidth, drawable, 0); + if (highlightWidth <= 1) { + TkDrawInsetFocusHighlight(tkwin, fgGC, 1, drawable, 0); } else { - TkDrawInsetFocusHighlight (tkwin, bgGC, highlightWidth, drawable, 0); + TkDrawInsetFocusHighlight(tkwin, bgGC, highlightWidth, drawable, 0); if (fgGC != bgGC) { - TkDrawInsetFocusHighlight (tkwin, fgGC, highlightWidth - 1, + TkDrawInsetFocusHighlight(tkwin, fgGC, highlightWidth - 1, drawable, 0); } } |
