How + [NSColor selectedMenuItemColor] magically draws a gradient?

I am implementing a custom view of NSMenuItem that shows a selection as the user mouse over it. To do this, the code calls NSRectFill after setting [NSColor selectedMenuItemColor] as the active color. However, I noticed that the result is not just a solid color - in fact, it draws a gradient . Very nice, but I wonder how this "magic" works, i.e. If I want to define my own color, which I did not just draw solid, how would I?

+4
source share
2 answers

I don’t know how this works, but I found a way to replicate the behavior using special gradients (or any other drawing operations). The β€œtrick” is to use CGPatternRef , which allows you to specify a callback function to draw a template. Usually this callback function draws one β€œcell” of the template, but you can simply specify a very large size of the picture (for example, CGFLOAT_MAX ) to be able to fill the entire area with one callback call.

To demonstrate the technique, here is the NSColor category, which allows you to create color from NSGradient . When you set color this color and then use it to fill the area, the gradient is drawn (linear, from bottom to top, but you can easily change this). This even works for stroking tracks or filling non-rectangular paths, for example [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(0, 0, 100, 100)] fill] , because NSBezierPath automatically clamps the picture.

 //NSColor+Gradient.h #import <Cocoa/Cocoa.h> @interface NSColor (Gradient) + (NSColor *)my_gradientColorWithGradient:(NSGradient *)gradient; @end //NSColor+Gradient.m #import "NSColor+Gradient.h" #import <objc/runtime.h> static void DrawGradientPattern(void * info, CGContextRef context) { NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; CGRect clipRect = CGContextGetClipBoundingBox(context); [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]]; NSGradient *gradient = (__bridge NSGradient *)info; [gradient drawInRect:NSRectFromCGRect(clipRect) angle:90.0]; [NSGraphicsContext setCurrentContext:currentContext]; } @implementation NSColor (Gradient) + (NSColor *)my_gradientColorWithGradient:(NSGradient *)gradient { CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern(NULL); CGPatternCallbacks callbacks; callbacks.drawPattern = &DrawGradientPattern; callbacks.releaseInfo = NULL; CGPatternRef pattern = CGPatternCreate((__bridge void *)(gradient), CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX), CGAffineTransformIdentity, CGFLOAT_MAX, CGFLOAT_MAX, kCGPatternTilingConstantSpacing, true, &callbacks); const CGFloat components[4] = {1.0, 1.0, 1.0, 1.0}; CGColorRef cgColor = CGColorCreateWithPattern(colorSpace, pattern, components); CGColorSpaceRelease(colorSpace); NSColor *color = [NSColor colorWithCGColor:cgColor]; objc_setAssociatedObject(color, "gradient", gradient, OBJC_ASSOCIATION_RETAIN); return color; } @end 

Usage example:

 NSArray *colors = @[ [NSColor redColor], [NSColor blueColor] ]; NSGradient *gradient = [[NSGradient alloc] initWithColors:colors]; NSColor *gradientColor = [NSColor my_gradientColorWithGradient:gradient]; [gradientColor set]; NSRectFill(NSMakeRect(0, 0, 100, 100)); [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(100, 0, 100, 100)] fill]; 

Result:

Screen shot

+2
source

My best guess is that it is defined as a kind of image of an image , however this does not fully answer my question, because it looks as if these patterns are usually stretched by tiles, not stretched.

This is confirmed by an Apple post engineer on cocoa -dev , which states:

[[NSColor selectedMenuItemColor] installed]; NSRectFill (someRect);

This works because selectedMenuItemColor is a template that happens to draw a gradient. You can just as easily draw almost anything with a drawing [...]

He does not specify how these patterns can be drawn stretched rather than tiled, although as a highlighted menu background. Another post in this thread claims to be a β€œ special case in drawing code,” but it could just speculate.

0
source

All Articles