AppleScript's "display dialog" window without text wrapping

AppleScript’s display dialog command opens a dialog window of a fixed width that is independent of the width of the lines of its dialog and default answer text. Any text lines that are too long to fit into their allotted space wrap onto the next line. As described in a post several years ago, this text-wrapping behavior can be avoided by widening the window’s button labels, which has the effect of widening the dialog window and its text fields. The handler described in the current post, called displayDialog, replicates the behavior of AppleScript’s display dialog command in all respects except that it automatically widens the dialog window sufficiently with the button label-widening technique to prevent the dialog and/or default answer text from wrapping. It utilizes information gathered from “reverse engineering” of the standard display dialog window to determine how much widening is needed for a given combination of dialog text, default answer text, and window buttons. It has been found to work successfully for macOS through 10.15 Catalina. Because the solution is user interface-dependent, the handler may need to be updated if Apple introduces significant formatting changes to the display dialog window in future macOS releases.

The handler’s input argument is an AppleScript record whose properties are analogous to the input parameters of AppleScript’s display dialog command, and its return value is identical to the display dialog command’s return value. One input record property, dialogText, is required and corresponds to the display dialog command’s unnamed direct parameter. The remaining input record properties are optional and correspond to the analogously named optional parameters of the display dialog command. Any optional property missing from the input record will be assigned a default value identical to the default value of the analogously named optional parameter of the display dialog command.

The input record properties are as follows:

Required property:
dialogText → corresponds to display dialog’s unnamed direct parameter

Optional properties:
defaultAnswer → corresponds to display dialog’s default answer parameter
hiddenAnswer → corresponds to display dialog’s hidden answer parameter
theButtons → corresponds to display dialog’s buttons parameter
defaultButton → corresponds to display dialog’s default button parameter
cancelButton → corresponds to display dialog’s cancel button parameter
withTitle → corresponds to display dialog’s with title parameter
withIcon → corresponds to display dialog’s with icon parameter
givingUpAfter → corresponds to display dialog’s giving up after parameter

Please refer to the StandardAdditions dictionary entry for the display dialog command for valid values for the input record properties.

Details of how the handler works are described in the handler’s comments. Some highlights are as follows:

  • The handler performs input argument validation.
  • The handler will catch and display any execution error in a dialog window.
  • The font used for the dialog text in display dialog’s window is obtained from NSFont’s systemFontOfSize method with its parameter set to zero (i.e., the default size.)
  • The font used in the dialog window’s default answer text is also NSFont’s system font, but its size varies with different window configurations in unpredictable ways. Since the size has never been observed to exceed that of the dialog text, the handler conservatively estimates its size to be that of the dialog text (i.e., system font of default size) so that the window will always be sufficiently widened to prevent the default answer text from wrapping. In practice, the window is almost always widened a little more than needed, except in the case where the hiddenAnswer input property is set to true, in which case the default answer consists of a string of black circle characters in the system font.
  • The handler properly handles the additional widening of the dialog window resulting from (1) the inclusion of an icon in the window, and (2) the use of a certain window button configuration, specifically 3 buttons in which button 2 is the cancel button and button 3 is the default button.
  • The window is never widened beyond the margins of the display screen (whose width is obtained from NSScreen’s mainScreen’s frame property), even if text lines end up being wrapped.
  • The button labels are widened with leading and trailing pads of hair space characters (character id 8202), chosen because they are the thinnest Unicode space characters available and thus allow the most precise label widening. A middle dot character (character id 183) is added to either end of the widened label. Without this latter addition, spurious off-center positioning of the widened label within the button frame may at unpredictable times be observed, most commonly when there is only one window button. The addition of a visible character at either end of the widened label prevents this problem. Any visible character may be used. The middle dot character was chosen because of its inconspicuousness.
  • Because there are far too many possible optional parameter configurations to allow the display dialog command to be hard-coded in the handler, instead a text representation of the command is generated and then executed with a run script command. The results of the run script command, which are identical to those of the display dialog command that it executes, are returned to the calling program, except that the clicked button’s original label, not the widened label, is returned.

One final point to mention is that this solution is based on my best attempt at “reverse engineering” AppleScript’s display dialog window. Obviously, not all possible combinations of dialog text, default answer text, and window buttons could be tested. Should any cases of handler failure be found, it would be helpful to learn of them so that the handler can be improved.

Examples of handler usage:


-- These "use" statements are required in the script containing the displayDialog handler
use framework "Foundation"
use scripting additions

-- The following example shows that the only required input record property is "dialogText"; default values will be supplied for all missing optional properties, just as is the case with the standard "display dialog" command

set myDialogText to "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do." & return & "Phasellus vestibulum lorem sed risus, integer eget aliquet eiusmod tempor praesent tristique." & return & "Ornare quam viverra orci sagittis, purus viverra accumsan in nisl nisi scelerisque eu, condimentum mattis pellentesque id nibh."

display dialog myDialogText
--> the dialog text's three sentences wrap onto seven lines in AppleScript's standard dialog window

displayDialog({dialogText:myDialogText})
--> the dialog text's three sentences don't wrap in the widened dialog window produced by the displayDialog handler

-- Just to have some fun and show that the handler can handle multibyte characters without problems

set myDialogText to "┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨°½②①éⵘ≅─⌘⌥┇‡⋘⋙▉⫶┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨°½②①éⵘ≅─⌘⌥┇‡⋘⋙▉⫶┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨"
set myButtons to {"Quit", "Proceed"}
set myDefaultButton to "Proceed"

display dialog myDialogText buttons myButtons default button myDefaultButton
--> the dialog text wraps onto four lines in AppleScript's standard dialog window

displayDialog({dialogText:myDialogText, theButtons:myButtons, defaultButton:myDefaultButton})
--> the dialog text doesn't wrap in the widened dialog window produced by the displayDialog handler

-- An example with a default answer, a window icon, and the special three-button configuration that produces increased spacing between the first and second window buttons

set myDialogText to "Make any desired changes:"
set myDefaultAnswer to "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud."
set myButtons to {"Maybe", "No", "Yes"}
set myDefaultButton to "Yes"
set myCancelButton to "No"
set myTitle to "Silly Example"
set myIcon to caution

display dialog myDialogText default answer myDefaultAnswer buttons myButtons default button myDefaultButton cancel button myCancelButton with title myTitle with icon myIcon
--> the default answer wraps onto four lines in AppleScript's standard dialog window

displayDialog({dialogText:myDialogText, defaultAnswer:myDefaultAnswer, theButtons:myButtons, defaultButton:myDefaultButton, cancelButton:myCancelButton, withTitle:myTitle, withIcon:myIcon})
--> the default answer doesn't wrap in the widened dialog window produced by the displayDialog handler

on displayDialog(inputRecord)
-- ...place the handler code described below here...
end displayDialog

Here is the displayDialog handler:


use framework "Foundation"
use scripting additions

on displayDialog(inputRecord)
	-- Replicates the behavior of AppleScript's "display dialog" command, except that the dialog window is widened as necessary to prevent any wrapping of dialog and/or default answer text lines
	script main
		-- "display dialog" window properties
		property dialogTextFontAttribute : missing value -- the font used for the window's dialog text
		property hiddenAnswerChar : missing value -- the character used to hide the default answer text
		property hiddenAnswerCharWidth : missing value -- the pixel width of the hidden answer character
		property nonwidenedDialogWindowWidth : missing value -- the pixel width of a standard, non-widened dialog window
		property buttonToWindowWidth : missing value -- the pixel width of the space between each outermost button and the adjacent window margin
		property buttonToButtonWidth : missing value -- the pixel width of the space between buttons
		property buttonToLabelWidth : missing value -- the pixel width of the space between either end of a button label and the adjacent button margin
		property iconAdjustmentWidth : missing value -- the pixel width of the extra space between the leftmost button and the adjacent window margin due to the presence of a window icon
		property specialThreeButtonAdjustmentWidth : missing value -- the pixel width of the extra space between the first and second buttons when all of the following conditions apply: (A) there are three buttons, (B) the rightmost button is the default button, and (C) the middle button is the cancel button
		-- Other properties
		property hairSpaceChar : missing value -- the hair space character (character id 8202)
		property hairSpaceCharWidth : missing value -- the pixel width of the hair space character
		property middleDotChar : missing value -- the middle dot character (character id 183)
		property middleDotCharWidth : missing value -- the pixel width of the middle dot character
		property screenWidth : missing value -- the pixel width of the display screen
		-- Utility handlers
		on getStringWidth(theString)
			-- Returns the pixel width of a string in the font used for the "display dialog" window's dialog text = NSFont's system font of default size
			return ((current application's NSString's stringWithString:theString)'s sizeWithAttributes:(my dialogTextFontAttribute))'s width as real
		end getStringWidth
		on replicateCharacter(theChar, nReps)
			-- Returns a string consisting of a character replicated a specified number of times
			return ((current application's NSString's stringWithString:"")'s stringByPaddingToLength:nReps withString:theChar startingAtIndex:0) as text
		end replicateCharacter
		on textRep(theValue)
			-- Returns the text representation of any AppleScript value
			try
				|| of {theValue}
			on error m
				try
					set {tid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "{"}
					set m to m's text items 2 thru -1 as text
					set AppleScript's text item delimiters to "}"
					set valueAsText to m's text items 1 thru -2 as text
					set AppleScript's text item delimiters to tid
				on error
					set AppleScript's text item delimiters to tid
					error "Problem with utility handler textRep:" & return & return & "Could not get the text representation of the input value."
				end try
			end try
			return valueAsText
		end textRep
		on widenButtonLabel(theLabel, labelWidth, targetWidth)
			-- Widens a button label to a target pixel width using left and right hair space character pads along with a middle dot character at either end of the widened label, the latter to prevent spurious off-center positioning of the label within the button
			-- Notes:
			--      The hair space character is used for padding because it is the narrowest Unicode character available and thus allows the most precise widening
			--      Any visible character may be used at the outer margins of the label; the middle dot character was chosen because of its inconspicuousness
			set nJustifyingHairSpaceChars to ((targetWidth - labelWidth - 2 * (my middleDotCharWidth)) / (my hairSpaceCharWidth)) as integer
			set justifiedString to theLabel
			if nJustifyingHairSpaceChars > 0 then
				set nLeftHairSpaceChars to nJustifyingHairSpaceChars div 2
				set nRightHairSpaceChars to nJustifyingHairSpaceChars - nLeftHairSpaceChars
				set justifiedString to (my middleDotChar) & (my replicateCharacter(my hairSpaceChar, nLeftHairSpaceChars)) & theLabel & (my replicateCharacter(my hairSpaceChar, nRightHairSpaceChars)) & (my middleDotChar)
			end if
			return justifiedString
		end widenButtonLabel
		on run
			-- Assign constant values to the "display dialog" window properties
			set my dialogTextFontAttribute to current application's NSDictionary's dictionaryWithObject:(current application's NSFont's systemFontOfSize:0) forKey:(current application's NSFontAttributeName)
			set my hiddenAnswerChar to character id 9679 -- 9679 = the black circle character used by the "display dialog" window to hide the default answer's text when that option is specified
			set my hiddenAnswerCharWidth to my getStringWidth(my hiddenAnswerChar)
			set my nonwidenedDialogWindowWidth to 420
			set my buttonToWindowWidth to 22
			set my buttonToButtonWidth to 12
			set my buttonToLabelWidth to 12
			set my iconAdjustmentWidth to 78
			set my specialThreeButtonAdjustmentWidth to 26
			-- Assign constant values to the other properties
			set my hairSpaceChar to character id 8202
			set my hairSpaceCharWidth to my getStringWidth(my hairSpaceChar)
			set my middleDotChar to character id 183
			set my middleDotCharWidth to my getStringWidth(my middleDotChar)
			set my screenWidth to current application's NSScreen's mainScreen()'s frame()'s second item's first item as real
			-- Validate and process the input record
			try
				tell (inputRecord & {defaultAnswer:missing value, hiddenAnswer:missing value, theButtons:missing value, defaultButton:missing value, cancelButton:missing value, withTitle:missing value, withIcon:missing value, givingUpAfter:missing value})
					if length ≠ 9 then error
					set {dialogText, defaultAnswer, hiddenAnswer, theButtons, defaultButton, cancelButton, withTitle, withIcon, givingUpAfter} to {its dialogText, its defaultAnswer, its hiddenAnswer, its theButtons, its defaultButton, its cancelButton, its withTitle, its withIcon, its givingUpAfter}
				end tell
			on error
				error "The handler input argument must be a record with the following properties:" & return & return & "Required property (equivalent to \"display dialog\"'s direct parameter):" & return & return & tab & "dialogText" & return & return & "Optional properties (equivalent to \"display dialog\"'s analogously named optional parameters):" & return & return & tab & "defaultAnswer" & return & tab & "hiddenAnswer" & return & tab & "theButtons" & return & tab & "defaultButton" & return & tab & "cancelButton" & return & tab & "withTitle" & return & tab & "withIcon" & return & tab & "givingUpAfter"
			end try
			-- Validate and process the input record properties
			if dialogText's class ≠ text then error "The input property dialogText must be a text string."
			tell defaultAnswer to if (it ≠ missing value) and (its class ≠ text) then error "The input property defaultAnswer must be the missing value or a text string."
			tell hiddenAnswer to if (it ≠ missing value) and (its class ≠ boolean) then error "The input property hiddenAnswer must be the missing value or a boolean true or false value."
			tell theButtons
				if it = missing value then
					set {theButtons, defaultButton, cancelButton} to {{"Cancel", "OK"}, 2, 1}
				else if its class ≠ list then
					set theButtons to {it}
				end if
			end tell
			tell theButtons
				try
					if (length < 1) or (length > 3) then error
					repeat with i from 1 to length
						set currButton to item i
						if currButton's class ≠ text then error
						if currButton = defaultButton then set defaultButton to i
						if currButton = cancelButton then set cancelButton to i
					end repeat
					set nButtons to length
				on error
					error "The input property theButtons must be the missing value, a text string (i.e., one button), or a list of one to three text strings."
				end try
			end tell
			try
				tell defaultButton to if not ((it = missing value) or ((its class = integer) and (it ≥ 1) and (it ≤ nButtons))) then error
			on error
				error "The button specified by the input property defaultButton does not exist."
			end try
			try
				tell cancelButton to if not ((it = missing value) or ((its class = integer) and (it ≥ 1) and (it ≤ nButtons))) then error
			on error
				error "The button specified by the input property cancelButton does not exist."
			end try
			tell withTitle to if (it ≠ missing value) and (its class ≠ text) then error "The input property withTitle must be the missing value or a text string."
			tell withIcon
				try
					if not (({it} is in {missing value, stop, note, caution, 0, 1, 2}) or ((its class = alias) and ((it as text) ends with ".icns"))) then error
				on error
					error "The input property withTitle must be one of the following values:" & return & tab & "missing value" & return & tab & "stop or 0" & return & tab & "note or 1" & return & tab & "caution or 2" & return & tab & "AppleScript alias to a \".icns\" file"
				end try
			end tell
			tell givingUpAfter
				try
					if (it ≠ missing value) and (its class ≠ integer) then set givingUpAfter to it as integer
				on error
					error "The input property givingUpAfter can't be transformed into an integer."
				end try
			end tell
			-- Get the maximum pixel width of the dialog and default answer text lines; in the case of a hidden default answer, this value consists of the pixel width of a string of black circle characters (character id 9679) replicated to the number of characters in the default answer's text
			set maxTextWidth to 0
			repeat with currLine in (get dialogText's paragraphs)
				tell my getStringWidth(currLine's contents) to if it > maxTextWidth then set maxTextWidth to it
			end repeat
			tell defaultAnswer
				if it ≠ missing value then
					if hiddenAnswer = true then
						tell length * (my hiddenAnswerCharWidth) to if it > maxTextWidth then set maxTextWidth to it
					else
						repeat with currLine in (get its paragraphs)
							tell my getStringWidth(currLine's contents) to if it > maxTextWidth then set maxTextWidth to it
						end repeat
					end if
				end if
			end tell
			-- If an icon is to be displayed in the "display dialog" window, account for the increased width between the leftmost button and the left window margin
			set buttonToWindowAdjustmentWidth to 0
			if withIcon ≠ missing value then set buttonToWindowAdjustmentWidth to my iconAdjustmentWidth
			-- Determine if the "display dialog" window needs to be widened to prevent wrapping of dialog and/or default answer text
			set windowNeedsWidening to maxTextWidth > ((my nonwidenedDialogWindowWidth) - 2 * (my buttonToWindowWidth) - buttonToWindowAdjustmentWidth)
			-- If the window needs to be widened, do so by widening the window's button labels
			set modifiedButtons to theButtons
			if windowNeedsWidening then
				-- Don't widen the "display dialog" window beyond the display screen's width; this is accomplished by setting an upper limit to the maximum text width value
				tell ((my screenWidth) - 2 * (my buttonToWindowWidth) - buttonToWindowAdjustmentWidth) to if maxTextWidth > it then set maxTextWidth to it
				-- Account for an increased width between the first and second buttons if all of the following conditions apply: (A) there are three buttons, (B) the rightmost button is the default button, and (C) the middle button is the cancel button
				set buttonToButtonAdjustmentWidth to 0
				if (defaultButton = 3) and (cancelButton = 2) then set buttonToButtonAdjustmentWidth to my specialThreeButtonAdjustmentWidth
				-- Calculate the target width for button labels that will widen the "display dialog" window just enough to prevent text wrapping
				set targetWidth to (maxTextWidth + (my buttonToButtonWidth) - buttonToButtonAdjustmentWidth) / nButtons - (my buttonToButtonWidth) - 2 * (my buttonToLabelWidth)
				-- Widen each button label to the target width
				set modifiedButtons to {}
				repeat with currButton in theButtons
					set end of modifiedButtons to my widenButtonLabel(currButton's contents, my getStringWidth(currButton's contents), targetWidth)
				end repeat
			end if
			-- Create a text representation of the "display dialog" command, incorporating any optional parameters specified in the input record
			set theCommand to "activate" & return & "display dialog " & my textRep(dialogText) & " buttons " & my textRep(modifiedButtons)
			if defaultButton ≠ missing value then set theCommand to theCommand & " default button " & my textRep(defaultButton)
			if cancelButton ≠ missing value then set theCommand to theCommand & " cancel button " & my textRep(cancelButton)
			if defaultAnswer ≠ missing value then set theCommand to theCommand & " default answer " & my textRep(defaultAnswer)
			if hiddenAnswer ≠ missing value then set theCommand to theCommand & " hidden answer " & my textRep(hiddenAnswer)
			if withTitle ≠ missing value then set theCommand to theCommand & " with title " & my textRep(withTitle)
			if withIcon ≠ missing value then set theCommand to theCommand & " with icon " & my textRep(withIcon)
			if givingUpAfter ≠ missing value then set theCommand to theCommand & " giving up after " & my textRep(givingUpAfter)
			-- Execute the text representation of the "display dialog" command with a "run script" command, and capture the returned values
			set returnedValues to (run script theCommand)
			-- Set the "button returned" return value to the original button name, not the widened button name
			if windowNeedsWidening then
				set buttonReturned to returnedValues's button returned
				repeat with i from 1 to modifiedButtons's length
					if modifiedButtons's item i = buttonReturned then
						set returnedValues's button returned to theButtons's item i
						exit repeat
					end if
				end repeat
			end if
			-- Return the "display dialog" command's returned values to the calling program
			return returnedValues
		end run
	end script
	-- Wrap the entire executed handler code in a try block so that any execution error may be captured and displayed
	try
		run main
	on error m number n
		if n = -128 then error number -128
		if n ≠ -2700 then set m to "(" & n & ") " & m
		tell application "System Events"
			activate
			display dialog ("Problem with handler displayDialog:" & return & return & m) buttons "OK" default button "OK" cancel button "OK"
		end tell
	end try
end displayDialog

Edit note: Two changes were made to the “Examples of handler usage” section from the original post: (1) the “Lorem ipsum” example was lengthened to three sentences to demonstrate the helpfulness of opening the dialog window without text wrapping, and (2) the multibyte character string example was modified by removing certain multibyte characters that were not being displayed properly by the MacScripter webpage generator.

The following is a mild improvement over the originally submitted version of the displayDialog handler above. It incorporates the only non-displaying character I could find in the entire array of Unicode characters, namely the obscure TAG LATIN SMALL LETTER A character = character id 917601, that prevents off-center positioning of button labels widened with hair space character pads when added to the beginning and end of widened button labels. All other non-displaying characters I tried failed in this regard. This modification removes the mildly annoying middle dot characters that were placed at the beginning and end of widened button labels in the original version of the handler. I also changed the names of certain variables used in the creation of widened button labels to more generic names. This is the recommended version of the handler to use:


use framework "Foundation"
use scripting additions

on displayDialog(inputRecord)
	-- Replicates the behavior of AppleScript's "display dialog" command, except that the dialog window is widened as necessary to prevent any wrapping of dialog and/or default answer text lines
	script main
		-- "display dialog" window properties
		property dialogTextFontAttribute : missing value -- the font used for the window's dialog text
		property hiddenAnswerChar : missing value -- the character used to hide the default answer text
		property hiddenAnswerCharWidth : missing value -- the pixel width of the hidden answer character
		property nonwidenedDialogWindowWidth : missing value -- the pixel width of a standard, non-widened dialog window
		property buttonToWindowWidth : missing value -- the pixel width of the space between each outermost button and the adjacent window margin
		property buttonToButtonWidth : missing value -- the pixel width of the space between buttons
		property buttonToLabelWidth : missing value -- the pixel width of the space between either end of a button label and the adjacent button margin
		property iconAdjustmentWidth : missing value -- the pixel width of the extra space between the leftmost button and the adjacent window margin due to the presence of a window icon
		property specialThreeButtonAdjustmentWidth : missing value -- the pixel width of the extra space between the first and second buttons when all of the following conditions apply: (A) there are three buttons, (B) the rightmost button is the default button, and (C) the middle button is the cancel button
		-- Other properties
		property labelWideningChar : missing value -- the space character that will be replicated to form the left and right pads used to widen button labels (will be assigned the HAIR SPACE character = character id 8202)
		property labelWideningCharWidth : missing value -- the pixel width of the button label-widening character
		property labelTerminatingChar : missing value -- the character that will added to the beginning and end of widened button labels to prevent off-center positioning of labels in the button frame (will be assigned the TAG LATIN SMALL LETTER A character = character id 917601, the only [?] non-displaying Unicode character that prevents off-center positioning)
		property labelTerminatingCharWidth : missing value -- the pixel width of the button label-terminating character
		property screenWidth : missing value -- the pixel width of the display screen
		-- Utility handlers
		on getStringWidth(theString)
			-- Returns the pixel width of a string in the font used for the "display dialog" window's dialog text = NSFont's system font of default size
			return ((current application's NSString's stringWithString:theString)'s sizeWithAttributes:(my dialogTextFontAttribute))'s width as real
		end getStringWidth
		on replicateCharacter(theChar, nReps)
			-- Returns a string consisting of a character replicated a specified number of times
			return ((current application's NSString's stringWithString:"")'s stringByPaddingToLength:nReps withString:theChar startingAtIndex:0) as text
		end replicateCharacter
		on textRep(theValue)
			-- Returns the text representation of any AppleScript value
			try
				|| of {theValue}
			on error m
				try
					set {tid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "{"}
					set m to m's text items 2 thru -1 as text
					set AppleScript's text item delimiters to "}"
					set valueAsText to m's text items 1 thru -2 as text
					set AppleScript's text item delimiters to tid
				on error
					set AppleScript's text item delimiters to tid
					error "Problem with utility handler textRep:" & return & return & "Could not get the text representation of the input value."
				end try
			end try
			return valueAsText
		end textRep
		on widenButtonLabel(theLabel, labelWidth, targetWidth)
			-- Widens a button label to a target pixel width using left and right space character pads along with a special character at either end of the widened label, the latter to prevent spurious off-center positioning of the label within the button frame
			-- Notes:
			-- The HAIR SPACE character is the narrowest Unicode space character available for the creation of the space pads and thus is used to achieve the most precise widening possible
			-- The special character TAG LATIN SMALL LETTER A is added to the beginning and end of the widened label to prevent spurious off-center positioning of the label within the button frame; it is the only non-displaying Unicode character discovered to date that fulfills this function; alternatively, any visible character may be used
			set nLabelWideningSpaceChars to ((targetWidth - labelWidth - 2 * (my labelTerminatingCharWidth)) / (my labelWideningCharWidth)) as integer
			set modifiedLabel to theLabel
			if nLabelWideningSpaceChars > 0 then
				set nLeftSpaceChars to nLabelWideningSpaceChars div 2
				set nRightSpaceChars to nLabelWideningSpaceChars - nLeftSpaceChars
				set modifiedLabel to (my labelTerminatingChar) & (my replicateCharacter(my labelWideningChar, nLeftSpaceChars)) & theLabel & (my replicateCharacter(my labelWideningChar, nRightSpaceChars)) & (my labelTerminatingChar)
			end if
			return modifiedLabel
		end widenButtonLabel
		on run
			-- Assign constant values to the "display dialog" window properties
			set my dialogTextFontAttribute to current application's NSDictionary's dictionaryWithObject:(current application's NSFont's systemFontOfSize:0) forKey:(current application's NSFontAttributeName)
			set my hiddenAnswerChar to character id 9679 -- 9679 = the black circle character used by the "display dialog" window to hide the default answer's text when that option is specified
			set my hiddenAnswerCharWidth to my getStringWidth(my hiddenAnswerChar)
			set my nonwidenedDialogWindowWidth to 420
			set my buttonToWindowWidth to 22
			set my buttonToButtonWidth to 12
			set my buttonToLabelWidth to 12
			set my iconAdjustmentWidth to 78
			set my specialThreeButtonAdjustmentWidth to 26
			-- Assign constant values to the other properties
			set my labelWideningChar to character id 8202
			set my labelWideningCharWidth to my getStringWidth(my labelWideningChar)
			set my labelTerminatingChar to character id 917601
			set my labelTerminatingCharWidth to my getStringWidth(my labelTerminatingChar)
			set my screenWidth to current application's NSScreen's mainScreen()'s frame()'s second item's first item as real
			-- Validate and process the input record
			try
				tell (inputRecord & {defaultAnswer:missing value, hiddenAnswer:missing value, theButtons:missing value, defaultButton:missing value, cancelButton:missing value, withTitle:missing value, withIcon:missing value, givingUpAfter:missing value})
					if length ≠ 9 then error
					set {dialogText, defaultAnswer, hiddenAnswer, theButtons, defaultButton, cancelButton, withTitle, withIcon, givingUpAfter} to {its dialogText, its defaultAnswer, its hiddenAnswer, its theButtons, its defaultButton, its cancelButton, its withTitle, its withIcon, its givingUpAfter}
				end tell
			on error
				error "The handler input argument must be a record with the following properties:" & return & return & "Required property (equivalent to \"display dialog\"'s direct parameter):" & return & return & tab & "dialogText" & return & return & "Optional properties (equivalent to \"display dialog\"'s analogously named optional parameters):" & return & return & tab & "defaultAnswer" & return & tab & "hiddenAnswer" & return & tab & "theButtons" & return & tab & "defaultButton" & return & tab & "cancelButton" & return & tab & "withTitle" & return & tab & "withIcon" & return & tab & "givingUpAfter"
			end try
			-- Validate and process the input record properties
			if dialogText's class ≠ text then error "The input property dialogText must be a text string."
			tell defaultAnswer to if (it ≠ missing value) and (its class ≠ text) then error "The input property defaultAnswer must be the missing value or a text string."
			tell hiddenAnswer to if (it ≠ missing value) and (its class ≠ boolean) then error "The input property hiddenAnswer must be the missing value or a boolean true or false value."
			tell theButtons
				if it = missing value then
					set {theButtons, defaultButton, cancelButton} to {{"Cancel", "OK"}, 2, 1}
				else if its class ≠ list then
					set theButtons to {it}
				end if
			end tell
			tell theButtons
				try
					if (length < 1) or (length > 3) then error
					repeat with i from 1 to length
						set currButton to item i
						if currButton's class ≠ text then error
						if currButton = defaultButton then set defaultButton to i
						if currButton = cancelButton then set cancelButton to i
					end repeat
					set nButtons to length
				on error
					error "The input property theButtons must be the missing value, a text string (i.e., one button), or a list of one to three text strings."
				end try
			end tell
			try
				tell defaultButton to if not ((it = missing value) or ((its class = integer) and (it ≥ 1) and (it ≤ nButtons))) then error
			on error
				error "The button specified by the input property defaultButton does not exist."
			end try
			try
				tell cancelButton to if not ((it = missing value) or ((its class = integer) and (it ≥ 1) and (it ≤ nButtons))) then error
			on error
				error "The button specified by the input property cancelButton does not exist."
			end try
			tell withTitle to if (it ≠ missing value) and (its class ≠ text) then error "The input property withTitle must be the missing value or a text string."
			tell withIcon
				try
					if not (({it} is in {missing value, stop, note, caution, 0, 1, 2}) or ((its class = alias) and ((it as text) ends with ".icns"))) then error
				on error
					error "The input property withTitle must be one of the following values:" & return & tab & "missing value" & return & tab & "stop or 0" & return & tab & "note or 1" & return & tab & "caution or 2" & return & tab & "AppleScript alias to a \".icns\" file"
				end try
			end tell
			tell givingUpAfter
				try
					if (it ≠ missing value) and (its class ≠ integer) then set givingUpAfter to it as integer
				on error
					error "The input property givingUpAfter can't be transformed into an integer."
				end try
			end tell
			-- Get the maximum pixel width of the dialog and default answer text lines; in the case of a hidden default answer, this value consists of the pixel width of a string of black circle characters (character id 9679) replicated to the number of characters in the default answer's text
			set maxTextWidth to 0
			repeat with currLine in (get dialogText's paragraphs)
				tell my getStringWidth(currLine's contents) to if it > maxTextWidth then set maxTextWidth to it
			end repeat
			tell defaultAnswer
				if it ≠ missing value then
					if hiddenAnswer = true then
						tell length * (my hiddenAnswerCharWidth) to if it > maxTextWidth then set maxTextWidth to it
					else
						repeat with currLine in (get its paragraphs)
							tell my getStringWidth(currLine's contents) to if it > maxTextWidth then set maxTextWidth to it
						end repeat
					end if
				end if
			end tell
			-- If an icon is to be displayed in the "display dialog" window, account for the increased width between the leftmost button and the left window margin
			set buttonToWindowAdjustmentWidth to 0
			if withIcon ≠ missing value then set buttonToWindowAdjustmentWidth to my iconAdjustmentWidth
			-- Determine if the "display dialog" window needs to be widened to prevent wrapping of dialog and/or default answer text
			set windowNeedsWidening to maxTextWidth > ((my nonwidenedDialogWindowWidth) - 2 * (my buttonToWindowWidth) - buttonToWindowAdjustmentWidth)
			-- If the window needs to be widened, do so by widening the window's button labels
			set modifiedButtons to theButtons
			if windowNeedsWidening then
				-- Don't widen the "display dialog" window beyond the display screen's width; this is accomplished by setting an upper limit to the maximum text width value
				tell ((my screenWidth) - 2 * (my buttonToWindowWidth) - buttonToWindowAdjustmentWidth) to if maxTextWidth > it then set maxTextWidth to it
				-- Account for an increased width between the first and second buttons if all of the following conditions apply: (A) there are three buttons, (B) the rightmost button is the default button, and (C) the middle button is the cancel button
				set buttonToButtonAdjustmentWidth to 0
				if (defaultButton = 3) and (cancelButton = 2) then set buttonToButtonAdjustmentWidth to my specialThreeButtonAdjustmentWidth
				-- Calculate the target width for button labels that will widen the "display dialog" window just enough to prevent text wrapping
				set targetWidth to (maxTextWidth + (my buttonToButtonWidth) - buttonToButtonAdjustmentWidth) / nButtons - (my buttonToButtonWidth) - 2 * (my buttonToLabelWidth)
				-- Widen each button label to the target width
				set modifiedButtons to {}
				repeat with currButton in theButtons
					set end of modifiedButtons to my widenButtonLabel(currButton's contents, my getStringWidth(currButton's contents), targetWidth)
				end repeat
			end if
			-- Create a text representation of the "display dialog" command, incorporating any optional parameters specified in the input record
			set theCommand to "activate" & return & "display dialog " & my textRep(dialogText) & " buttons " & my textRep(modifiedButtons)
			if defaultButton ≠ missing value then set theCommand to theCommand & " default button " & my textRep(defaultButton)
			if cancelButton ≠ missing value then set theCommand to theCommand & " cancel button " & my textRep(cancelButton)
			if defaultAnswer ≠ missing value then set theCommand to theCommand & " default answer " & my textRep(defaultAnswer)
			if hiddenAnswer ≠ missing value then set theCommand to theCommand & " hidden answer " & my textRep(hiddenAnswer)
			if withTitle ≠ missing value then set theCommand to theCommand & " with title " & my textRep(withTitle)
			if withIcon ≠ missing value then set theCommand to theCommand & " with icon " & my textRep(withIcon)
			if givingUpAfter ≠ missing value then set theCommand to theCommand & " giving up after " & my textRep(givingUpAfter)
			-- Execute the text representation of the "display dialog" command with a "run script" command, and capture the returned values
			set returnedValues to (run script theCommand)
			-- Set the "button returned" return value to the original button name, not the widened button name
			if windowNeedsWidening then
				set buttonReturned to returnedValues's button returned
				repeat with i from 1 to modifiedButtons's length
					if modifiedButtons's item i = buttonReturned then
						set returnedValues's button returned to theButtons's item i
						exit repeat
					end if
				end repeat
			end if
			-- Return the "display dialog" command's returned values to the calling program
			return returnedValues
		end run
	end script
	-- Wrap the entire executed handler code in a try block so that any execution error may be captured and displayed
	try
		run main
	on error m number n
		if n = -128 then error number -128
		if n ≠ -2700 then set m to "(" & n & ") " & m
		tell application "System Events"
			activate
			display dialog ("Problem with handler displayDialog:" & return & return & m) buttons "OK" default button "OK" cancel button "OK"
		end tell
	end try
end displayDialog

And here are the same examples as above, but now without the visible characters at the beginning and end of the widened button labels:


-- These "use" statements are required in the script containing the displayDialog handler
use framework "Foundation"
use scripting additions

-- The following example shows that the only required input record property is "dialogText"; default values will be supplied for all missing optional properties, just as is the case with the standard "display dialog" command

set myDialogText to "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do." & return & "Phasellus vestibulum lorem sed risus, integer eget aliquet eiusmod tempor praesent tristique." & return & "Ornare quam viverra orci sagittis, purus viverra accumsan in nisl nisi scelerisque eu, condimentum mattis pellentesque id nibh."

display dialog myDialogText
--> the dialog text's three sentences wrap onto seven lines in AppleScript's standard dialog window

displayDialog({dialogText:myDialogText})
--> the dialog text's three sentences don't wrap in the widened dialog window produced by the displayDialog handler

-- Just to have some fun and show that the handler can handle multibyte characters without problems

set myDialogText to "┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨°½②①éⵘ≅─⌘⌥┇‡⋘⋙▉⫶┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨°½②①éⵘ≅─⌘⌥┇‡⋘⋙▉⫶┇‡⋘⋙▉⫶·±⊕⊞§÷צôt↓➨"
set myButtons to {"Quit", "Proceed"}
set myDefaultButton to "Proceed"

display dialog myDialogText buttons myButtons default button myDefaultButton
--> the dialog text wraps onto four lines in AppleScript's standard dialog window

displayDialog({dialogText:myDialogText, theButtons:myButtons, defaultButton:myDefaultButton})
--> the dialog text doesn't wrap in the widened dialog window produced by the displayDialog handler

-- An example with a default answer, a window icon, and the special three-button configuration that produces increased spacing between the first and second window buttons

set myDialogText to "Make any desired changes:"
set myDefaultAnswer to "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud."
set myButtons to {"Maybe", "No", "Yes"}
set myDefaultButton to "Yes"
set myCancelButton to "No"
set myTitle to "Silly Example"
set myIcon to caution

display dialog myDialogText default answer myDefaultAnswer buttons myButtons default button myDefaultButton cancel button myCancelButton with title myTitle with icon myIcon
--> the default answer wraps onto four lines in AppleScript's standard dialog window

displayDialog({dialogText:myDialogText, defaultAnswer:myDefaultAnswer, theButtons:myButtons, defaultButton:myDefaultButton, cancelButton:myCancelButton, withTitle:myTitle, withIcon:myIcon})
--> the default answer doesn't wrap in the widened dialog window produced by the displayDialog handler

on displayDialog(inputRecord)
-- ...place the handler code here...
end displayDialog

Edit note: Not surprisingly, other non-displaying Unicode characters in the TAG block perform the function of centering the button label in the button frame as described for the TAG LATIN SMALL LETTER A character above. They are the TAG LATIN SMALL LETTER A through TAG LATIN SMALL LETTER Z characters, corresponding to character id 917601 through 917626, and the CANCEL TAG character, character id 917631. But in keeping with so many other non-displaying Unicode characters, the LANGUAGE TAG and TAG SPACE characters, character id 917505 and 917536, don’t work.