NSAlert new and old

Although I don’t have macOS 26 Tahoe yet, word has it that the NSAlert layout has been changed to something a little less ugly, although it looks like Apple just changed the alignment.

I’ve been playing with the fonts and colors of the NSAlert text fields for a while, and during testing have noticed that the old formatting is still in there, and pops up from time to time for various combinations of the number of buttons and combined text field sizes. I have a minimal alert that fakes getting rid of the icon by covering the space with a new header text field, so if anyone has used the new layout, it might be interesting to see if much has actually been changed.

The following positions the header text field over wherever the icon imageView gets placed and works (in Sonoma anyway), but (depending on the font used) increasing the size of the font, using the message text or adding to the informative text, or adding a fifth button (!) is enough to revert to the (older) older layout:

use framework "Foundation"
use scripting additions

# performSelectorOnMainThread doesn't return anything, so put results in some properties
property response : missing value -- the name of the button pressed
property failure : missing value -- {errorMessage:string, errorNumber:integer}

on run
    if current application's NSThread's isMainThread() as boolean then -- app
        doAlertStuff()
    else
        my performSelectorOnMainThread:"doAlertStuff" withObject:(missing value) waitUntilDone:true -- script
    end if
    if failure is not missing value then error failure's errorMessage number failure's errorNumber
    return response
end run

to doAlertStuff() -- example
    set loremText to "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin et dolor ante. Nunc lacinia ipsum quis turpis sollicitudin mollis. Donec laoreet tortor et ligula feugiat suscipit. Aliquam quis ullamcorper orci." & return
    # set loremText to text 1 thru word 7 of loremText -- shorter string
    showAlert for loremText given header:"Header" & return & "Text", buttons:{"One", "Two", "Three", "Four"} --, "Five"}
end doAlertStuff

# Minimal alert with new text field located to cover space used for icon imageView.
# NSalert layout is not stable for certain combinations of buttons and textField size - adjust accordingly.
to showAlert for (infoText as text) given header:(header as text) : "Testing", buttons:(buttons as list) : {"OK"} -- given arguments are optional
    try
        tell (current application's NSAlert's alloc()'s init())
            set {imageView, infoField} to {item 4, item 6} of its |window|'s contentView's subviews
            tell imageView
                its setHidden:true -- comment to show default icon for alignment
                # its setEditable:true -- uncomment to allow tab selection for alignment
            end tell
            its setMessageText:"" -- not used, just use header and info field
            tell infoField
                its setFont:(current application's NSFont's fontWithName:"Futura Medium Italic" |size|:14) -- or whatever
                its setTextColor:(current application's NSColor's blueColor) -- or whatever
                # its setBordered:true -- uncomment to show a border for alignment
            end tell
            its setInformativeText:infoText
            repeat with aButton in buttons
                (its addButtonWithTitle:aButton) -- adding buttons starts the response at 1000
            end repeat
            its layout() -- layout the alert to update positions
            set origin to first item of imageView's frame() -- to locate the text field
            set |size| to second item of infoField's frame() -- to size the text field
            its (|window|'s contentView's addSubview:(my (makeLabelField at {origin, |size|} for header)))
            set response to item ((its runModal() as integer) - 999) of buttons -- first button returns 1000
        end tell
    on error errmess number errnum
        set failure to {errorMessage:errmess, errorNumber:errnum}
    end try
end showAlert


# Make label text field with same width as info text field and height to cover iconView.
to makeLabelField at (location as list) for (theString as text)
    set {{x, y}, {w, h}} to location
    tell (current application's NSTextField's labelWithString:theString)
        its setFrame:{{20, y - 3}, {w, 70}}
        its setAlignment:(current application's NSTextAlignmentCenter) -- keep header centered
        its setLineBreakMode:(current application's NSLineBreakByWordWrapping)
        its setFont:(current application's NSFont's fontWithName:"Marker Felt" |size|:32) -- or whatever
        its setTextColor:(current application's NSColor's redColor) -- or whatever
        # its setBordered:true -- uncomment to show a border for alignment
        return it
    end tell
end makeLabelField

The way the new text field is positioned obviously doesn’t work when the old style is triggered, which may or may not happen with the newer alert, depending on if Apple really did anything other than change the alignment to NSTextAlignmentNatural.