Can different fields show/disappear depending on popup/checkbox choice in Dialog Toolkit?

There are a couple of issues. The first is that in the doStuff handler, the if statement is in a tell statement, so the call to the convertNumberToCurrencyString handler needs to be be used with my or of me, and you are using the localizedStringFromNumber method, which wants a number, so the textField will need to be coerced to a number. Maybe you were also going to use numberFromString?

The second step is a bit more problematic, in that by the time the doStuff handler is called, the modal dialog is completed, so there is no going back.

A controlTextDidChange delegate method could be used with the textField to only accept numeric (or whatever) characters in the first place, and a controlTextDidEndEditing delegate method could be used when the editing is complete so that you can check to see if the stringValue formats correctly, clearing the textField and putting up an alert if it doesn’t. Both would be used while the dialog is still running.

Thank you, this worked perfectly (yes, I already had the conversion to number starting from a string)

Is this hard to implement? Would this option maintain all other entered fields so I can only edit the mistyped fields? Otherwise if I would have to enter again all fields it’s not worth the effort, and an error dialog explaining what happened is enough. This is what I came up with in the sample script, with a dialog showing the error if the field is not a number, did I write it correctly? It works, so it should be not so shabby :slight_smile:

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use script "Dialog Toolkit Plus" version "1.1"
use script "Dialog Toolkit Plus Extended" version "1.1" -- datePicker


property popupItems : {"This Thing", "That Thing", "The Other Thing"} -- the operations to perform
global controlGroups -- this will be a dictionary of the alternate groups and their controls

on number_to_string(this_number)
	set this_number to this_number as string
	if this_number contains "E+" then
		set x to the offset of "," in this_number
		set y to the offset of "+" in this_number
		set z to the offset of "E" in this_number
		set the decimal_adjust to characters (y - (length of this_number)) thru -1 of this_number as string as number
		if x is not 0 then
			set the first_part to characters 1 thru (x - 1) of this_number as string
		else
			set the first_part to ""
		end if
		set the second_part to characters (x + 1) thru (z - 1) of this_number as string
		set the converted_number to the first_part
		repeat with i from 1 to the decimal_adjust
			try
				set the converted_number to the converted_number & character i of the second_part
			on error
				set the converted_number to the converted_number & "0"
			end try
		end repeat
		return the converted_number
	else if this_number contains "." then
		set AppleScript's text item delimiters to "."
		set arrayObjects to text items of this_number
		set AppleScript's text item delimiters to ","
		set this_number to arrayObjects as string
		set AppleScript's text item delimiters to ""
		return this_number
	else
		return this_number
	end if
end number_to_string

on convertNumberToCurrencyString(theNumber)
	set theStyle to NSNumberFormatterCurrencyStyle of current application
	set theFormattedNumber to localizedStringFromNumber_numberStyle_(theNumber, theStyle) of NSNumberFormatter of current application
	return (theFormattedNumber as string)
end convertNumberToCurrencyString

on run -- example
	set controlGroups to current application's NSMutableDictionary's dictionary -- for using a keyPath
	set {viewWidth, theTop} to {400, 28}
	
	# controls are stacked from the bottom up for easier vertical positioning
	set {theButtons, minWidth} to create buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel" with equal widths
	if minWidth > viewWidth then set viewWidth to minWidth
	set {maxLabelWidth, controlLeft} to {100, 110}
	
	########## begin control group #1 - these are layered in the same space, with only the active alternate shown
	set newTop to theTop -- alternate A (This Thing)
	set {A2_textField, A2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop) total width viewWidth label text "This #2:" field left controlLeft
	set {A1_textField, A1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "This #1:" field left controlLeft
	repeat with anItem in (addAlternate(1, first item of popupItems, {A1_textField, A1_labelField, A2_textField, A2_labelField}))
		(anItem's setHidden:true)
	end repeat
	----
	set newTop to theTop -- alternate B (That Thing)
	set {B3_checkbox, newTop, newWidth} to create checkbox "That Checkbox" bottom (newTop - 24) max width (viewWidth / 2) left inset controlLeft without initial state
	set {B2_textField, B2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #2:" field left controlLeft
	set {B1_textField, B1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #1:" field left controlLeft
	repeat with anItem in (addAlternate(1, second item of popupItems, {B1_textField, B1_labelField, B2_textField, B2_labelField, B3_checkbox}))
		(anItem's setHidden:true)
	end repeat
	########## end control group #1
	
	set theTop to newTop -- continue from previous top
	set {divider, theTop} to create rule (theTop + 12) rule width viewWidth -- NSBox
	set {invoiceTextField, invoiceLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width (viewWidth / 2) label text "Invoice number:" field left controlLeft
	set {contractorTextField, contractorLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Contractor:" field left controlLeft
	set {clientTextField, clientLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Client:" field left controlLeft
	set {dateLabelField, theTop} to create label " Date:" left inset 60 bottom (theTop + 12) max width maxLabelWidth control size regular size
	set {datePicker, theTop} to create textual datepicker textual stepper picker elements YearMonthDay initial date (current date) min date seconds 0 max date seconds 0 left inset controlLeft bottom (theTop - 20) extra width 0 extra height 0
	
	set {operationPopup, theTop} to create popup popupItems left inset controlLeft bottom (theTop + 8) popup width 180 initial choice (last item of popupItems)
	operationPopup's setToolTip:"the operation to perform"
	operationPopup's setTarget:me
	operationPopup's setAction:"popupAction:" -- choose from group #1
	
	
	set allControls to {operationPopup, dateLabelField, datePicker, clientLabelField, clientTextField, contractorLabelField, contractorTextField, invoiceLabelField, invoiceTextField, divider, A1_labelField, A1_textField, A2_labelField, A2_textField, B1_labelField, B1_textField, B2_labelField, B2_textField, B3_checkbox} -- control references are listed from top to bottom for easier indexing
	set {buttonName, controlValues} to display extended window "Descriptive Title" acc view width viewWidth acc view height theTop acc view controls allControls buttons theButtons without align cancel button
	doStuff for (formatResult from controlValues)
end run

# Extract the desired controls from the returned dialog values and group into a record.
to formatResult from dialogResult
	tell (date (item 3 of dialogResult) as «class isot» as string) to set ISODate to text 9 thru 10 & "/" & text 6 thru 7 & "/" & text 1 thru 4 -- format date using the built-in ISO date class (no time zone info)
	tell dialogResult -- use record for consistent key:value pairs
		set common to {operation:item 1, theDate:ISODate, client:item 5, contractor:item 7, invoice:item 9} -- common items
		if first item is first item of popupItems then -- common + alternate A
			set results to common & {A1_textField:item 12, A2_textField:item 14}
		else if first item is second item of popupItems then -- common + alternate B
			set results to common & {B1_textField:item 16, B2_textField:item 18, B3_checkbox:item 19}
		else if first item is third item of popupItems then
			set results to common
		else -- oops
			set results to missing value
		end if
	end tell
	return results
end formatResult

# Add a set of alternate controls to the controlGroups dictionary - controls are returned for any customization.
to addAlternate((group as text), (keyName as text), (controls as list))
	if (controlGroups's valueForKey:group) is missing value then (controlGroups's setValue:(current application's NSMutableDictionary's dictionary()) forKey:group) -- add new group as needed
	controlGroups's setValue:(current application's NSDictionary's dictionaryWithDictionary:{controls:controls}) forKeyPath:(group & "." & keyName) -- add keyName alternate controls to group
	return controls
end addAlternate

# Action to change the visibility of group #1 controls based on the popup value.
on popupAction:sender
	repeat with aKey in popupItems -- hide everything
		set controlList to (controlGroups's valueForKeyPath:("1." & aKey & ".controls"))
		if controlList is not missing value then repeat with aControl in controlList
			((contents of aControl)'s setHidden:true)
		end repeat
	end repeat
	set controlList to (controlGroups's valueForKeyPath:("1." & ((sender's title) as text) & ".controls"))
	if controlList is not missing value then repeat with aControl in controlList -- show the selected control group
		((contents of aControl)'s setHidden:false)
	end repeat
end popupAction:

# Do stuff for the dialog result values.
to doStuff for dialogValues
	tell (operation of dialogValues) to if it is first item of popupItems then
		try
			return my convertNumberToCurrencyString(my number_to_string(A2_textField of dialogValues) as number)
		on error
			tell application "System Events"
				display dialog "Not a number!" with icon 0 buttons {"Cancel"} default button "Cancel"
			end tell
			error number -128
		end try
	else if it is second item of popupItems then
		-- perform `That` with dialogValues for that operation
	else if it is third item of popupItems then
		-- perform `The Other` with dialogValues for the other operation
	end if
	return dialogValues
end doStuff

Delegate handlers can be implemented by setting the delegate for the particular textField and including the handler - other textFields will not be affected. The object sending the notification is also passed to the delegate handler for comparison, in the event you are doing different things for different objects. These handlers are called when the textField is changed and/or the editing is completed, so you can perform normal editing while the dialog is running. When the dialog exits, the item values via the doStuff handler can be used normally, as they would already be processed.

To enable the delegate handler(s), the delegate for the textField can be set when it is created, for example:

A2_textField's setDelegate:me

Any delegate method(s) are automatically called by adding them to the script. For the controlTextDidChange handler, it can be something like the following (set acceptable characters as desired):

# Check out additions to the number formatted text field.
on controlTextDidChange:notificationObject
    set acceptable to characters of "1234567890E,+-" -- or whatever
    set object to notificationObject's object -- the textField that posted the notification
    set theText to object's stringValue as text
    repeat with aCharacter in (characters of theText) -- just check everything in case something was pasted
        considering case -- for the exponent indicator `E`
            if aCharacter is not in acceptable then -- indicate and remove
                current application's NSBeep()
                set {prevTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, aCharacter}
                set {textItems, AppleScript's text item delimiters} to {text items of theText, ""}
                set {theText, AppleScript's text item delimiters} to {textItems as text, prevTID}
                (object's setStringValue:theText)
            end if
        end considering
    end repeat
end controlTextDidChange:

For the controlTextDidEndEditing handler, something like the following can be used. Note that the completion of any editing needs to be indicated by exiting the textField, for example tabbing to move to another control - the notification will not be sent if you were to immediately click on the dialog completion button while still in the textField.

# Verify the number in the formatted text field after editing is complete.
on controlTextDidEndEditing:notificationObject
    set object to notificationObject's object -- the textField that posted the notification
    set theText to object's stringValue as text
    -- check formatting of theText as desired, if failure then clear the textField and show an error alert
end controlTextDidEndEditing:

Thank you very much red_menace.

I tried both delegate methods and, If I understood well, in this particular case I have to choose between the two, since having both doesn’t make sense: if I’m prevented to add non acceptable characters from the beginning it doesn’t make sense to me to check the textfield after the editing, right?

I added the first one (controltextdidchange) and it works nice, but I don’t understand how to get an error notification (I can hear NSBeep ()); I tried to insert a display dialog but it feels kinda laggy after first error, and I have to exit and enter again the textfield to write again any character. This is how I updated the test script:

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use script "Dialog Toolkit Plus" version "1.1"
use script "Dialog Toolkit Plus Extended" version "1.1" -- datePicker


property popupItems : {"This Thing", "That Thing", "The Other Thing"} -- the operations to perform
global controlGroups -- this will be a dictionary of the alternate groups and their controls

# Check out additions to the number formatted text field.
on controlTextDidChange:notificationObject
	set acceptable to characters of "1234567890E,+- " -- or whatever
	set object to notificationObject's object -- the textField that posted the notification
	set theText to object's stringValue as text
	repeat with aCharacter in (characters of theText) -- just check everything in case something was pasted
		considering case -- for the exponent indicator `E`
			if aCharacter is not in acceptable then -- indicate and remove
				current application's NSBeep()
				set {prevTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, aCharacter}
				set {textItems, AppleScript's text item delimiters} to {text items of theText, ""}
				set {theText, AppleScript's text item delimiters} to {textItems as text, prevTID}
				(object's setStringValue:theText)
				tell application "System Events"
					display dialog "Character not acceptable" with icon caution
				end tell
			end if
		end considering
	end repeat
end controlTextDidChange:

on run -- example
	set controlGroups to current application's NSMutableDictionary's dictionary -- for using a keyPath
	set {viewWidth, theTop} to {400, 28}
	
	# controls are stacked from the bottom up for easier vertical positioning
	set {theButtons, minWidth} to create buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel" with equal widths
	if minWidth > viewWidth then set viewWidth to minWidth
	set {maxLabelWidth, controlLeft} to {100, 110}
	
	########## begin control group #1 - these are layered in the same space, with only the active alternate shown
	set newTop to theTop -- alternate A (This Thing)
	set {A2_textField, A2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop) total width viewWidth label text "This #2:" field left controlLeft
	set {A1_textField, A1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "This #1:" field left controlLeft
	repeat with anItem in (addAlternate(1, first item of popupItems, {A1_textField, A1_labelField, A2_textField, A2_labelField}))
		(anItem's setHidden:true)
	end repeat
	----
	set newTop to theTop -- alternate B (That Thing)
	set {B3_checkbox, newTop, newWidth} to create checkbox "That Checkbox" bottom (newTop - 24) max width (viewWidth / 2) left inset controlLeft without initial state
	set {B2_textField, B2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #2:" field left controlLeft
	set {B1_textField, B1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #1:" field left controlLeft
	repeat with anItem in (addAlternate(1, second item of popupItems, {B1_textField, B1_labelField, B2_textField, B2_labelField, B3_checkbox}))
		(anItem's setHidden:true)
	end repeat
	########## end control group #1
	
	set theTop to newTop -- continue from previous top
	set {divider, theTop} to create rule (theTop + 12) rule width viewWidth -- NSBox
	set {invoiceTextField, invoiceLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width (viewWidth / 2) label text "Invoice number:" field left controlLeft
	set {contractorTextField, contractorLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Contractor:" field left controlLeft
	set {clientTextField, clientLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Client:" field left controlLeft
	set {dateLabelField, theTop} to create label " Date:" left inset 60 bottom (theTop + 12) max width maxLabelWidth control size regular size
	set {datePicker, theTop} to create textual datepicker textual stepper picker elements YearMonthDay initial date (current date) min date seconds 0 max date seconds 0 left inset controlLeft bottom (theTop - 20) extra width 0 extra height 0
	
	set {operationPopup, theTop} to create popup popupItems left inset controlLeft bottom (theTop + 8) popup width 180 initial choice (last item of popupItems)
	operationPopup's setToolTip:"the operation to perform"
	operationPopup's setTarget:me
	operationPopup's setAction:"popupAction:" -- choose from group #1
	A2_textField's setDelegate:me
	
	set allControls to {operationPopup, dateLabelField, datePicker, clientLabelField, clientTextField, contractorLabelField, contractorTextField, invoiceLabelField, invoiceTextField, divider, A1_labelField, A1_textField, A2_labelField, A2_textField, B1_labelField, B1_textField, B2_labelField, B2_textField, B3_checkbox} -- control references are listed from top to bottom for easier indexing
	set {buttonName, controlValues} to display extended window "Descriptive Title" acc view width viewWidth acc view height theTop acc view controls allControls buttons theButtons without align cancel button
	doStuff for (formatResult from controlValues)
end run

# Extract the desired controls from the returned dialog values and group into a record.
to formatResult from dialogResult
	tell (date (item 3 of dialogResult) as «class isot» as string) to set ISODate to text 9 thru 10 & "/" & text 6 thru 7 & "/" & text 1 thru 4 -- format date using the built-in ISO date class (no time zone info)
	tell dialogResult -- use record for consistent key:value pairs
		set common to {operation:item 1, theDate:ISODate, client:item 5, contractor:item 7, invoice:item 9}
		if first item is first item of popupItems then -- common + alternate A
			set results to common & {A1_textField:item 12, A2_textField:item 14}
		else if first item is second item of popupItems then -- common + alternate B
			set results to common & {B1_textField:item 16, B2_textField:item 18, B3_checkbox:item 19}
		else if first item is third item of popupItems then
			set results to common
		else -- oops
			set results to missing value
		end if
	end tell
	return results
end formatResult

# Add a set of alternate controls to the controlGroups dictionary - controls are returned for any customization.
to addAlternate((group as text), (keyName as text), (controls as list))
	if (controlGroups's valueForKey:group) is missing value then (controlGroups's setValue:(current application's NSMutableDictionary's dictionary()) forKey:group) -- add new group as needed
	controlGroups's setValue:(current application's NSDictionary's dictionaryWithDictionary:{controls:controls}) forKeyPath:(group & "." & keyName) -- add keyName alternate controls to group
	return controls
end addAlternate

# Action to change the visibility of group #1 controls based on the popup value.
on popupAction:sender
	repeat with aKey in popupItems -- hide everything
		set controlList to (controlGroups's valueForKeyPath:("1." & aKey & ".controls"))
		if controlList is not missing value then repeat with aControl in controlList
			((contents of aControl)'s setHidden:true)
		end repeat
	end repeat
	set controlList to (controlGroups's valueForKeyPath:("1." & ((sender's title) as text) & ".controls"))
	if controlList is not missing value then repeat with aControl in controlList -- show the selected control group
		((contents of aControl)'s setHidden:false)
	end repeat
end popupAction:

# Do stuff for the dialog result values.
to doStuff for dialogValues
	tell (operation of dialogValues) to if it is first item of popupItems then
		-- This
	else if it is second item of popupItems then
		-- That
	else if it is third item of popupItems then
		-- The Other
	end if
	return dialogValues
end doStuff

As I told you I tried the only method I know - display dialog - but it doesn’t perform well, but I’m sure there is a method for this

Second question: is the

A2_textField's setDelegate:me

pasted in the right position? It works, but I don’t know if there is a more appropriate place to paste.

Third question: just in case I would like to use this method to limit another textfield, let’s say A1, with different characters (let’s say I would like it to accept only letters and not numbers - just an example to differentiate), how can I do that? I tried duplicating the controltextdidchange but, as you can imagine, it didn’t work because I stuck at the way to refer the first to A1 textfield and the second to A2 textfield

Apart from this, I finished my first version of the script and it seems to work very nice thanks to your huge help :slight_smile: - otherwise I would not have been able to script it, this is an handy addition I would like to add to improve it

1.) You can use one or the other or both, but one reason for using both is that people can get creative when making errors. For example, using multiples of allowed characters that aren’t numbers. In that case, while the textField will contain valid characters, the string may not evaluate to a number. By using controlTextDidChange you can give an indication that a particular character was not accepted, while controlTextDidEndEditing can be used to check for a valid number after editing is completed. Putting up an error dialog for each unaccepted character would probably get annoying, so a better place would be controlTextDidEndEditing, where the check for a valid number can be performed after the whole thing is entered. After checking, you can also do stuff like clear the textField if there is an error. Placeholder text or a tooltip for the textField describing what it is for could also be added, for example:

A2_textField's setToolTip:"This text field only accepts the characters 0-9, comma, plus, minus, and `E`"

2.) I try to group the settings for a particular control together so things don’t get lost, but your location works fine.

3.) The notification sent to the delegate method (notificationObject in my example) contains an object property, which is the object that sent the notification. A comparison can be made to this object (the object variable in my example), but I should probably explain how to access the alternate control instances.

When the script sets up a group of alternate controls, it places their references in the controlGroups property by calling the addAlternate handler. The property is a dictionary that has keys for the group number (there is only one so far), within those there are dictionaries with keys for control values (for this script the popup value), and within those there is a key (controls) for a list of controls (the controls for a particular group and value) - you can also see the layout in Script Debugger’s variable listing for controlGroups. I use this arrangement because a valueForKeyPath method can be used to reduce the number of statements used to get individual items from the dictionary. For example, for the A2_textField, it is the third item of the list of controls that is at the dictionary’s keyPath of “1.This Thing.controls” (also see the list passed to the addAlternate handler).

With that said, for the controlTextDidEndEditing delegate handler you would rearrange things so that it looks something like:

# Verify the value in the formatted text field after editing is complete.
on controlTextDidEndEditing:notificationObject
    set object to notificationObject's object -- the textField that posted the notification
    set controlList to controlGroups's valueForKeyPath:"1.This Thing.controls" -- the controls to compare
    set theText to object's stringValue as text
    if object's isEqual:(item 1 of controlList) then -- check controls as desired
        -- do stuff for the A1_textField
    else if object's isEqual:(item 3 of controlList) then
        -- do stuff for the  A2_textField
    end if
end controlTextDidEndEditing:

The controlTextDidChange delegate handler can be arranged in the same way.

Using tab (to navigate the controls), return, or clicking in another control will end editing in the current textField and trigger its controlTextDidEndEditing delegate. I misspoke in my previous reply - the delegate handler will get called when exiting the dialog while still in the textField, but fixing any error may get more involved since the dialog will be in the process of being dismissed. One solution would be to keep the default button disabled while entering all the various fields, and only enable it after everything is properly filled in - you would need to keep track of all that, though.

You are perfectly right, I didn’t think about that possibility, because I was thinking only to numbers not in scientific notation, but a possible mistyping can be a double decimal sign

I tried completing the script but I can’t get nowhere else than putting a dialog error, which recognizes if the input is a number or not, but obviously makes the program stop :frowning:

This was my poor idea, but I’m sure it’s not what you had in mind

on controlTextDidEndEditing:notificationObject
	set object to notificationObject's object -- the textField that posted the notification
	set controlList to controlGroups's valueForKeyPath:"1.This Thing.controls" -- the controls to compare
	set theText to object's stringValue as text
	
	if object is equal to (item 1 of controlList) then -- check controls as desired
		-- do stuff for the A1_textField
	else if object is equal to (item 3 of controlList) then -- check for A2_textField
		try
			set theNumber to theText as number
		on error
			display dialog "Please enter a valid number for A2." buttons {"OK"} default button "OK"
		end try
	end if
end controlTextDidEndEditing:

This sounds nice but I don’t know if it’s complicated to implement

Your dialog in controlTextDidEndEditing works for me (although you aren’t doing anything to force a change, such as clearing the textField) - are you talking about not getting the error dialog if you close the dialog while still editing the textField? If so, which textFields must be complete (including the number fields that must have a valid number)?

My idea for the test code is that in A2_textfield only allowed characters can be written, and this is controlled by controlTextDidChange, with the setToolTip message when passing the cursor above the textfield, and I like the way this works.
I didn’t realize at first the meaning of the second handler, but you correctly taught me that some combinations of allowed characters that makes the input not a number anymore can be entered: in this case I would like to get a warning that something went wrong, but without having to start again from the beginning, and correct the mistyping.
In my poor solution, if I enter a string of allowed characters that are not a number (let’s say 1E2+) and I press OK, I get an alert but closing it the main dialog is gone. Furthermore, the string is kept. Obviously this happens because there are no instructions to make it behave differently, but this is because I simply don’t know how to write in a programming language what it has to do :slight_smile:

I’m always trying to work on this same example in the way that I can then try translate in my script, hoping that I can understand just a little of how it works :slight_smile:

The controlTextDidEndEditing handler is only called when one of the textFields (that has its delegate set) finishes editing - tabbing or clicking another control, etc. A return can also be used to end editing, except when the main dialog has a default button set, since the return will then be set to click the button (which will end the main dialog). Does it do the same thing if you don’t set a default button when creating the buttons?

You are right, I was unconsciously pressing return without exiting the textfield. So I got the warning but the dialog considered that I hit return and gave output regardless of the mistyping. But I fear that users that will use this script will not have the patience of clicking out of the textfield, so maybe this solution, as it is written, it’s not so useful. Apart from this, when using correctly (clicking out of the textfield and getting back to correct) the main dialog it’s heavily lagging and it’s not easy to correct the mistyped.

I attach the full script for an easier test, if you want to

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use script "Dialog Toolkit Plus" version "1.1"
use script "Dialog Toolkit Plus Extended" version "1.1" -- datePicker


property popupItems : {"This Thing", "That Thing", "The Other Thing"} -- the operations to perform
global controlGroups -- this will be a dictionary of the alternate groups and their controls

# Check out additions to the number formatted text field.
on controlTextDidChange:notificationObject
	set object to notificationObject's object -- the textField that posted the notification
	set controlList to controlGroups's valueForKeyPath:"1.This Thing.controls" -- the controls to compare
	set theText to object's stringValue as text
	if object's isEqual:(item 1 of controlList) then -- check controls as desired
		set acceptable to characters of "ABC" -- or whatever
		set object to notificationObject's object -- the textField that posted the notification
		set theText to object's stringValue as text
		repeat with aCharacter in (characters of theText) -- just check everything in case something was pasted
			considering case -- for the exponent indicator `E`
				if aCharacter is not in acceptable then -- indicate and remove
					current application's NSBeep()
					set {prevTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, aCharacter}
					set {textItems, AppleScript's text item delimiters} to {text items of theText, ""}
					set {theText, AppleScript's text item delimiters} to {textItems as text, prevTID}
					(object's setStringValue:theText)
				end if
			end considering
		end repeat
	else if object's isEqual:(item 3 of controlList) then
		set acceptable to characters of "1234567890E,+- " -- or whatever
		set object to notificationObject's object -- the textField that posted the notification
		set theText to object's stringValue as text
		repeat with aCharacter in (characters of theText) -- just check everything in case something was pasted
			considering case -- for the exponent indicator `E`
				if aCharacter is not in acceptable then -- indicate and remove
					current application's NSBeep()
					set {prevTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, aCharacter}
					set {textItems, AppleScript's text item delimiters} to {text items of theText, ""}
					set {theText, AppleScript's text item delimiters} to {textItems as text, prevTID}
					(object's setStringValue:theText)
				end if
			end considering
		end repeat
		
	end if
end controlTextDidChange:


on controlTextDidEndEditing:notificationObject
	set object to notificationObject's object -- the textField that posted the notification
	set controlList to controlGroups's valueForKeyPath:"1.This Thing.controls" -- the controls to compare
	set theText to object's stringValue as text
	
	if object is equal to (item 1 of controlList) then -- check controls as desired
		-- do stuff for the A1_textField
	else if object is equal to (item 3 of controlList) then -- check for A2_textField
		try
			set theNumber to theText as number
		on error
			display dialog "Please enter a valid number for A2." buttons {"OK"} default button "OK"
		end try
	end if
end controlTextDidEndEditing:

on run -- example
	set controlGroups to current application's NSMutableDictionary's dictionary -- for using a keyPath
	set {viewWidth, theTop} to {400, 28}
	
	# controls are stacked from the bottom up for easier vertical positioning
	set {theButtons, minWidth} to create buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel" with equal widths
	if minWidth > viewWidth then set viewWidth to minWidth
	set {maxLabelWidth, controlLeft} to {100, 110}
	
	########## begin control group #1 - these are layered in the same space, with only the active alternate shown
	set newTop to theTop -- alternate A (This Thing)
	set {A2_textField, A2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop) total width viewWidth label text "This #2:" field left controlLeft
	set {A1_textField, A1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "This #1:" field left controlLeft
	repeat with anItem in (addAlternate(1, first item of popupItems, {A1_textField, A1_labelField, A2_textField, A2_labelField}))
		(anItem's setHidden:true)
	end repeat
	----
	set newTop to theTop -- alternate B (That Thing)
	set {B3_checkbox, newTop, newWidth} to create checkbox "That Checkbox" bottom (newTop - 24) max width (viewWidth / 2) left inset controlLeft without initial state
	set {B2_textField, B2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #2:" field left controlLeft
	set {B1_textField, B1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #1:" field left controlLeft
	repeat with anItem in (addAlternate(1, second item of popupItems, {B1_textField, B1_labelField, B2_textField, B2_labelField, B3_checkbox}))
		(anItem's setHidden:true)
	end repeat
	########## end control group #1
	
	set theTop to newTop -- continue from previous top
	set {divider, theTop} to create rule (theTop + 12) rule width viewWidth -- NSBox
	set {invoiceTextField, invoiceLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width (viewWidth / 2) label text "Invoice number:" field left controlLeft
	set {contractorTextField, contractorLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Contractor:" field left controlLeft
	set {clientTextField, clientLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Client:" field left controlLeft
	set {dateLabelField, theTop} to create label " Date:" left inset 60 bottom (theTop + 12) max width maxLabelWidth control size regular size
	set {datePicker, theTop} to create textual datepicker textual stepper picker elements YearMonthDay initial date (current date) min date seconds 0 max date seconds 0 left inset controlLeft bottom (theTop - 20) extra width 0 extra height 0
	
	set {operationPopup, theTop} to create popup popupItems left inset controlLeft bottom (theTop + 8) popup width 180 initial choice (last item of popupItems)
	operationPopup's setToolTip:"the operation to perform"
	operationPopup's setTarget:me
	operationPopup's setAction:"popupAction:" -- choose from group #1
	
	A1_textField's setDelegate:me
	A2_textField's setDelegate:me
	A1_textField's setToolTip:"This text field only accepts ABC"
	A2_textField's setToolTip:"This text field only accepts the characters 0-9, comma, plus, minus, and `E`"
	
	set allControls to {operationPopup, dateLabelField, datePicker, clientLabelField, clientTextField, contractorLabelField, contractorTextField, invoiceLabelField, invoiceTextField, divider, A1_labelField, A1_textField, A2_labelField, A2_textField, B1_labelField, B1_textField, B2_labelField, B2_textField, B3_checkbox} -- control references are listed from top to bottom for easier indexing
	set {buttonName, controlValues} to display extended window "Descriptive Title" acc view width viewWidth acc view height theTop acc view controls allControls buttons theButtons without align cancel button
	doStuff for (formatResult from controlValues)
end run

# Extract the desired controls from the returned dialog values and group into a record.
to formatResult from dialogResult
	tell (date (item 3 of dialogResult) as «class isot» as string) to set ISODate to text 9 thru 10 & "/" & text 6 thru 7 & "/" & text 1 thru 4 -- format date using the built-in ISO date class (no time zone info)
	tell dialogResult -- use record for consistent key:value pairs
		set common to {operation:item 1, theDate:ISODate, client:item 5, contractor:item 7, invoice:item 9}
		if first item is first item of popupItems then -- common + alternate A
			set results to common & {A1_textField:item 12, A2_textField:item 14}
		else if first item is second item of popupItems then -- common + alternate B
			set results to common & {B1_textField:item 16, B2_textField:item 18, B3_checkbox:item 19}
		else if first item is third item of popupItems then
			set results to common
		else -- oops
			set results to missing value
		end if
	end tell
	return results
end formatResult

# Add a set of alternate controls to the controlGroups dictionary - controls are returned for any customization.
to addAlternate((group as text), (keyName as text), (controls as list))
	if (controlGroups's valueForKey:group) is missing value then (controlGroups's setValue:(current application's NSMutableDictionary's dictionary()) forKey:group) -- add new group as needed
	controlGroups's setValue:(current application's NSDictionary's dictionaryWithDictionary:{controls:controls}) forKeyPath:(group & "." & keyName) -- add keyName alternate controls to group
	return controls
end addAlternate

# Action to change the visibility of group #1 controls based on the popup value.
on popupAction:sender
	repeat with aKey in popupItems -- hide everything
		set controlList to (controlGroups's valueForKeyPath:("1." & aKey & ".controls"))
		if controlList is not missing value then repeat with aControl in controlList
			((contents of aControl)'s setHidden:true)
		end repeat
	end repeat
	set controlList to (controlGroups's valueForKeyPath:("1." & ((sender's title) as text) & ".controls"))
	if controlList is not missing value then repeat with aControl in controlList -- show the selected control group
		((contents of aControl)'s setHidden:false)
	end repeat
end popupAction:

# Do stuff for the dialog result values.
to doStuff for dialogValues
	tell (operation of dialogValues) to if it is first item of popupItems then
		-- This
	else if it is second item of popupItems then
		-- That
	else if it is third item of popupItems then
		-- The Other
	end if
	return dialogValues
end doStuff

Following is an updated example script with all the changes so far - I rearranged the script to group the delegate handlers, and extracted the checks and validations to separate handlers. I also added a requirement that the A1 and A2 textfields both must have valid values in order for the main dialog OK button to be enabled. I mostly did this so that while the OK button is disabled, using return will just end editing for any of the textFields without closing the main dialog. In the event return is used for the last required textField and its contents are valid, the main dialog will also close, since the OK button will have been enabled.

There is additional feedback (text color) when the formatted textField values are valid, and I tried to provide for most common user errors, but there are probably still some edge cases if someone tries hard enough.

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use script "Dialog Toolkit Plus" version "1.1"
use script "Dialog Toolkit Plus Extended" version "1.1" -- datePicker

property popupItems : {"This Thing", "That Thing", "The Other Thing"} -- the operations to perform
global controlGroups -- this will be a dictionary of the alternate groups and their controls
global textColors -- this will be a list of colors for the formatted textFields
global okButton -- a reference for enabling the main `OK` button
global switching -- a flag to indicate switching to another control group while still editing

on run -- build sample controls
    set controlGroups to current application's NSMutableDictionary's dictionary -- for using a keyPath
    set {viewWidth, theTop} to {400, 28}
    
    # controls are stacked from the bottom up for easier vertical positioning
    set {theButtons, minWidth} to create buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel" with equal widths
    set {okButton, switching} to {last item of theButtons, false}
    set textColors to {current application's NSColor's textColor, current application's NSColor's systemGreenColor}
    if minWidth > viewWidth then set viewWidth to minWidth
    set {maxLabelWidth, controlLeft} to {100, 110}
    
    ########## begin control group #1 - these are layered in the same space, with only the active alternate shown
    set newTop to theTop -- alternate A (This Thing)
    set {A2_textField, A2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop) total width viewWidth label text "This #2:" field left controlLeft
    A2_textField's setDelegate:me
    A2_textField's setTag:0 -- tag is used as a boolean to indicate a valid value
    A2_textField's setPlaceholderString:"Required"
    A2_textField's setToolTip:"This text field only accepts the characters 0-9, comma, plus, minus, space, and `E`"
    set {A1_textField, A1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "This #1:" field left controlLeft
    A1_textField's setDelegate:me
    A1_textField's setTag:0 -- tag is used as a boolean to indicate a valid value
    A1_textField's setPlaceholderString:"Required"
    A1_textField's setToolTip:"This text field only accepts the characters A,B, and C (case sensitive)"
    repeat with anItem in (addAlternate(1, first item of popupItems, {A1_textField, A1_labelField, A2_textField, A2_labelField}, false))
        (anItem's setHidden:true)
    end repeat
    ----
    set newTop to theTop -- alternate B (That Thing)
    set {B3_checkbox, newTop, newWidth} to create checkbox "That Checkbox" bottom (newTop - 24) max width (viewWidth / 2) left inset controlLeft without initial state
    set {B2_textField, B2_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #2:" field left controlLeft
    set {B1_textField, B1_labelField, newTop, fieldLeft} to create side labeled field ("") left inset 0 bottom (newTop + 6) total width viewWidth label text "That #1:" field left controlLeft
    repeat with anItem in (addAlternate(1, second item of popupItems, {B1_textField, B1_labelField, B2_textField, B2_labelField, B3_checkbox}, true))
        (anItem's setHidden:true)
    end repeat
    ########## end control group #1
    
    set theTop to newTop -- continue from previous top
    set {divider, theTop} to create rule (theTop + 12) rule width viewWidth -- NSBox
    set {invoiceTextField, invoiceLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width (viewWidth / 2) label text "Invoice number:" field left controlLeft
    set {contractorTextField, contractorLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Contractor:" field left controlLeft
    set {clientTextField, clientLabelField, theTop, fieldLeft} to create side labeled field ("") bottom (theTop + 12) total width viewWidth label text "Client:" field left controlLeft
    set {dateLabelField, theTop} to create label " Date:" left inset 60 bottom (theTop + 12) max width maxLabelWidth control size regular size
    set {datePicker, theTop} to create textual datepicker textual stepper picker elements YearMonthDay initial date (current date) min date seconds 0 max date seconds 0 left inset controlLeft bottom (theTop - 20) extra width 0 extra height 0
    
    set {operationPopup, theTop} to create popup popupItems left inset controlLeft bottom (theTop + 8) popup width 180 initial choice (last item of popupItems)
    operationPopup's setToolTip:"The operation to perform"
    operationPopup's setTarget:me
    operationPopup's setAction:"popupAction:" -- choose from group #1
    
    set allControls to {operationPopup, dateLabelField, datePicker, clientLabelField, clientTextField, contractorLabelField, contractorTextField, invoiceLabelField, invoiceTextField, divider, A1_labelField, A1_textField, A2_labelField, A2_textField, B1_labelField, B1_textField, B2_labelField, B2_textField, B3_checkbox} -- control references are listed from top to bottom for easier indexing
    set {buttonName, controlValues} to display extended window "Descriptive Title" acc view width viewWidth acc view height theTop acc view controls allControls buttons theButtons without align cancel button
    doStuff for (formatResult from controlValues)
end run

# Extract the desired controls from the returned dialog values and group into a record.
to formatResult from dialogResult
    tell (date (item 3 of dialogResult) as «class isot» as string) to set ISODate to text 9 thru 10 & "/" & text 6 thru 7 & "/" & text 1 thru 4 -- format date using the built-in ISO date class (no time zone info)
    tell dialogResult -- use record for consistent key:value pairs
        set common to {operation:item 1, theDate:ISODate, client:item 5, contractor:item 7, invoice:item 9}
        if first item is first item of popupItems then -- common + alternate A
            set results to common & {A1_textField:item 12, A2_textField:item 14}
        else if first item is second item of popupItems then -- common + alternate B
            set results to common & {B1_textField:item 16, B2_textField:item 18, B3_checkbox:item 19}
        else if first item is third item of popupItems then
            set results to common
        else -- oops
            set results to missing value
        end if
    end tell
    return results
end formatResult

# Add a set of alternate controls to the controlGroups dictionary - controls are returned for further customization.
to addAlternate((group as text), (keyName as text), (controls as list), (okEnabled as boolean))
    if (controlGroups's valueForKey:group) is missing value then (controlGroups's setValue:(current application's NSMutableDictionary's dictionary()) forKey:group) -- add new group as needed
    controlGroups's setValue:(current application's NSMutableDictionary's dictionaryWithDictionary:{controls:controls, okEnabled:okEnabled}) forKeyPath:(group & "." & keyName) -- add keyName alternate controls to group
    return controls
end addAlternate

# Action to change the visibility of group #1 controls based on the popup value.
on popupAction:sender
    set switching to true
    repeat with aKey in popupItems -- hide everything
        set controlList to (controlGroups's valueForKeyPath:("1." & aKey & ".controls"))
        if controlList is not missing value then repeat with aControl in controlList
            ((contents of aControl)'s setHidden:true)
        end repeat
    end repeat
    set controlList to (controlGroups's valueForKeyPath:("1." & ((sender's title) as text) & ".controls"))
    if controlList is not missing value then repeat with aControl in controlList -- show the selected control group
        ((contents of aControl)'s setHidden:false)
    end repeat
    set enabled to (controlGroups's valueForKeyPath:("1." & ((sender's title) as text) & ".okEnabled"))
    if enabled is missing value then set enabled to true -- default
    enableOK(enabled as boolean) -- enable/disable button according to control group setting
    set switching to false
end popupAction:

# Do stuff for the dialog result values.
to doStuff for dialogValues
    tell (operation of dialogValues) to if it is first item of popupItems then
        -- perform `This` with dialogValues for this operation
    else if it is second item of popupItems then
        -- perform `That` with dialogValues for that operation
    else if it is third item of popupItems then
        -- perform `The Other` with dialogValues for the other operation
    end if
    return dialogValues
end doStuff


###################################
# Delegate Handlers and Utilities
###################################

# Check changes to a formatted textField.
on controlTextDidChange:notificationObject
    set object to notificationObject's object -- the textField that posted the notification
    set {A1, A2} to {item 1, item 3} of (controlGroups's valueForKeyPath:"1.This Thing.controls") -- the controls to compare
    if (object's isEqual:A1) as boolean then
        checkAcceptable(object, characters of "ABC")
    else if (object's isEqual:A2) as boolean then
        checkAcceptable(object, characters of "1234567890E,+- ")
    end if
    enableOK(false)
end controlTextDidChange:

to checkAcceptable(textFieldRef, acceptable) -- check for acceptable characters - others are removed
    set indicated to false
    textFieldRef's setTextColor:(first item of textColors) -- reset to normal text color
    set theString to textFieldRef's stringValue as text
    repeat with aCharacter in (characters of theString) -- just check the whole string in case something was pasted
        considering case
            if aCharacter is not in acceptable then -- indicate and remove
                if not indicated then -- avoid multiple beeps
                    current application's NSBeep()
                    set indicated to true
                end if
                set theString to (replaceText of theString from aCharacter to "")
                (textFieldRef's setStringValue:theString)
            end if
        end considering
    end repeat
end checkAcceptable

# Validate textField contents after editing.
on controlTextDidEndEditing:notificationObject
    set object to notificationObject's object -- the textField that posted the notification
    set theString to object's stringValue as text
    set {A1, A2} to {item 1, item 3} of (controlGroups's valueForKeyPath:"1.This Thing.controls") -- the controls to compare
    if (object's isEqual:A1) as boolean then
        updateValidity(object, validate_A1(theString), "Unacceptable A1_textField - please enter a valid string.")
    else if (object's isEqual:A2) as boolean then
        updateValidity(object, validate_A2(theString), "Unacceptable A2_textField - please enter a valid number.")
    end if
    set valid to (A1's stringValue as text is not "") and (A1's tag as integer is not 0) and ¬
        (A2's stringValue as text is not "") and (A2's tag as integer is not 0)
    (controlGroups's setValue:valid forKeyPath:"1.This Thing.okEnabled")
    (my enableOK(valid)) -- only enable if everything has a valid value
end controlTextDidEndEditing:

to updateValidity(textFieldRef, (valid as boolean), (errorMessage as text))
    textFieldRef's setTag:(valid as integer)
    textFieldRef's setTextColor:(item ((valid as integer) + 1) of textColors) -- visual feedback
    if not valid then -- indicate that editing is needed
        if not switching then if errorMessage is not "" then (showPopover for errorMessage at textFieldRef)
        if switching then textFieldRef's setStringValue:"" -- clear
    end if
end updateValidity

to validate_A1(theString) -- test if A1_textField contains a valid string
    try
        -- whatever
        return true
    end try
    return false
end validate_A1

to validate_A2(theString) -- test if A2_textField contains a valid number
    try
        theString as number
        return true
    end try
    return false
end validate_A2

to enableOK(flag as boolean) -- enable the OK button according to the flag value
    okButton's setEnabled:flag
    okButton's setKeyEquivalent:(item ((flag as integer) + 1) of {"", return})
end enableOK

to replaceText of (theText as text) from (oldText as text) to (newText as text)
    set {prevTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, oldText}
    set {itemList, AppleScript's text item delimiters} to {text items of theText, newText}
    set {theText, AppleScript's text item delimiters} to {itemList as text, prevTID}
    return theText
end replaceText

to showPopover for (message as text) at viewRef given edge:edge as integer : 1 -- given arguments are optional
    if (message is "") or (viewRef is missing value) then return
    set viewController to current application's NSViewController's alloc's init
    tell (current application's NSTextField's labelWithString:message)
        its setFrameOrigin:{10, 10} -- view inset
        its setAlignment:(current application's NSTextAlignmentCenter)
        its setFont:(current application's NSFont's fontWithName:"Helvetica" |size|:14)
        its setTextColor:(current application's NSColor's systemRedColor)
        its sizeToFit() -- adjust textField to string after text settings
        tell (its frame) to set {width, height} to {current application's NSWidth(it), current application's NSHeight(it)}
        viewController's setView:(current application's NSView's alloc's initWithFrame:{{0, 0}, {width + 20, height + 18}})
        viewController's view's addSubview:it
    end tell
    tell current application's NSPopover's alloc's init()
        its setContentViewController:viewController
        its setBehavior:(current application's NSPopoverBehaviorTransient)
        its showRelativeToRect:{{0, 0}, {0, 0}} ofView:viewRef preferredEdge:edge -- default NSMinYEdge
    end tell
end showPopover

There shouldn’t be any lag when editing, unless your production script is doing something. You can also just avoid editing an invalid value after the error dialog by clearing the textField (this is also done if switching controls while editing).


Edited to update script:

  • Cleaned up duplicate code in the delegate and supporting handlers.
  • Added handler to use popovers as a workaround for issues discovered when using display alert or display dialog with the Dialog Toolkit in a script application.

I have to study it and try to translate into my script but for a first test it looks amazing! This is a great upgrade and feels exactly like what I had in mind, so I mark this as solution. In case I will have some troubles translating in my script I will come back here, but for now I think we’re done. Thank you again for the help!

I found how you can replicate the lag also in this updated version: after inserting a bad number in A2_textfield, instead of clicking tab or return, just click in another textfield (e.g. A1_textfield). In this case you get the dialog, but getting back to edit the A2_textfield is not working as I would expect (and as it works when clicking tab or return). Do you have any idea what is causing this? I hope I did explain myself

It is normal to switch to the control you click in, so having to switch back would also be normal - is that what you are talking about? Being able to use return was one of the reasons for disabling the OK button.

If you are wanting to switch back to the error textField no matter what control you switch to, I haven’t had much luck manipulating the key-view loop (the tab navigation). GUI scripting via System Events isn’t going to work very well either, as it would need to identify the controls. I’m still experimenting, but so far the dialog window isn’t cooperating.

I have noticed that using a popover instead of an alert/dialog is a little better, in that you can navigate while it is showing, and it goes away when you click anywhere outside it. You still need to get back to the textField, although it isn’t quite as intrusive as an alert/dialog. I can edit the previous script if you wanted to try that.

Using return or tab keys seem to work as I would expect, the problem happens changing the textfield using the mouse. I registered a short video of the strange behavior I’m talking about, hoping that it makes more clear what I’m trying to explain :slight_smile: how can I show you? Seems that I can’t upload here, I can send you a PM with a dropbox or icloud link depending on what is your preference

There appears to be an issue in current versions of macOS with editing text fields after displaying an alert/dialog from within the dialog in a script application using the Dialog Toolkit. For whatever reason, text fields will no longer be updated unless the editing is ended or the text field is repeatedly clicked while editing. Normal operation resumes after operations with various other controls, but until then things act weird.

This does not appear in some earlier macOS versions (tested in 12.7.6 Monterey and 10.12.6 Sierra systems without issue), so there appears to be a bug/regression/interaction somewhere. As a workaround, the latest script (post #31) has been updated to include a handler to use popovers, which don’t appear to exhibit the same behavior.

1 Like