Change elements dynamically using Shane's Dialog Toolkit?

probably. Then use something like:

(theAlert's |buttons|()'s objectAtIndex:0)'s setEnabled:false

And you must do that on the main thread.

Thanks for helping out even when you’re on vacation!

Here’s what I’ve got:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use script "Dialog Toolkit ROT"


set {aPopop, printAreaPopupLabel, theTop, matrixLeft} to create labeled popup {"One", "Two", "Three"} left inset 0 bottom 0 popup width 300 max width 400 label text "Selection" popup left 0

aPopop's setTarget:me
aPopop's setAction:"setActive:"


set {buttonName, suppressedState, controlsResults, theAlert} to display enhanced alert with return reference "The Title" message "" as informational alert buttons {"Cancel", "OK"} giving up after 120 acc view width 700 acc view height theTop acc view controls {aPopop} without suppression


on setActive:sender
	my ((theAlert's |buttons|()'s objectAtIndex:0)'s setEnabled:false)
end setActive:

And so far I’ve been running it on the main thread by running it from Script Editor with [control]. I figured I’d move it back to Script Debugger and figure out how to run it in the main thread there after I have it working.

Currently, it does not disable, and console logs:

I’m used to using UI Browser or Accessibility Inspector to browse UI’s for Applescript UI scripting, but don’t know how to figure out how to properly refer to UI elements in AS Obj C.

  • Tom.

So… I was thinking about this some more, and am wondering if there isn’t a “catch 22” here…

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use script "Dialog Toolkit ROT"


set {aPopop, printAreaPopupLabel, theTop, matrixLeft} to create labeled popup {"One", "Two", "Three"} left inset 0 bottom 0 popup width 300 max width 400 label text "Selection" popup left 0

aPopop's setTarget:me
aPopop's setAction:"setActive:"


set {buttonName, suppressedState, controlsResults, theAlert} to display enhanced alert with return reference "The Title" message "" as informational alert buttons {"Cancel", "OK"} giving up after 120 acc view width 700 acc view height theTop acc view controls {aPopop} without suppression

set someVariable to (theAlert's |buttons|()'s objectAtIndex:0)

return someVariable

on setActive:sender
	my ((theAlert's |buttons|()'s objectAtIndex:0)'s setEnabled:false)
end setActive:

This still gets the same error, but here it returns

so that looks to me like it’s finding “(theAlert’s |buttons|()'s objectAtIndex:0)”

So I was thinking about my addition of returning a reference, and I’m thinking that this doesn’t actually work with order-of-operations here…

All the variables set when the “display enhanced alert with return reference” command is called aren’t going to be set until that handler in the dictionary completes and returns to executing the script, right? So “theAlert” isn’t defined in my script until the user has clicked “OK.” But the “setActive:” handler is being called and using the “theAlert” variable before that dialog is returned.

If that’s correct, then I’m thinking that, rather than simply modifying “display enhanced alert” to be “display enhanced alert with return reference” and return the “theAlert” variable the handler used, I need to construct an alternate version of “display enhanced alert” that first just returns what’s constructed as “theAlert,” then I need to call that separately.

I’ll take a look at doing that. I feel like this has to be the problem. I should have thought of this.

Hooray!

It seems I correctly identified the problem and solution.

New functions in library:

on return enhanced alert mainText message theExplanation as styleType buttons buttonsList suppression showSuppression acc view width theWidth acc view height theHeight acc view controls controlsList
	if styleType = critical alert then
		set styleNum to 2
	else if styleType = warning alert then
		set styleNum to 0
	else
		set styleNum to 1
	end if
	set theError to current application's AEInteractWithUser(-1, missing value, missing value) -- -1 is kAEDefaultTimeout
	if theError is not 0 then
		error "User interaction disallowed" number theError
	end if
	-- make the accessory view
	set theAccessoryView to current application's NSView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, theWidth, theHeight))
	theAccessoryView's setSubviews:controlsList
	-- reverse buttons because they get added in reverse order cf AS
	set buttonsList to reverse of buttonsList
	-- create an alert
	set theAlert to current application's NSAlert's alloc()'s init()
	-- set up alert
	tell theAlert
		its setAlertStyle:styleNum
		its setMessageText:mainText
		its setInformativeText:theExplanation
		repeat with anEntry in buttonsList
			(its addButtonWithTitle:anEntry)
		end repeat
		its setShowsSuppressionButton:showSuppression
		its setAccessoryView:theAccessoryView
		its (|window|()'s setAutorecalculatesKeyViewLoop:true)
	end tell
	set alertRecord to {theAlert:theAlert, controlsList:controlsList, buttonsList:buttonsList}
	return {alertRecord}
end return enhanced alert

on show returned alert alert reference alertRecord giving up after giveUp
	set theAlert to theAlert of alertRecord
	set controlsList to controlsList of alertRecord
	set buttonsList to buttonsList of alertRecord
	-- if giveUp value > 0, tell the app to abort any modal event loop after that time, and thus close the panel
	if giveUp > 0 then current application's NSApp's performSelector:"abortModal" withObject:(missing value) afterDelay:giveUp inModes:{current application's NSModalPanelRunLoopMode}
	-- show alert in modal loop on main thread
	my performSelectorOnMainThread:"showTheAlert:" withObject:theAlert waitUntilDone:true
	--	if a giveUp time was specified and the alert didn't timeout, cancel the pending abort request
	if giveUp > 0 and returnCode is not current application's NSModalResponseAbort then current application's NSObject's cancelPreviousPerformRequestsWithTarget:(current application's NSApp) selector:"abortModal" object:(missing value)
	-- get values after alert is closed
	set suppressedState to theAlert's suppressionButton()'s state() as boolean
	set buttonNumber to returnCode mod 1000 + 1 -- where 1 = right-most button
	if buttonNumber = 0 then
		set buttonName to "Gave Up"
	else
		set buttonName to item buttonNumber of buttonsList
	end if
	-- get values from controls
	set controlResults to {}
	repeat with aControl in controlsList
		if (aControl's isKindOfClass:(current application's NSTextField)) as boolean then
			set end of controlResults to aControl's stringValue() as text
		else if (aControl's isKindOfClass:(current application's NSPopUpButton)) as boolean then
			set end of controlResults to aControl's titleOfSelectedItem() as text
		else if (aControl's isKindOfClass:(current application's NSButton)) as boolean then
			set end of controlResults to aControl's state() as boolean
		else if (aControl's isKindOfClass:(current application's NSPathControl)) as boolean then
			set end of controlResults to aControl's |URL|()'s |path|() as text
		else if (aControl's isKindOfClass:(current application's NSMatrix)) as boolean then
			set end of controlResults to aControl's selectedCell()'s title() as text
		else -- NSBox
			set end of controlResults to missing value
		end if
	end repeat
	return {buttonName, suppressedState, controlResults}
end show returned alert

new test script:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use script "Dialog Toolkit ROT"

property alertRecord : {}

set {aPopop, printAreaPopupLabel, theTop, matrixLeft} to create labeled popup {"One", "Two", "Three"} left inset 0 bottom 0 popup width 300 max width 400 label text "Selection" popup left 0

aPopop's setTarget:me
aPopop's setAction:"setActive:"



set {alertRecord} to return enhanced alert "The Title" message "" as informational alert buttons {"Cancel", "OK"} acc view width 700 acc view height theTop acc view controls {aPopop} without suppression

set {buttonName, suppressedState, controlsResults} to show returned alert alert reference alertRecord giving up after 120


on setActive:sender
	set theAlert to theAlert of alertRecord
	((theAlert's |buttons|()'s objectAtIndex:0)'s setEnabled:false)
end setActive:

And it works!

However, I’ve been trying to get the syntax right for running it on the main thread in Script Debugger, and that’s been an issue for me so far.

Oops! Nope, just thought of something else and tried that, and now I’ve got it working from Script Debugger like this:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use script "Dialog Toolkit ROT"

property alertRecord : {}

set {aPopop, printAreaPopupLabel, theTop, matrixLeft} to create labeled popup {"One", "Two", "Three"} left inset 0 bottom 0 popup width 300 max width 400 label text "Selection" popup left 0

aPopop's setTarget:me
aPopop's setAction:"setActive:"


set {alertRecord} to return enhanced alert "The Title" message "" as informational alert buttons {"Cancel", "OK"} acc view width 700 acc view height theTop acc view controls {aPopop} without suppression

set {buttonName, suppressedState, controlsResults} to show returned alert alert reference alertRecord giving up after 120


on setActive:sender
	my performSelectorOnMainThread:"setActiveOnMain:" withObject:{missing value} waitUntilDone:true
end setActive:

on setActiveOnMain:sender
	set theAlert to theAlert of alertRecord
	((theAlert's |buttons|()'s objectAtIndex:0)'s setEnabled:false)
end setActiveOnMain:

At first I was trying to get the syntax right to call the “setActive:” handler from “aPopop’s setAction:” AND run it on the main thread as one call. Maybe that’s possible, but I couldn’t get it. Then it occurred to me to just nest the functions.

So thanks so much!

Feel free to tell me to take a hike if I’m driving you nuts here, but there’s always something else that would make the UI better. I’d love to have a super obvious indicator when the UI was able to fully populate itself with default values, so the user should be able to just hit “return” unless they know they’re doing something unusual and will have to modify the defaults. I was hoping to maybe set the entire background of the dialog to a pale green when everything’s auto-filled, and a pale red when they have to pay attention. Or maybe change the icon to signify these things. What I’ve been able to Google up so far does not sound promising on the background color.

I can, of course, change a label, or even the dialog title, but I’ve found UI-wise that anything requiring someone to read text is much slower for users than something like the background color being as an indicator.

*** EDIT AT END - Figured it out ***

Now I’m having the same issue I had with having a default selection on popup lists, but with deactivation of the “OK” button.

It’s working fine to activate or deactivate the “OK” button based on any user interaction, but the intention was, until the a valid set of selections have been made, for the button to be deactivated.

This means that it must be deactivated when the dialog opens, and I’ll use logic to activate it. The activation part looks good, but I can’t get it to be deactivated on open.

I’ve been trying to modify Dialog Toolkit for this, but it seems to be back to the “chicken and the egg” problem that

does not work until the Alert window is open.

Adding:

inside Dialog Toolkit when the dialog was being constructed did work, but doing the same inside dialog toolkit for setting button activation does not, it can only be deactivated/activated once the dialog is shown.

Putting it inside the “showTheAlert” handler in Dialog Toolkit immediately after

doesn’t invoke it until the dialog has been dismissed.

I’ve been looking through the documentation for something like “defaultEnabled” under NSAlert and NSButton, but with no luck.

Man, I keep writing these and then figuring out something else to try when I’m basically done… then I think about just not posting, but I figure it’s better to post the problem/solution in the thread in case anyone in the future Google’s it up.

Functioning edit to Display Enhanced Alert making the buttons:

repeat with anEntry in buttonsList
			set aButton to (its addButtonWithTitle:anEntry)
			if buttonActive is false then
				if anEntry as text is "OK" then
					(aButton's setEnabled:false)
				end if
			end if
		end repeat

I’m having trouble with setting the icon.

I modified “Display Enhanced Alert” in the following way:

	tell theAlert
		its setAlertStyle:styleNum
		its setMessageText:mainText
		its setInformativeText:theExplanation
		repeat with anEntry in buttonsList
			set aButton to (its addButtonWithTitle:anEntry)
			if buttonActive is false then
				if anEntry as text is "OK" then
					(aButton's setEnabled:false)
				end if
			end if
		end repeat
		its setShowsSuppressionButton:showSuppression
		its setAccessoryView:theAccessoryView
		its (|window|()'s setAutorecalculatesKeyViewLoop:true)
		its icon:theImage -- <- ADDED THIS
	end tell

It appeared to me that that was the right syntax from the Xcode documentation…

And the documentation for “icon” says it wants an NSImage as it’s argument.

I pass theIcon into “Display Enhanced Alert” as an argument.

In the calling script, I added “use framework “AppKit”” at the top, and then I set the variable passed as “theIcon” as follows:

set dialogIconPath to "/Volumes/Hackintosh HD/Users/work/Dropbox/Clipart Project/ROT Setup/Script Dependencies/AIC_ICON.png"
set dialogIcon to current application's NSImage's alloc()'s initWithContentsOfFile:dialogIconPath

And I pass “dialogIcon” to “Display Enhanced Alert” where it becomes “theIcon” variable.
The error I get is:

Any help?

Thanks,

Tom.

Try:

its setIcon:theImage

Ah, thanks, that seems obvious in retrospect, especially looking at how you set all the other values.

Hard to get used to gluing words together and changing capitalization to build commands when I’m used to Applescript.

  • Tom.