TIP 655: Support the styleMask property for NSWindow and NSPanel in Aqua

Bounty program for improvements to Tcl and certain Tcl packages.
Author:         Marc Culler
State:          Draft
Type:           Project
Vote:           Pending
Created:        28-Jan-2023
Keywords:       Tk, Aqua
Tcl-Version:    8.7
Tk-Branch: 655


This TIP applies only to the Aqua port of Tk. It proposes adding several new platform-specific options to the wm attributes command for the Aqua window manager.


The Aqua window manager used by macOS provides a number of different styles for the frame and titlebar of a window. For example, the system color and font selection dialogs use the NSPanel subclass of NSWindow which has a narrower title bar and exhibits different behaviors - an NSPanel floats above all other windows, including windows belonging to other applications, and clicking on the panel when the app is inactive does not activate it. As another example, modal dialogs windows associated with a normal document window often have no title bar and feature rounded corners.

Many of these styles existed in the Carbon API and Tk has provided access to some of these Carbon features for a long time via the ::tk::unsupported::MacWindowStyle command. Many of the Carbon API calls continue to work, to some extent, within the current Cocoa API that replaced Carbon for OSX. This part of the Carbon API was extremely ornate and complex, so full access from Tk was not practical. In Cocoa the API was simplified considerably. Objects in the NSWindow class (as well as its NSPanel subclass) have an integer-valued styleMask property whose bits control the various style choices for window frames and title bars. The core goal of this TIP is to provide direct control over the styleMask bits with a command in the main tk namespace rather than in ::tk::unsupported. This is handled by giving a Tk name to each bit and by adding a new -stylemask option to the wm attributes command which can be used to set the values of the bits. Changing a bit causes an instant change to the window decorations.

On the other hand, the choice of whether the underlying Aqua window for a Tk Window should be in the NSWindow class or the NSPanel class must be made before the NSWindow or NSPanel is instantiated and cannot be changed. The way that this is handled by ::tk::unsupported::MacWindowStyle is unfortunate. The implementation essentially creates an intentional race condition. The toplevel must be created first. Then the user must call ::tk::unsupported::MacWindowStyle style to specify the style. And that must be done in the brief moment after the toplevel command has created a TkWindow with the specified pathname and before that TkWindow has actually been mapped. The user is left to handle this squeeze play on her own. In the interpreter it is necessary to put both commands on the same line separated by a semicolon.

This tip does away with the race condition. The choice of which window to use is made with the new wm attributes -class option, which can take two possible values: nswindow and nspanel. The implementation of the option allows the -type option to be specified for a pathname that does not identify a window. In that case the value of the option is cached in a Tcl hash table and looked up when the Aqua window is created in the course of mapping a Tk window with that pathname. It is an error to change the type of a window for which the underlying Aqua window already exists.

A different, logically independent, aspect of this tip is to expose the NSWindow attributes tabbingIdentifier and tabbingMode as wm attributes. This allows using tabbed windows in a Tk application. A partial implementation of this, which did not provide enough control to make it practical to use tabs in Tk applications, has been removed from the ::tk::unsupported::MacWindowStyle command.


Six new options are added to the wm attributes command for macOS. These are:

wm attributes window -appearance ?auto|aqua|darkaqua*?

wm attributes window -class ?nswindow|nspanel?

wm attributes window -isdark

wm attributes window -stylemask ?bitnamelist?

wm attributes window -tabbingid ?string?

wm attributes window -tabbingmode ?auto|preferred|disallowed?

When using the options -class, -tabbingid, or -tabbingmode it is allowed for the pathname to not be associated to an existing window. In that case the option value is cached, and is used when a window with that pathname is created later. It is an error to modify the class after the underlying macOS window for a toplevel has been instantiated.

The allowed bit names for the bitnamelist are: titled, closable, miniaturizable, resizable, fullsizecontentview, docmodal, utility, nonactivatingpanel, and HUDwindow. Each named bit is set to 1 in the stylemask, and all other bits are set to 0.

When the fullsizecontentview bit is set to 1 it also causes the macOS window to be modified so its title bar is transparent. (Otherwise, drawing in the titlebar area would not be visible.)

The -tabbingid and -tabbingmode options are used to control how toplevel windows are displayed as tabs in a tab group. They set the value of the NSWindow propertied named tabbingIdentifier and tabbingMode. Tabs in the same group must have the same tabbingid, and a toplevel can only be displayed as a tab if either its tabbingmode is preferred or if its tabbingMode is auto and the user has specified in the System Settings that tabs are preferred when opening documents.


For example, to create a resizeable NSPanel with red and yellow buttons in its (narrow) titlebar:

wm attributes .t -class nspanel

toplevel .t

wm attributes .t {titled closable miniaturizable resizable}


This TIP proposes new options for the wm attributes command on Aqua. The effect of some of these can also be acheived by using options available in the tk::unsupported::MacWindowStyle command. The tip does not propose changing that command since it is used, for example, in the Python tkinter module as well as in some standard Tk packages. .

Reference Implementation

These features are implemented in the 655 branch.


This document has been placed in the public domain.