For a macro I'm writing, I needed a Prompt for User Input action that would have a variable number of items in it, depending on the particular data the macro was working with at the time. The prompt needed to be able to present these additional items, which can range from none to dozens.
One way to do this was posted by @DanThomas way back in 2016: MACRO: Dynamic Prompt for User Input. However, this requires a plug-in, and I still wasn't certain it would work for my situation.
Then, much more recently, @Nige_S posted a how-to about using XML and AppleScript to present a user prompt. It was this post that got me started on the road to success, though there were some bumps along the way.
I thought others might be interested in how this works, so I created a demo macro that presents a user prompt with a variable and random number of items in it each time it runs:
And here's the macro that does the magic, but if you're going to use this method in your macros, you'll want to read the how-to that follows, as well as the comments in the demo macro, as this is not an automatic process :).
Dynamic Prompt for User Input demo.kmmacros (47 KB)
The Details Behind the Macro
@Nige_S gave a great brief summary of how you set this up in the above-linked post—basically you need a group with a Prompt action and a return Action, then some XML and AppleScript. And while that's true, it's a pretty big leap from that to a working form with dynamically added content.
For me, it was many trials and errors later before I had it working as I wanted; if you want a more detailed explanation of the steps involved, read the following sections which cover setup in great detail.
Group setup and XML editing
-
Create a new group. In that group, add a Prompt for User Input action and a Return action. Set up the Prompt action with the static data that won't change from run to run. You can use variables as you normally would, make pop-up lists, etc. To make things easier when editing, include one additional field for each type of dynamic data you'll be including—if you need to add some unknown number of checkboxes, and some unknown number of menu pop-ups, add one of each of those types to the form.
ă…¤
Here's the form I used in the demo macro:
ă…¤
ă…¤
Don't bother renaming the group as I did here; it's not needed and just adds more XML to your code.
ㅤㅤ
It doesn't matter what you put in the Return action, as you'll be editing it. -
Make sure the group is enabled, then right-click on the group and select Copy as XML, and paste into a text editor. Delete the first four lines and the last two lines:
ă…¤
-
Find the section (or sections) with the placeholder for the dynamic data. In the demo macro, that looks like this:
ă…¤
ă…¤
Replace all of that with a Keyboard Maestro variable reference for the XML data you're going to insert:
ă…¤
Repeat this process for each dynamic section of the prompt (the demo macro has just one). -
Find the Return section of the XML:
ă…¤
Replace the<string>
section's contents with the variable that will hold your form's responses:
ă…¤
You can do any other edits here you'd like, including changing the text in your prompt. If you want line breaks, use %Return%
instead of actual line breaks or \n
characters (though those may also work).
That's all the editing you need to do. Copy your edited text.
The test macro
Once you're done editing, it's time to build a test macro that uses your edited XML, just to make sure you have that part working. Start by creating a new variable to hold the XML. Call it whatever you like; I used instance_theXML
in the demo macro.
Paste your edited XML text into the variable. Then create a new AppleScript action below the variable, with this AppleScript:
set kmInst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
set theCode to getvariable "instance_theXML" instance kmInst
end tell
tell application "Keyboard Maestro Engine"
return (do script theCode)
end tell
If you changed the name of the variable from instance_theXML
, update the AppleScript code to reflect the new name. Set the AppleScript action to save its results to a variable—this isn't important for the test, which simply shows the prompt works, but it's how you'll save the data returned by the real form.
Now run the macro; any variable values you included in the XML will be blank, of course, but the Prompt for User Input dialog should appear. If it appears, congratulations, you've built the core of a dynamic Prompt for User Input box. If it doesn't appear, there's something wrong with the XML. What that might be is hard to say, of course, but it may be simplest to just start again.
For use in real macros
For use in your real macro, exactly what you do will depend on what you're adding to the prompt. In this demo macro, I did probably the most complicated thing possible, as I added variables with dynamic names to the form. And as Keyboard Maestro only supports certain characters in variable names, that meant doing some text processing on each thing in the list, to make sure it was a valid variable name.
I have tried to comment the demo macro in detail, but here are some key things to remember if you're building your own version:
Variable returns from dynamic prompt
The dynamic prompt doesn't work like a normal prompt, at least relative to returning variables from the form. If your form uses global variables, they'll work. But local and instance variables will not, as they're not returned when the form is submitted. That's the reason for the Return action; the variable you return will be built to include the variables from the form.
Here's the return variable (local_theReturnString
) from the demo macro:
"MOVING ESTIMATE"
The user provided the following input:
Rooms: %%Variable%%zzDemo__Rooms in home%%
sqft: %%Variable%%local_Square footage%%
Items being moved [1 = Yes, 0 = No]
-----------------------------------
%Variable%local_itemListReturn%
For rooms and square footage, those variables are the variables used in the form itself. I used a local and a global for purposes of the demo: Both are returned in the return string, but at the end of the macro, a text box shows that only the global's value is readable as a straight variable.
Why are some variables double-percentage-wrapped and the last not? Because you need to have the actual text %Variable%local_varname%
in the return string, not the value of that variable. But if that's the case, why isn't `%local_itemListReturn% double-percentaged?
Because that variable is built as a list of strings that are structured to be Keyboard Maestro variables, i.e. Toaster: %Variable%local_Toaster%
. I want the local_itemListReturn
variable to be interpreted, so that it's replaced with my list of dynamic variable names.
Protect text for use in XML
If you're inserting dynamic text, and/or editing the text in your XML, you must use XLM-safe values for certain characters (< > " ' &). The easiest way to do this is to use a Filter action on your variables, set to Encode HTML Entities. If you're putting in raw text in the input form, you'll have to manually type &
instead of &
, "
instead of "
, etc.
Adding custom XML
When you edited the XML, you added a variable that will insert custom XML into the form. That's the dynamic part. In the demo macro, this variable is built here:
The comment shows the XML that's wrapped around each entry in the list, with $1 being the name of the variable for each item. There's also a disabled For Each version that's much easier to read, and lets you customize the XML on a per item basis, i.e. if item = toaster, then offer a pop-up menu of Two Slice|Four Slice
instead of a yes/no.
Processing the returned form data
In my real macro, I'm currently using a regular expression to parse local_theReturnString
out into separate variables. But @Nige_S has just shown me a much nicer way to do this via JSON variables, and I'll probably switch to that. (The demo macro doesn't do any processing; it stops as soon as the form is submitted.)
There's a lot of info here, and at the same time, probably not nearly enough info to answer certain questions. And I'm far from an expert at this, having just set it up for the first time ever a day or two ago. But hopefully the demo macro and this text can get you started with dynamic input prompts, if you have a need for such things.
I will do my best to answer questions if you have them—though be warned I still have lots of things I'm figuring out myself :).
-rob.