probably. Then use something like:
(theAlert's |buttons|()'s objectAtIndex:0)'s setEnabled:false
And you must do that on the main thread.
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.
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.