MacOS – How to make script out of focus in AppleScript

applescriptmacoswindow-manager

I have an Automator Service that is comprised of a single "Run AppleScript" action. The AppleScript basically consists of a series of dialogs.

In the middle of the script, the script relies on keystroking ⌘ command + C in order to get the selected rich text into the script as the clipboard. This copy command must fall in the middle of the script, as the user might want to paste the real clipboard into the initial dialog.

AppleScript dialogs in Services are by default pinned on top of all windows; you cannot change this.

AppleScript is only able to keystroke ⌘ command + C when the underlying app is in focus, which makes sense, considering that it is the underlying app — not the script dialog — that contains the selected text.

So, I'll put my question inelegantly: Is there a way to make the top part of the window immediately behind the Service dialog gray and thus make the top bar of the Service dialog white, while still not moving the script dialog out of view?

I cannot go by the title of the window or the app of the window, because these variables will vary. The constant is that the window that I want to bring into focus will always be immediately behind the Service dialog.

The following is an example of what I'm talking about:

-- Back up original clipboard contents:
set savedClipboard to the clipboard

set optionList to {"Uppercase case"}
set dialogBoxChoice to choose from list optionList with title "Change case of selection" with prompt "Choose a letter case:"

-- Copy selected text to clipboard:
tell application "System Events" to keystroke "c" using {command down}
delay 1 -- Without this, the clipboard may have stale data.
set theSelectedText to the clipboard

-- Makes the selected text all uppercase:
-- From: http://apple.stackexchange.com/a/171196/184907
set theModifiedSelectedText to (do shell script ("echo " & theSelectedText & " | tr a-z A-Z;"))

display dialog theModifiedSelectedText

-- Overwrite the old selection with the desired text:
set the clipboard to theModifiedSelectedText
tell application "System Events" to keystroke "v" using {command down}
delay 1 -- Without this delay, may restore clipboard before pasting.

-- Restore clipboard:
set the clipboard to savedClipboard

If you put the above code in a keyboard-shortcut-triggered Service, you will see that the script will fail to tell application "System Events" to keystroke "c" using {command down}; thus, theSelectedText will be identical to the savedClipboard every time.

Best Answer

This will do what I think you're looking for. I removed the 'choice of one' from the beginning as it didn't seem relevant to the workflow.

I'm using an activeApp variable to call the app forwards after the dialogs. If you use System Events to call forwards it will not restore the frontmost window.

By using tell me instead of tell application "System Events" except where necessary, you can avoid the complication of the modal dialog stealing focus & not returning to your frontmost window afterwards.

Adding & quoted form of to the shellscript will work in more cases. Some apps will error without it, including Automator itself.

The Automator service receives no input from any application
I've left in a lot of debug, in the form of dialogs, to observe the progress.

on run {input, parameters}
    tell me
        tell application "System Events" to set activeApp to name of first application process whose frontmost is true
        -- Back up original clipboard contents:
        set savedClipboard to the clipboard
        display dialog ("Original clip - " & (the clipboard))
        activate application activeApp
        tell application "System Events" to keystroke "c" using command down
        delay 1
        display dialog ("Copied clip - " & (the clipboard))
        set theSelectedText to the clipboard
        set theModifiedSelectedText to do shell script "echo " & quoted form of theSelectedText & " | tr a-z A-Z;"
        display dialog {"Modified clip - " & theModifiedSelectedText}
        activate application activeApp
        set the clipboard to theModifiedSelectedText
        delay 1
        tell application "System Events" to keystroke "v" using {command down}
        -- Restore clipboard:
        delay 1
        set the clipboard to savedClipboard
        display dialog ("Restored clip - " & (the clipboard))
    end tell
    set input to {}
    return input
end run