Parmanoir

CoreUI can paint pretty big

If you've been using the Safari 4 beta, you've discovered the full page zoom : instead of changing the font size, Safari now zooms everything on the page, from images to buttons. This produces ultra sharp text, dreadfully pixelated images, and … ultra sharp controls !

The controls are drawn by CoreUI, a private framework new to Leopard. To draw controls at any size, CoreUI uses xml "recipes" to mix BIG pictures and PDFs. We can't access CoreUI directly, but as WebKit is open source, we can peek to see how Safari does it !

Image:CoreUI Embiggened.png

Here's the Safari recipe :

  • change the scale of the graphics context
  • scale back the drawing rect by the same amount
  • ask an NSButtonCell to paint itself

… Et voilà !

// We're in a custom view, derived right from NSView
- (void)drawRect:(NSRect)rect
{
	// Allocate a button cell
	id cell = [NSButtonCell new];
	[cell setButtonType:NSMomentaryPushInButton];
	[cell setBezelStyle:NSRoundedBezelStyle];

	// Compute scale - scale the button to fit width
	float scale = (rect.size.width / [cell cellSize].width);

	// Apply scale to the graphics context
	CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];;
	CGContextScaleCTM(context, scale, scale);

	// Scale down drawing rect
	rect.size.width /= scale;
	rect.size.height /= scale;
	
	// Draw rect
	[cell drawWithFrame:rect inView:self];

	// We're done !	
	[cell release];
}

Sample Code Image:iconZip.pngEmbiggened paints a big button. Click to cycle through button styles.

Qwerty Denzel
2009 03 08

A small niggle. Instead of using the rect argument, which is the region that needs updating, you should ask for the view's bounds.

NSRect bounds = [self bounds]; // Now use this for the full width and height.
Patrick Geiller
2009 03 08

I'm not noticing any difference between rect and [self bounds] ? What's it supposed to do ?

Qwerty Denzel
2009 03 09

The rect is the dirty rectangle that needs redrawing. When you resize the window and the view, the whole view needs updating.

However, a part of the view can also be chosen to redraw, through use of methods such as:

   - (void)setNeedsDisplayInRect:(NSRect)invalidRect

The invalidRect argument is then the rectangle used in the -drawRect: method. (Or if further rectangles are specified later, then a union of all those rectangles.)

By only invalidating what has changed, and then by drawing only what has been invalidated, your drawing becomes more efficient.

Picture where the user is working on some sort of elements in a view, such as selecting a range of text in a text view, or rolling their mouse over a list of buttons and having them highlight. Only a portion of the view is changing and needing to be redrawn.

If you launch the 'Quartz Debug' utility that shipped with your developer tools, you can enable options to flash, or highlight, the areas that are being updated on screen. You'll see that Apple are usually pretty efficient with the amount they redraw.

So coming back to the first point, since using rect is inappropriate, you should use -[NSView bounds] for finding the actual size of the view (in its drawing space).

Apply has a guide on efficient view drawing if you want more information.

Patrick Geiller
2009 03 09

Ah indeed when I move the view on the side to be clipped by the window, redrawing is incorrect. Adding rect = [self bounds] before Scale down drawing rect fixes the issue. Thank you !


Follow me on Twitter
Planet Cocoa
Cocoa.fr

2011 02 22Distance field
2010 07 202Binding through NSApp
2010 05 122Forwarding invocations
2010 02 272Core Image black fringes
2010 02 21Quickest Way to Shell
2010 02 08Who's calling ?
2009 09 2138 ways to use Blocks in Snow Leopard
2009 08 182Bracket Mess
2009 08 124Taming JavascriptCore within and without WebView
2009 04 15Debugging with Activity Monitor
2009 03 25How Core Image Color Tracking works
2009 03 1510Custom NSThemeFrame
2009 03 10Which framework is running ?
2009 03 074CoreUI can paint pretty big
2009 02 18Localization with functions
2009 01 30Did you forget to nest alloc and init?
2009 01 16JSCocoa on the iPhone
2009 01 11Mixing WebView and JavascriptCore
2009 01 09Badge overflow
2009 01 09Find your Garbage Collection leaks with Instruments

Powered by MediaWiki