There are times when display dialog and display alert are not quite enough. If you’re running Mavericks or Yosemite, AppleScriptObjC gives you more options, in particular by giving you access to extra possibilities with alerts.
Two simple possibilities are the ability to have more than three buttons – something not really needed often, but sometimes justified – and the ability to include a Do not show this message again checkbox, known as a suppression button. A more complex option is to include an accessory view – an area above the buttons where you can add extra controls.
Alerts also lend themselves more to being coded as handlers, making them ideal to store in script libraries. By storing them in script libraries, you also make them accessible from Mavericks. (In fact, in Mavericks they have to be in libraries.)
Before getting down to code, it’s probably worth discussing the process. First, you create a new NSAlert, using either the new() or alloc() and init() methods. It’s roughly equivalent to “make new…” in traditional AppleScript. You can then set various properties – alertStyle, messageText, informativeText and showsSuppressionButton. Buttons are added one at a time, from right to left, using addButtonWithTitle:. The right-most button is always the default button, and a button called “Cancel” (or localized equivalent) responds to the escape key or command-…
After that, you show the dialog by calling runModal(), which returns a number that relates to the button that was pressed.
The handler below takes an integer for the style, and a list of button titles from left-to-right, to match the standard AppleScript order; the code then reverses the order before adding them. It uses the result of runModal() to work out the button title, and it is ignoring the suppression button for the moment.
Before you run the code, a warning: ASObjC code that involves drawing stuff on screen, like this, has to be run in the foreground on what is known as the main thread. If it is run on a background thread, the likely result is that the app will crash. Scripts are nearly always run on the main thread – the one exception being in script editors, where they are usually run on background threads. So when you are trying out this code, you need to make sure you run it in the foreground.
In Script Editor, you run a script in the foreground by holding the control key when running it, or pressing control-command-R. You can see the Run command change names in the Script menu when you hold down the control key. In ASObjC Explorer you check the ‘Run in foreground’ checkbox at the bottom of the script window.
Because it is easy to forget when you are testing, especially in Script Editor, I’ve added some extra code that checks if it is running in the foreground, and if not shows a dialog and stops, rather than letting the app crash. This simply asks the NSThread class if the current code is on the main thread.
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit" -- required for NSAlert
-- check we are running in foreground
if not (current application's NSThread's isMainThread()) as boolean then
display alert "This script must be run from the main thread." buttons {"Cancel"} as critical
error number -128
end if
-- for styleNum, 0 = warning, 1 = informational, 2 = critical
on displayAlert:mainText message:theExplanaton asStyle:styleNum buttons:buttonsList
set buttonsList to reverse of buttonsList -- because they get added in reverse order cf AS
-- create an alert
set theAlert to current application's NSAlert's alloc()'s init()
-- set up the alert
tell theAlert
its setAlertStyle:styleNum
its setMessageText:mainText
its setInformativeText:theExplanaton
repeat with anEntry in buttonsList
(its addButtonWithTitle:anEntry)
end repeat
end tell
-- show the alert
set returnCode to theAlert's runModal()
-- get values after alert is closed
set buttonNumber to returnCode mod 1000 + 1 -- where 1 = right-most button
set buttonName to item buttonNumber of buttonsList
return buttonName
end displayAlert:message:asStyle:buttons:
set buttonName to (my displayAlert:"Stay alert" message:"This is the 9 o'clock news" asStyle:2 buttons:{"Cancel", "Maybe", "Possibly", "Perhaps", "Probably", "OK"})