Creating custom dropdown menus with apple script or JXA, but not with choose from list function

Hey red_menace

This does exactly what I need, thank you very much for the example.
I already tried it in context of my project and it worked exactly as I imagined.

I’m so grateful for your help and help from the others. Thank you all. You definitely threw me in some new world, at least for me.
Gonna studdy this for sure, as I’d like to understand all this.

Sorry for bothering you, but I have a problem here.

The code you sent me really works. It works even after I did modifications and adjusted everything to my needs.
However, it works only if ran from script editor.
When I copy the code to keyboard maestro action “Execute apple script” it doesn’t run, or maybe it does but there’s no any error or sign of something is going on.
I tried to save the script as application then ran it from keyboard maestro.
Firstly I got the popup dialog which tells me to click Run button to run script or quit button to cancel it, and if I click run it does work with no issues.

Then I found a forum post where someone had same problem, and the fix was to not use Control modifier in key command which calls the script, that guy used the alfred, I use keyboard maestro. It really got rid of that annoying pop up, but now although the script runs, screen reader tells me that menu is openned, but I can’t go through the menu items, it acts like either menu is empty which is not, or like screen reader can’t focus on it, don’t know.

I tried all this with your original example, with no modifications and same thing happens.
Of course, the applet normally runs if I run it from desktop.
Any idea what can be causing this?

Thanks in advance.

Some news.
The keyboard maestro does shows something, I get a dialog which displays missing value, which is obviously result of script.
I didn’t see this dialog immediately, just noticed that are some hidden system dialogs as VoiceOver calls them and those were keyboard maestro dialogs.

I get now why it displays missing value as a result, but I don’t get why it works normally in cases I explained above, but not in this case.

I don’t use Keyboard Maestro or Alfred, so was not able to test with those. For an application, it sounds like it was saved with the Show Startup Screen option selected. The original script also works with osascript if you can use that, either from the Terminal or in a do shell script command, just be aware of the warning I mentioned earlier.

If the window is displayed and you can close it by hitting escape, try to coerce the menu list to ˋlistˋ:

its addItemsWithTitles:(itemList as list)

But if you can’t close it, your script has some issue that makes it crash.
It can be a terminology issue or a variable that is not in the right class…

If you posted your entire script, we could be of a better help.

Ok, here’s the full code which runs in script editor, but with keyboard maestro behaves as I previously explained.

**use** *framework* "Foundation"

**use** *scripting additions*

# UI item outlets

**property** mainWindow : *missing value*

**property** popupButton : *missing value*

# script properties

**property** outcome : *missing value* -- result

**property** fail : *missing value* -- error

**on** **run** -- example

**try**

**if** *current application's* NSThread's isMainThread() **as** *boolean* **then**

doStuff()

**else**

**my** performSelectorOnMainThread:"doStuff" withObject:(*missing value*) waitUntilDone:*true*

**end** **if**

**if** fail **is** **not** *missing value* **then** **error** fail's errmess number fail's errnum

**return** outcome

**on** **error** errmess number errnum

**display alert** "Error " & errnum message errmess

**end** **try**

**end** **run**

**on** doStuff() -- UI stuff needs to be done on the main thread

**try**

**set** mainWindow **to** makeWindow at {} **with** panel **given** contentSize:{0, 0} --, title:"This is a Test", styleMask:3

**set** menuList **to** getEffects()

**set** theDefault **to** *item* 1 **of** menuList

**set** **my** popupButton **to** makePopupButton at {20, 20} **given** itemList:menuList --, title:theDefault

**set** subViewItems **to** {popupButton}

**repeat** **with** aSubview **in** subViewItems -- add subviews in key view order

(mainWindow's contentView()'s addSubview:aSubview)

**end** **repeat**

mainWindow's setInitialFirstResponder:(mainWindow's contentView) -- or whatever

mainWindow's makeKeyAndOrderFront:**me**

popupButton's performClick:**me**

mainWindow's performClose:(*missing value*)

**on** **error** errmess number errnum

**set** **my** fail **to** {errmess:errmess, errnum:errnum}

**end** **try**

**end** doStuff

# Make and return a NSWindow or NSPanel.

# Default styleMask includes title, close, and minimize buttons, and is not resizeable.

# If no origin is given the window will be centered.

**to** makeWindow at (origin **as** *list*) **given** contentSize:contentSize **as** *list* : {400, 200}, styleMask:styleMask **as** *integer* : 15, title:title **as** *text* : "", panel:panel **as** *boolean* : *false*, floats:floats **as** *boolean* : *false*, aShadow:aShadow **as** *boolean* : *true*, minimumSize:minimumSize **as** *list* : {}, maximumSize:maximumSize **as** *list* : {}, backgroundColor:backgroundColor : *missing value*

**tell** *current application* **to** **set** theClass **to** *item* ((panel **as** *integer*) + 1) **of** {**its** NSWindow, **its** NSPanel}

**tell** (theClass's alloc()'s initWithContentRect:{{0, 0}, contentSize} styleMask:styleMask backing:2 defer:*true*)

**if** origin **is** {} **then**

**tell** **it** **to** |center|()

**else**

**its** setFrameOrigin:origin

**end** **if**

**if** title **is** **not** "" **then** **its** setTitle:title

**if** panel **and** floats **then** **its** setFloatingPanel:*true*

**its** setHasShadow:aShadow

**if** minimumSize **is** **not** {} **then** **its** setContentMinSize:minimumSize

**if** maximumSize **is** **not** {} **then** **its** setContentMaxSize:maximumSize

**if** backgroundColor **is** **not** *missing value* **then** **its** setBackgroundColor:backgroundColor

**its** setAutorecalculatesKeyViewLoop:*true* -- include added items in the key loop

**return** **it**

**end** **tell**

**end** makeWindow

# Make and return a NSPopUpButton.

**to** makePopupButton at (origin **as** *list*) **given** maxWidth:maxWidth **as** *integer* : 0, itemList:itemList **as** *list* : {}, title:title **as** *text* : "", pullDown:pullDown **as** *boolean* : *false*, tag:tag **as** *integer* : 0, action:action **as** *text* : "popupButtonAction:", target:target : *missing value*

**if** title **is** "missing value" **then** **set** title **to** ""

**if** maxWidth < 0 **then** **set** maxWidth **to** 0 -- a maxWidth of 0 will size to fit the menu

**tell** (*current application's* NSPopUpButton's alloc()'s initWithFrame:{origin, {maxWidth, 32}} pullsDown:pullDown)

**its** addItemsWithTitles:itemList

**if** pullDown **then** -- initial title

**its** insertItemWithTitle:"" atIndex:0 -- add placeholder

**its** setTitle:title

**else** -- initial selection

**if** title **is** **not** "" **and** title **is not** **in** itemList **then** **set** title **to** **first** *item* **of** itemList

**end** **if**

**its** selectItemWithTitle:title -- blank title (all items deselected) if empty

**if** tag > 0 **then** **its** setTag:tag

**if** action **is not** **in** {"", "missing value"} **then**

**if** target **is** *missing value* **then** **set** target **to** **me** -- 'me' can't be used as an optional default

**its** setTarget:target

**its** setAction:action -- see the following action handler

**end** **if**

**if** maxWidth **is** 0 **then** -- sizeToFit works differently for pull-down (title vs menu), so do it manually

**set** theSize **to** width **of** (**its** |menu|'s |size| **as** *record*)

**if** pullDown **then** **set** theSize **to** theSize + 10 -- adjust for checkmark space

**its** setFrameSize:{theSize, 25}

**end** **if**

**return** **it**

**end** **tell**

**end** makePopupButton

# Perform an action when the connected popup button is pressed.

**on** popupButtonAction:sender

**set** selected **to** sender's titleOfSelectedItem **as** *text*

**if** (sender's pullsDown **as** *boolean*) **then** -- for pull-down

sender's setTitle:selected -- synchronizeTitleAndSelectedItem doesn't want to work

sender's sizeToFit() -- sized according to the title

**end** **if**

**set** **my** outcome **to** selected

-- whatever

**end** popupButtonAction:

**on** getEffects()

**tell** *application* "System Events" **to** **tell** *process* "Logic Pro X"

**tell** (*windows* **whose** title **ends with** "- Tracks")

**tell** (*groups* **whose** description **is** "Inspector")

**tell** *UI element* 1 **of** *UI element* 1 **of** **last** *group* **of** *list* 1

**set** effects **to** {}

**repeat** **with** theItem **from** 2 **to** **count** **of** *groups*

**set** counter **to** **count** **of** value **of** *attribute* "AXChildren" **of** *group* theItem

**if** counter = 3 **then**

**set** **beginning** **of** effects **to** description **of** *group* theItem **as** *string*

**end** **if**

**end** **repeat**

**end** **tell**

**end** **tell**

**end** **tell**

**end** **tell**

**return** effects

**end** getEffects ```

Try this and let us know if you get an error.

use framework "Foundation"
use scripting additions

-- UI item outlets
property mainWindow : missing value
property popupButton : missing value

-- script properties
property outcome : missing value
property fail : missing value

on run -- example
	try
		if current application's NSThread's isMainThread() as boolean then
			doStuff()
		else
			my performSelectorOnMainThread:"doStuff" withObject:(missing value) waitUntilDone:true
		end if
		if fail is not missing value then error fail's errmess number fail's errnum
		return outcome
	on error errmess number errnum
		display alert "Error " & errnum message errmess
	end try
end run

-- UI stuff needs to be done on the main thread
on doStuff()
	try
		set mainWindow to makeWindow at {} with panel given contentSize:{0, 0} --, title:"This is a Test", styleMask:3
		set menuList to getEffects()
		set menuList to menuList as list -- consolidate the list
		log menuList
		if menuList = {} then error "Menu list is empty" number -2700 -- -2700 is a generic Applescript error
		set theDefault to item 1 of menuList
		set my popupButton to makePopupButton at {20, 20} given itemList:menuList --, title:theDefault
		set subViewItems to {popupButton}
		repeat with aSubview in subViewItems -- add subviews in key view order
			(mainWindow's contentView()'s addSubview:aSubview)
		end repeat
		mainWindow's setInitialFirstResponder:(mainWindow's contentView) -- or whatever
		mainWindow's makeKeyAndOrderFront:me
		popupButton's performClick:me
		mainWindow's performClose:(missing value)
	on error errmess number errnum
		set my fail to {errmess:errmess, errnum:errnum}
	end try
end doStuff

-- Make and return a NSWindow or NSPanel.
-- Default styleMask includes title, close, and minimize buttons, and is not resizeable.
-- If no origin is given the window will be centered.
to makeWindow at (origin as list) given contentSize:contentSize as list : {400, 200}, styleMask:styleMask as integer : 15, title:title as text : "", panel:panel as boolean : false, floats:floats as boolean : false, aShadow:aShadow as boolean : true, minimumSize:minimumSize as list : {}, maximumSize:maximumSize as list : {}, backgroundColor:backgroundColor : missing value
	tell current application to set theClass to item ((panel as integer) + 1) of {its NSWindow, its NSPanel}
	tell (theClass's alloc()'s initWithContentRect:{{0, 0}, contentSize} styleMask:styleMask backing:2 defer:true)
		if origin is {} then
			tell it to |center|()
		else
			its setFrameOrigin:origin
		end if
		if title is not "" then its setTitle:title
		if panel and floats then its setFloatingPanel:true
		its setHasShadow:aShadow
		if minimumSize is not {} then its setContentMinSize:minimumSize
		if maximumSize is not {} then its setContentMaxSize:maximumSize
		if backgroundColor is not missing value then its setBackgroundColor:backgroundColor
		its setAutorecalculatesKeyViewLoop:true -- include added items in the key loop
		return it
	end tell
end makeWindow

-- Make and return a NSPopUpButton.
to makePopupButton at (origin as list) given maxWidth:maxWidth as integer : 0, itemList:itemList as list : {}, title:title as text : "", pullDown:pullDown as boolean : false, tag:tag as integer : 0, action:action as text : "popupButtonAction:", target:target : missing value
	if title is "missing value" then set title to ""
	if maxWidth < 0 then set maxWidth to 0 -- a maxWidth of 0 will size to fit the menu
	tell (current application's NSPopUpButton's alloc()'s initWithFrame:{origin, {maxWidth, 32}} pullsDown:pullDown)
		its addItemsWithTitles:itemList
		if pullDown then -- initial title
			its insertItemWithTitle:"" atIndex:0 -- add placeholder
			its setTitle:title
		else -- initial selection
			if title is not "" and title is not in itemList then set title to first item of itemList
		end if
		its selectItemWithTitle:title -- blank title (all items deselected) if empty
		if tag > 0 then its setTag:tag
		if action is not in {"", "missing value"} then
			if target is missing value then set target to me -- ‘me' can't be used as an optional default
			its setTarget:target
			its setAction:action -- see the following action handler
		end if
		if maxWidth is 0 then -- sizeToFit works differently for pull-down (title vs menu), so do it manually
			set theSize to width of (its |menu|'s |size| as record)
			if pullDown then set theSize to theSize + 10 -- adjust for checkmark space
			its setFrameSize:{theSize, 25}
		end if
		return it
	end tell
end makePopupButton

-- Perform an action when the connected popup button is pressed.
on popupButtonAction:sender
	set selected to sender's titleOfSelectedItem as text
	if (sender's pullsDown as boolean) then -- for pull-down
		sender's setTitle:selected -- synchronizeTitleAndSelectedItem doesn't want to work
		sender's sizeToFit() -- sized according to the title
	end if
	set my outcome to selected
	-whatever
end popupButtonAction:

-- Get effects by UI scripting 
on getEffects()
	tell application "System Events" to tell process "Logic Pro X"
		tell (window 1 whose title ends with "- Tracks")
			set theParent to a reference to (UI element 1 of UI element 1 of last group of list 1 of (group 1 whose description is "Inspector"))
			if not (exists theParent) then return "Parent UI element is missing"
			tell theParent
				set effects to {}
				set theCount to (count of groups)
				if theCount < 1 then return "No children found"
				repeat with theItem from 1 to theCount
					set counter to count of (value of (attribute "AXChildren" of group theItem))
					if counter = 3 then
						set beginning of effects to (description of group theItem) as string
					end if
				end repeat
			end tell
		end tell
	end tell
	
	return effects
end getEffects

When called with execute apple script from keyboard maestro, same thing as before, I get that missing value thing.

If I compile it as application it will run but still can’t go through menu items, screen reader announces it’s menu and it’s openned, but can’t do anything. Hitting escape key closes it.
I’d avoid using it like that even if it works, as there needs to be multiple such context menus, and if they run as applications, end user would have to give a permision, I mean to all of the examples to run, that’s not convenient at all.

I tried also from execute apple script action, to call this script with do shell script, but still the same: “missing value”.

I have installed the latest version of KM.

  • created a new macro with only one action: execute an applescript
  • pasted the script from my last post in the text area
  • asked to display result in a window
  • gave it a hot key trigger

Then I ran the macro by pressing the hot key:

  • the popup menu shows up
  • clicked a menu item
  • the title of the chosen menu was displayed in a window

In short: it worked.

Do you have other steps or actions in your KM macro?
If yes, can you create one with only the script?
Can you reset the accessibility permission?

If none of these works, maybe you can try FastScripts?


MacOS 12.7.5 (21H1222) French localization
Keyboard Maestro 11.0.3 (11.0.3)
FastScripts 3.3.3 (1864)
Script Debugger 8.0.8 (8A80)

I edited my previous example to include a testing property and say statements to indicate what the script will be trying to return, independent of what is running it. Note that the default when running a script in Keyboard Maestro is to ignore the result; have you set that option correctly?

An application also won’t return anything, so you would need to add whatever to get the result from the action or run handler before the application quits and do your thing there. Using preferences or the Application Scripts folder could possibly be used to add dynamic menu lists without running into permission issues.

Also, please edit your previous script to add formatting - note that you can start and end the script text with three backticks to use the forum’s formatting, which avoids the typographer’s (curly) quotes and includes a button to open it in the script editor.

Hello Folks :wave:,

Especially @Lucky_Magician.

I am a Keyboard Maestro user for quite some time (since Version 8).

The Dialog with the Popup Button Menu has always to run on the main thread. To ensure this save it as a .scptd (Script-Bundle -) File and call it using an AppleScript From Keyboard Maestro or use the Filepath Variant of the Execute an AppleScript Action.
The action doesn’t have support for Text based Scripts like this one. I’ve requested this as a feature to Keyboard Maestro’s Developer but I don’t really know if he implements it or not.

If you’re a FastScripts User call the saved Script-Bundle from AppleScript using FastScripts‘s AppleScript support.

Normally this should work without issues - I have quite a few Scripts with Dialogs written this way or using Shane’s Dialog Toolkit Plus and I do it this way.

Please note: This should also be done exact the same way if the dialog was created with Shane‘s Dialog Toolkit Plus Library.

Greetings from Germany :de:

Tobias

Did the same as you just explained, latest keyboard maestro version, macro with trigger and single action: execute apple script.
When I run it screen reader says osa scripting not responding, and in KM display dialog I get that missing value result.
This is gonna be published for everyone, for free of course, so many blind people can use it, or anyone else, so it’s not that convenient to use both keyboard maestro and fast scripts.
However, if it works for you, then it must work here too. Yes, I copied your code with no changes, made sure it’s working by running it from script editor, then put it to keybaord maestro.
What OS version you’re on?
I have latest sonoma installed, Logic Pro is also latest version, keyboard maestro latest version too.

O yes, I’m aware of it, keyboard maestro user for long time, and I made sure to display result.
I added additional handler which deals with result, basicly just take the string from result (option I select in popup menu) then finds group whose description is string returned, and clicks open button to open the plug-in window. Again, it works from script editor, as it’s nothing complicated, but from keyboard maestro it doesn’t that is, behaves the same as I explained in my previous post.

BTW, thank you for letting me know how to format the pasted code to get that show in editor button, I wondered how you guys do it, I’ll edit my previous example.

Hey @Nr.5-need_input

Thank you for the suggestions. I tried saving script as bundle, but it doesn’t work. I mean the behaviour is the same, except now I just don’t get displayed anything from Keyboard Maestro. Probably Peter hasn’t implemented that yet.

However, it seems like it works for @ionah so there must be something on my machine which stops it from working.

I clearly understand the purpose and constraints of this script. I am not suggesting that every user should use FS. I was just asking if you could install a demo to see how your script behaves in that environment: it seems that there is a problem with OSAScript. Is it due to KM or Sonoma? I am trying to isolate the problem.

My macOS version is Monterey 12.7.5. This may explain why the script is working for me and not on your configuration. Is it possible for you to run this script on another Mac running Sonoma? Or maybe you could ask on the KM forum?

There is one point you didn’t answer: have you reset the accessibility permission in system settings? (You need to delete and re-import KM to make sure it is taken into account).

But before all that, can you test this revised version of @red_menace excellent script:

use framework "Foundation"
use framework "AppKit"
use scripting additions

-- UI item outlets
property thePanel : missing value
property thePopup : missing value

-- script properties
property outcome : missing value
property fail : missing value

on run
	try
		-- make sure LPX is open
		if application "Logic Pro X" is not running then error "Logic Pro is not running" number -2700
		
		-- build and show the menu on main thread
		if current application's NSThread's isMainThread() as boolean then
			displayMenu()
		else
			my performSelectorOnMainThread:"displayMenu" withObject:(missing value) waitUntilDone:true
		end if
		
		-- return result or display alert
		if fail is not missing value then error fail's theMess number fail's theNum
		return outcome
	on error theMess number theNum
		tell application "Logic Pro X" to display alert theMess message "Error " & theNum
	end try
end run

-- UI stuff needs to be done on the main thread
on displayMenu()
	try
		-- build the container window
		set thePanel to (current application's NSPanel's alloc()'s initWithContentRect:{{0, 0}, {400, 200}} styleMask:15 backing:2 defer:true)
		tell thePanel
			its |center|()
			its setFloatingPanel:true
			its setAutorecalculatesKeyViewLoop:true -- include added items in the key loop
		end tell
		
		-- get the list of effects in Logic Pro
		set menuList to getEffects()
		set menuList to menuList
		if menuList = {} then error "Menu list is empty" number -2700 2700 is a generic Applescript error
		
		-- build the pop up menu
		set thePopup to (current application's NSPopUpButton's alloc()'s initWithFrame:{{20, 20}, {0, 32}} pullsDown:true)
		tell thePopup
			its addItemsWithTitles:({""} & menuList) -- the menu needs a first empty item because it's a pull down 
			its setAction:"popupButtonAction:"
			set {theW, theH} to its |menu|()'s |size|() as list
			its setFrameSize:{theW, 0} -- fit the menu size to its content
			its setTarget:me
		end tell
		
		-- add the pop up menu to the window
		(thePanel's contentView()'s addSubview:thePopup)
		
		-- display the menu only by closing its container window 
		thePanel's setInitialFirstResponder:(thePanel's contentView())
		thePanel's makeKeyAndOrderFront:me
		thePopup's performClick:me
		thePanel's performClose:(missing value)
		
	on error errMess number errNum
		set my fail to {theMess:errMess, theNum:errNum}
	end try
end displayMenu

-- Perform an action when a menu item is chosen
on popupButtonAction:sender
	set selected to sender's titleOfSelectedItem as text
	if (sender's pullsDown as boolean) then -- for pull-down
		sender's setTitle:selected -- synchronizeTitleAndSelectedItem doesn't want to work
		sender's sizeToFit() -- sized according to the title
	end if
	set my outcome to selected
end popupButtonAction:

-- Get the list of effects by UI scripting 
on getEffects()
	activate application "Logic Pro X"
	tell application "System Events" to tell process "Logic Pro X"
		tell (window 1 whose title ends with "- Tracks")
			set theParent to a reference to (UI element 1 of UI element 1 of last group of list 1 of (group 1 whose description is "Inspector"))
			if not (exists theParent) then error "Parent UI element is missing" number -2700
			tell theParent
				set theEffects to {}
				set theCount to (count of groups)
				if theCount < 1 then error "No children found" number -2700
				repeat with theItem from 1 to theCount
					set counter to count of (value of (attribute "AXChildren" of group theItem))
					if counter = 3 then
						set beginning of theEffects to (description of group theItem) as string
					end if
				end repeat
			end tell
		end tell
	end tell
	
	return theEffects
end getEffects

Aha ok, got you now.
Sorry for not mentioning that, I reset accessibility permission but there was no change.

I tried this modified script and I’m still getting same output: missing value, and the behaviour is the same as I already explained.
Of course this is with keyboard maestro, from script editor it works with no issues.

However, I installed fast scripts and ran the script, and it worked perfectly.
Of course I firstly had to give it a permission, but after that it works with no issues.
I also tried it with few other cases where I’d need this custom context menu and it worked there too.
I didn’t save script as bundle then as normal script file “.scpt”, works perfectly.

Forgot to add that I already tried this script on my another machine and same behaviour. One machine is macbook pro 16 2019 version, and another one is mac mini with m2 pro chip, both running latest sonoma version.

I think you understand that KM is causing the issue.
Just to be sure: when you run the latest script in KM, the popup menu does not appear at all and you immediately get a missing value?

When you tried this last script, did you add any other statements?
Can you run your getEffects handler alone from KM and report if it gives the expected result?

Yeah seems like KM is causing this issue.
PopUp never appeared, I hear screen reader says osa scripting not responding, and I get that missing value from KM, nothing else.

getEffects() handler definitely works, already tried it from KM.
I just copied and pasted code you sent and tried it inside KM, didn’t make any modifycations.
I took same code and saved it as script, then tried that script with FS and it worked.
After it worked with fs, I then tried same with some other handlers and those examples worked with FS too.

Don’t know what else to try. I’ll gather all steps you guys recommended me and retry everything on my mac mini. I’ll report back if anything changes.

Don’t. It’s useless.
You said previously that my first script (the one who needs to click on OK button) was working. Can you confirm that it does when run from KM?

Otherwise, can you verify the KM preferences and confirm that all security settings are checked?

In preferences, under security it’s all checked except contacts.
I took that first code you sent and tried to run it without any modifycations. On first run, I got asked to give keyboard maestro a permission to run it, although the KM has permission. Anyway I reset it, but no luck, doesn’t work. In fact, nothing happens except screen reader saying osa scripting not responding when ran.

I tried that code in FS too, and it worked.

You’re right on checking everything on second machine, useless. Same behaviour.

OK. It’s confirmed: the problem comes from KM: the window is not created at all.

I used all the tricks I know. I’m out of ideas.
At least there’s FastScripts…

Maybe you should submit the issue to KM developers…
Sorry I can’t help you more!