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.

Adobe Flash Player OS X Update Script

Perhaps the Adobe Flash Player will not be around anymore in a few years. Until then, some of us are still using it, people like me. I don’t fancy the update mechanisms it offers, though, neither automatic updates nor the annoying pop-up thing.

There has been some Bash code around since a long time on how to update the Adobe Flash Player from the OS X command line. I have taken some of it and adapted it to current OS X El Capitan. When called, it will check if an update is available and, if it is, install it.

[Snippet] Rebuild Finder’s Context Menu

I have a lot of snippets that I found somewhere on the web, ages ago, or created myself. They solve small problems and range from making OS X working better to Cocoa development issues. Until now I didn’t have any central location for storing them, but will now begin publishing them on my blog, not least with the hidden agenda to not have them stored locally anymore.

Today: After each update of an app I bought in the Mac App Store, my Finder’s context menu shows duplicate entries for that app. Clean up this mess with two (well-known) Terminal commands.

Continue reading “[Snippet] Rebuild Finder’s Context Menu”