Closing Display Text Windows With AppleScript

I have the below Apple Script that is part of a macro; specifically:

  1. The macro determines the X and Y co-ordinates of the Display Text window;
  2. The macro assigns the X and Y co-ordinates if the Display Text window to Local_DisplayTextWindowX and Local_DisplayTextWindowY respectively.
  3. The macro pauses for N seconds and the below Apple Script then closes the Display Text window.
set kmInst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
	set winX to (getvariable "Local_DisplayTextWindowX" instance kmInst) as real
	set winY to (getvariable "Local_DisplayTextWindowY" instance kmInst) as real
end tell
tell application "System Events"
	tell application process "Keyboard Maestro Engine"
		repeat with w in (every window whose name is "Keyboard Maestro - Display Text")
			set wPos to position of w
			if (item 1 of wPos) is (round winX rounding down) and (item 2 of wPos) is (round winY rounding down) then
				tell (first button of w whose role description is "close button")
					perform action "AXPress"
				end tell
				exit repeat
			end if
		end repeat
	end tell
end tell

The problem is that I have used the Apple Script in a number of different macros and in one case (so far) i) two Display Text windows are created at (nearly) the same time ii) the two display text windows have the same pause and iii) only one of the Display Text windows is being closed.

The theory that I am working on is that the last of the two Display Text windows co-ordinates are overwriting the first Display Text Windows co-ordinates in the Apple Script thus only closing one of the Display Text windows.

While I know that I can change the Pause and likely get both Display Text windows to close, I am hoping / looking for a more robust solution.

What can de done to / with the Apple Script to get both Display Text windows to close while continuing to i) use the same variable names (I.e., Local_DisplayTextWindowX and Local_DisplayTextWindowY) and ii) the same Apple Script in all case so that I do not have to remember to change / tweak anything when using this in different macros.

Would for example something like the below be better because there is only 1 call for the co-ordinates where Local_DisplayTextWindowCoOrdinates contains the X|Y co-ordinates:

set kmInst to system attribute "KMINSTANCE"

tell application "Keyboard Maestro Engine"
	-- Get your pipe variable: "X|Y"
	set windowCoords to getvariable "Local_DisplayTextWindowCoOrdinates" instance kmInst
end tell

set AppleScript's text item delimiters to "|"
set targetX to (text item 1 of windowCoords) as number
set targetY to (text item 2 of windowCoords) as number
set AppleScript's text item delimiters to ""

tell application "System Events"
	tell application process "Keyboard Maestro Engine"
		set allWindows to (every window whose name is "Keyboard Maestro - Display Text")
		
		repeat with w in allWindows
			set wPos to position of w
			-- Rounding the coordinates to the nearest whole number for Screen 2
			set curX to (round (item 1 of wPos))
			set curY to (round (item 2 of wPos))
			
			-- Compare whole numbers
			if (curX is (round targetX)) and (curY is (round targetY)) then
				try
					tell (first button of w whose role description is "close button")
						perform action "AXPress"
					end tell
					return "Success"
				end try
			end if
		end repeat
	end tell
end tell

Thank you.

The robust solution is to not do it like this!

You can save the ID of the dialog after you spawn it by using the %LastWindowID% Token. You can close an Engine window by ID:

Make a sub macro that takes the Engine window ID as a parameter and has a Pause and a Close Action, and if you want the main to continue during the pause then call the sub asynchronously (which is why you don't want to use a sub-routine).

If you must use an AppleScript you should still target the window by ID:

set inst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
	delay 10
	close window id ((getvariable "Local_theWindow" instance inst) as integer)
end tell

So much simpler, and far less prone to error.

1 Like

Ahaa, much better than the solution that I landed on which was:

  • Close the window,
  • If window closes then exit the Apple Script
  • If window does not close then try again using a random delay of between (1,2) seconds. The randomness was added to avoid colliding with other windows!

Yours is way better!

Thank you!!

Nigel, I have implemented your solution and it is tremendous, it works perfectly!

Thank you!!

Don't thank me -- thank @peternlewis for already providing functionality we never knew we needed until we did!

2 Likes

Agreed and indeed!

@peternlewis huge thanks!

1 Like

A quick follow up.

If there a Keyboard Maestro action / way to check whether a window is open using its Keyboard Maestro Window ID?

To clear -- we're talking about Engine windows here, not Editor windows.

The quick-and-dirty way is to "Try" and move it by 0 pixels -- the trick is to change the options of the "Move a Window" Action to "Failure Aborts Macro" (the default is to Notify but continue).

Demo -- try with and without the first Action enabled:

Engine Window Exists Demo.kmmacros (6.4 KB)

You may not even need to do this. If the question is "is this window open so, if so I should close it" then just close it anyway and turn off the Notification.

I say "quick-and-dirty" because some people consider this against the "true philosophy" of error-handling -- this isn't an error, it's a testable state. And you can do that via AppleScript, with the usual added expense of AS environment instantiation:

Engine Window Exists Demo -- AS Version.kmmacros (6.9 KB)

Appreciate, love the creativity

Agreed and I thought of doing that!

Appreciate this as well.

Once again, choices, choices, choice...I will pick one an run with it.

Huge thanks!