Hacky way to get # windows for a program that doesn't have a dictionary?

I’m trying to close all open Notion windows. Notion doesn’t have a dictionary, so I’m 1) sending a cmd-W, 2) activating Finder, 3) activating Notion again to get the next window under focus, 4) repeat.

It’s obviously very hacky. Also I can’t determine when all the windows have been closed, so I just loop through 5 times.

Chrome/Brave doesn’t have great dictionary support, but Applescripts can pick up the number of windows:

tell application "Brave Browser"
	log (count of windows)
end tell

The same does not work in Notion:

tell application "Notion"
	log (count of windows)
end tell

-- output: Notion got an error: every window doesn’t understand the “count” message

Is there a better way to do this?

See if this works…

tell application "System Events"
   tell its process "Notion"
      count of windows
      
   end tell
end tell

You may need to set it’s frontmost to true

tell application "System Events"
   tell its process "Notion"
      set its frontmost to true
   end tell
end tell

(Note, I don’t have Notion installed but this works with other unscriptable apps).

If I recall you can also use System Events to close windows, as long as they don’t need saving.

Thanks for the reply! On my machine (m1 Monterey 12.1) it does require setting frontmost to true. However, count of windows is either 0 (if it’s actually zero) or 2 (if any windows are open)

Also if anyone else comes across this and is really getting into the weeds…

There needs to be a delay in between setting frontmost to true and the window count.

tell application "System Events"
	tell its process "Notion"
		set its frontmost to true
	end tell
end tell
delay 1
tell application "System Events"
	tell its process "Notion"
		log (count of windows)
		
	end tell
end tell

Actually… as long as the 0 window case is reliable (which it appears to be) that’s all I need for a loop-termination condition

You might be able to forego the loop and bringing forward the app like this:

to closeApplicationWindows(A as text)
		 tell application id "com.apple.systemevents" to tell (every process ¬
		 		 where (its bundle identifier = A) or its name = A) to click ¬
			 	 the value of attribute "AXCloseButton" in every window
end closeApplicationWindows

closeApplicationWindows("Notion")

You can, of course, substitute every with first, but that’s not necessary and using every allows System Events to complete it the command even if it ultimately ends up acting on an empty reference list.

As we all know System Events often likes to act before it thinks, looping may be unavoidable, but still optimisable:

to closeApplicationWindows(A as text)
		 tell application id "com.apple.systemevents" to tell (every process ¬
			 	where (its bundle identifier = A) or (its name = A)) to tell ¬
				(a reference to every window) to repeat while it exists
			 	click the value of attribute "AXCloseButton"
		 end repeat
end closeApplicationWindows

While this will cope with System Events’ impatience, it’s more permissive with than it needs to be. The following one seems to use the minimum number of iterations by forcing System Events to dereference the result of the click command in order to evaluate the comparison, which is the condition placed upon continuing the loop. It also results in the smoothest window closure, although this may be my subjective bias seeing what I expect to see:

to closeApplicationWindows(A as text)
		 tell application id "com.apple.systemevents" to tell (the first process ¬
			 	whose (bundle identifier = A) or (name = A)) to if exists then tell ¬
				(a reference to the attribute "AXCloseButton" of every window) to ¬
			 	repeat while (click the value)'s class = list
			 	end repeat
end closeApplicationWindows