29
\$\begingroup\$

Here's a little script that enters text into a document when activated.

tell application "TextEdit"
  activate
end tell
delay 0.2
tell application "System Events"
  keystroke "Hello World!"
  keystroke return
end tell

I stuck the delay in there because otherwise the first few keystrokes tend to trigger before the window is done activating, so I end up with "lo World!" in the document and "Hel" in whatever other window had focus when the script was activated. Is that the proper use of delay, or is there a better way to circumvent that problem?

\$\endgroup\$
  • 17
    \$\begingroup\$ Hello and welcome Abby and all to Code Review. Just as a head's up to all, these comments will be regularly monitored, and purged. Have (courteous) fun. \$\endgroup\$ – rolfl Dec 17 '14 at 22:10
  • 1
    \$\begingroup\$ You might find this blog useful. \$\endgroup\$ – Edward Dec 17 '14 at 22:26
  • 1
    \$\begingroup\$ Wiki Books has a code example that is similar here: en.wikibooks.org/wiki/AppleScript_Programming/System_Events This would seem to be an accepted practice due to the lack of custom events in Applescript (stackoverflow.com/questions/3127153/…). \$\endgroup\$ – xDaevax Dec 18 '14 at 16:41
  • 1
    \$\begingroup\$ Is your intention to insert "Hello World!" wherever the cursor happens to be? \$\endgroup\$ – 200_success Dec 18 '14 at 17:22
  • 3
    \$\begingroup\$ Despite being on permanent patrol for all other things Apple here on Code Review, and contrary to popular belief, AppleScript is outside my realm of expertise. With that said though, I believe this is our first AppleScript question? And I'm left wondering why we don't have an AppleScript FizzBuzz. Look out CodeReview... \$\endgroup\$ – nhgrif Dec 25 '14 at 1:32
10
+100
\$\begingroup\$

Rather than using System Events to generate keystrokes, consider using TextEdit itself to insert text.

tell application "TextEdit"
    activate
    tell first document to set its text to its text & "Hello World!\n"
end tell

There is a difference, though: this version always appends "Hello World!" to the end of the document, rather than wherever the cursor happens to be. (Unfortunately, TextEdit's AppleScript dictionary mentions nothing about cursors.)

A side benefit of this approach is that you don't have to grant permission to use Assistive Access to your script.

\$\endgroup\$
7
\$\begingroup\$

Assuming that you want to stick with the original plan to insert text wherever the cursor happens to be, you would need something more deterministic than an arbitrary delay.

This script covers all the scenarios that I can think of:

tell application "System Events"        
    -- In case TextEdit was already running and all windows were closed
    repeat until first window of application "TextEdit" exists
        tell application "TextEdit" to make new document at the front
        delay 0.001
    end repeat

    -- Ensure TextEdit can have focus
    repeat until process "TextEdit" is frontmost
        set frontmost of process "TextEdit" to true
        delay 0.001
    end repeat

    -- Give focus to the window
    set focused of first window of process "TextEdit" to true

    keystroke "Hello world\n"
end tell

In particular, the scenarios I tested include:

  • TextEdit was not running at all
  • TextEdit was running, but had no document windows at all
  • TextEdit was already running with one or more windows

To convince yourself that the technique is sound, try each of these scenarios with the loop bodies commented out, and you should see that the script will wait until the conditions are appropriate before continuing.

There is no guarantee, however, that the user won't deliberately try to induce a race condition while the keystrokes are being sent.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.