2009 03 07CoreUI can paint pretty big
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 !
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 Embiggened paints a big button. Click to cycle through button styles.
I'm not noticing any difference between rect and [self bounds] ? What's it supposed to do ?
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.
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 !
A small niggle. Instead of using the
rect
argument, which is the region that needs updating, you should ask for the view's bounds.