Textured Tabbed Windows

December 5, 2016

I didn't want to begin a paragraph, much less a whole article, with a lowercase character, so this is an unnecessary lead-in to what I originally wanted to say: macOS 10.12 Sierra has introduced a new feature called automatic window tabbing. Apple's SOP nowadays is to make new mac features — err, Mac features — opt-out rather than opt-in. Thus, automatic window tabbing is supposed to Just Work.™ In other words, it almost just works. I discovered through the magic of git-bisect that automatic window tabbing does not work when you programmatically create a textured window. The main menu items "Show Tab Bar", "Merge All Windows", etc., are never enabled for the window. Textured windows, which do not have a toolbar at all (or from another perspective, have toolbar all the way down), are specified by the style mask NSTexturedBackgroundWindowMask. Rather, they were specified by NSTexturedBackgroundWindowMask until that was deprecated in favor of NSWindowStyleMaskTexturedBackground, because why not.

Why would you create a textured window programmatically? Setting aside the question of why you would create a textured window, you might create a window programmatically because you are working without a nib. I tried to reproduce the window tabbing problem with a textured window in a nib, and at first I couldn't. Wondering what kind of special sauce enabled window tabbing, I opened the nib (xib) file in a text editor, as you do. I couldn't find any property of the window related to tabs, but I did notice one difference from my own windows: the contentView of the nib window had wantsLayer turned on. Sure enough, when I called [[window contentView] setWantsLayer:YES] on my programmatic textured window, it enabled automatic window tabbing! Needless to say, this is not documented anywhere. Except here, now.

Giving the window's content view a Core Animation layer backing may have unintended or undesirable consequences in your window, so is there another workaround for the tabbing problem with textured windows? If you call [window setTabbingMode:NSWindowTabbingModePreferred], this will force the textured window to support window tabbing. However, it will no longer be automatic window tabbing; indeed, the window will be tabbed when you first show it, which is surely not what you want. According to the AppKit release notes for macOS 10.12, "A window’s tabbingMode is only checked when a window is about to be shown, and the tabbingMode should be updated before the window is ordered in." This is not entirely accurate. If you show a window with the default NSWindowTabbingModeAutomatic, and then you set it to NSWindowTabbingModePreferred after it gets shown, the appearance of the window will not change immediately, but … the window will immediately support the tabbing-related main menu items! I haven't attempted a full implementation of this, so you may need to be careful about resetting the tabbingMode if the window gets ordered out and ordered back in. Also, there's the userTabbingPreference to consider, which is also discussed in the AppKit release notes.

It should be noted that non-textured windows do not require a layer-backed content view in order to support automatic window tabbing, so I don't know the reason for the difference. And the absence of layer backing doesn't seem to be an obstacle for textured windows, because we can set the tabbingMode programmatically.