The Strange Case of the OS X System Preferences Window Width

The OS X System Preferences app is something like a wrapper app. In itself, it doesn’t do more than to present all installed system preference panes and these panes then allow you to configure your OS X installation. In addition to system-provided preference panes that come with OS X, additional preference panes can be installed by the user, or by applications.

Xcode comes with a template for such custom preference panes and there is a Preference Pane Programming Guide. Custom preference panes aren’t full-scale applications but rather a user interface to adjust some settings, wrapped in a bundle, so they can’t be distributed via the Mac App Store as such, but could only be an accessory to an application. It might be for this reason that custom preference panes don’t seem to be too popular nowadays.

When I prepared a preference-pane-only app recently, I stumbled upon one inconsistency in Apple’s Preference Pane Programming Guide. The guide states in its section “The Preference Application”, as does the Xcode template, that the

System Preferences window has a fixed width (668 pixels)[.]

and fancies to also give another value in its section “Creating a Preference Pane Bundle”:

For System Preferences, the window should not be more than 595 pixels wide.

Both values didn’t match with what I saw on my computer, so I investigated the issue. Apparently, the System Preferences window on my computer running OS X El Capitan was considerably wider than 668 (or even 595) pixels. Some research later it turned out that the width of the System Preferences window depends on the system language: The window will be 668 pixels wide when English is the primary language, but will have a different width for other languages. For example, with German set as primary language, as it is on my computer, the window is 762 pixels wide:

prefpanewidth

Setting the language to Russian, it is 780 pixels wide, setting it to Italian, it is 750 pixels wide etc. I checked previous OS X versions and found that this flexible-width approach is around since OS X Yosemite, but is still not mentioned in any Apple documentation. While Apple’s preference panes that come with OS X dynamically adjust their width to the actual width of the System Preferences window, this is not true for any custom preference pane, that will instead be displayed centered with more or less wide blank margins. For example, the popular Growl app comes with a custom preference pane that on my computer with German system language displays like this:

growl

This doesn’t look too professional so I thought of making my preference pane dynamically resize itself, as do the system-provided preference panes. As the Preference Pane Programming Guide still assumes that preference panes have a fixed width (while it isn’t too sure about which width this is, see above), it naturally doesn’t mention any way how to achieve this. Also, the NSPreferencePane class that is to be used to implement a custom preference pane doesn’t offer any way to retrieve the actual width of the system preferences window.

I then came up with a solution which takes advantage of the fact that the code of the custom preference pane will be executed by the System Preferences app and will therefore also have its process identifier. We can now use this process identifier to read out the width of its window from the obscure Quartz Window Services CGWindowListCopyWindowInfo function. This is what below snippet does:

-(float)preferenceWindowWidth
{
    float result = 668.0; // default in case something goes wrong
    NSMutableArray *windows = (NSMutableArray *)CFBridgingRelease(CGWindowListCopyWindowInfo
      (kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID));
    int myProcessIdentifier = [[NSProcessInfo processInfo] processIdentifier];
    BOOL foundWidth = NO;
    for (NSDictionary *window in windows) {
        int windowProcessIdentifier = [[window objectForKey:@"kCGWindowOwnerPID"] intValue];
        if ((myProcessIdentifier == windowProcessIdentifier) && (!foundWidth)) {
            foundWidth = YES;
            NSDictionary *bounds = [window objectForKey:@"kCGWindowBounds"];
            result = [[bounds valueForKey:@"Width"] floatValue];
        }
    }
    return result;
}

In the mainViewDidLoad method of the custom preference pane code we can use the retrieved width value to resize the width of our preference pane’s main view:

-(void)mainViewDidLoad
{
    NSSize size = self.mainView.frame.size;
    size.width = [self preferenceWindowWidth];
    [[self mainView] setFrameSize:size];
}

I’ve filed a bug report with Apple to at least update the Preference Pane Programming Guide.

Check if Mission Control is Active

Unfortunately, Apple has never provided a public API to programmatically check if Mission Control is active. There are not too many use cases for such a check, but I have encountered one.

One of my apps is using a global hot key (using the DDHotkey library) so that the user can press the hot key and the app’s main window is displayed. However, if Mission Control is active while the hot key is pressed by the user, the app’s main window will be displayed but has no input focus, making it impossible to enter any text, click on any button etc.

Continue reading “Check if Mission Control is Active”

Arrow Buttons

We all know that the user interfaces of OS X apps are becoming more and more differentiated. Apple as well as third party developers slowly move away from the once authoritative OS X Human Interface Guidelines and create not only new user interface elements, but a whole new experience of interacting with applications, at least partly borrowing from iOS innovations.

It is not that easy for an Mac indie developer to follow this trend if he or she doesn’t have at least some graphical and design talent. It is therefore not by chance that so far I have been rather traditional in my approaches. Now, however, I used some free time over the holidays and created a set of custom button controls.

Continue reading “Arrow Buttons”

Collecting Code

Here and there I’ve posted several source code examples on this blog. While until now this code was available only within the respective blog posts, I’ve now collected all published examples and for convenient access made them available on a designated page.

The Launch At Login Sandbox Project

Sometimes it is useful for an app to launch at login. While a user can always accomplish this in the system preferences, having the possibility to turn on auto-launching inside the app is better. While in the glorious past this could be implemented in a number of ways, now in the age of the sandbox it is quite tricky to achieve. This tutorial will show how. It is a more comprehensive sequel to Delite Studio’s tutorial from October 2011 and /dev/random’s tutorial from January 2012.

Continue reading “The Launch At Login Sandbox Project”