This article is more than 1 year old
Create high-resolution displays for OS X
Cut to the CoreUI
Mac Secrets OK, I lied. Last time I said I'd continue our exploration of symbolic hotkeys. That can wait, though, as since then I found myself buried in the guts of Apple's new CoreUI framework and found a pressing need to talk about it.
What exactly is CoreUI, you ask?
As you may know, Apple is moving towards a fully-scalable, vector-based, resolution independent implementation that lets OS X take full advantage of high-resolution displays. There have been various World Wide Developer Conference (WWDC) workshops devoted to this topic, and it was widely expected that Leopard would complete this particular jigsaw, but it didn't. For a good overview, see John Siracusa's article here.
In essence, CoreUI is a low-level framework that - ultimately - will be responsible for drawing all the user interface widgets in OS X. Among other things, CoreUI will define the look and feel of the Aqua interface. I say "will" and "ultimately" because at the time of writing CoreUI is still something of a work in progress.
Some folks have reported that CoreUI isn't used at all in Leopard, unless you change the scale factor from its default value of 1.0, but this is simplistic. By default, CoreUI is used for drawing a number of user interface elements in Leopard.
Objective-C free
Unlike other undocumented frameworks I've discussed here before, CoreUI isn't implemented in Objective-C. It's actually a C++ class library that makes use of a couple of "art" files contained within the resources directory of CoreUI.framework.
At the end of this piece, I'm going to provide full source code to a small Cocoa application that demonstrates how to browse the images inside SArtFile.bin, one of the two files mentioned. Additionally, I'll show how to make a CoreUI call from Cocoa.
You can see the browser working below. Some of the images I've embedded are bitmaps while others are scalable PDF files such as the NSProgressIndicator "spinner" image shown.
Note: a frequent question in Mac programming forums is how do you draw a larger-than-normal spinner? Just use the scalable image provided, and rotate it through 30 degrees for each animation step. You could even make it rotate backwards if you wanted to visually represent a lengthy "undo" operation.
Examine the 'art' file contents in Core.UI framework with a browser
As you will see from the program interface, images inside SArtFile.bin are identified by number. The current implementation has 209 images and you can download an incomplete list of number meanings from a Mac theming site here.
If you wanted to augment my program, it would make sense to be able to just drag images out of the NSImageView to move them into a folder, or drag into the image view to update a bitmap. This is effectively what the Mac "themers" are doing, courtesy of a couple of Java command-line tools for which there is, as far as I know, no source code.
There are three types of files in the archive: PDF, PNG and TIFF. You can easily identify the type of file you're dealing with by looking at the first few bytes in each file.
My program works by loading the entire SArtFile.bin file into one NSData object, and then pulling out the relevant data for each constituent image. It should be pretty obvious how the archive is formatted but if not, suffice to say that the third and fourth bytes specify the number of images in the file, while the next four bytes specify a 32-bit master offset.
The next set of bytes - byte location eight onwards - comprise a series of 32-bit file offsets that point to information about each image. Eight bytes into this per-image information, you'll find the size of each file's data followed by an offset to the image data itself. This final offset needs to be added to the master offset to retrieve the image data. This should be fairly clear, but if not, consult the project source.
Finally, I promised to explain how to make a direct CoreUI call from Cocoa. The heart of the CoreUI rendering system is a routine called CUIDraw, which looks like this:
extern void CUIDraw (CUIRendererRef r, CGRect rect, CGContextRef ctx, CFDictionaryRef options, CFDictionaryRef * result);
If you want to call CUIDraw from a Cocoa project, be sure to include CoreUI.framework in your project, or it won't link. I also added the following type declaration to keep the compiler happy:
typedef CFTypeRef CUIRendererRef;
You can retrieve a reference to a CUIRenderer object through an undocumented NSWindow class method, like this:
CUIDraw ([NSWindow coreUIRenderer], rect, [[NSGraphicsContext currentContext] graphicsPort], (CFDictionaryRef) coreUIOptions, nil);
The crucial item here, though, is the dictionary that tells CoreUI what you want to draw, and how you want it drawn. There are a huge number of possible keys and values which can be passed through this dictionary-based interface - more than I've got room to mention here.
Just to give you as flavour of what can be done, see the sample file optionsDictionary.txt that I've included with this month's project, which can be downloaded from here. It's not part of the project itself, but it should give you a feel for how things work. The UI widget I've referenced - "littlearrows" - references the bitmaps used by the vertical stepper control. ®