I hate to tell you this, but the script isn't working for me. It's taking a hugely long time to run, minutes not seconds.
I took the above script and pasted it into an Execute AppleScript action in a new macro. I ran the macro. At the text-field prompt I typed "9" because I happen to know that there is a TextEdit window on my current desktop named "Untitled 9.rtfd". I got the Multicolored Beachball of Death. Repeatedly, even after giving System Events permission through Accessibility to control the computer.
I guessed that it could have something to do with AXRaise, maybe because I'm on Mojave. So I started researching AXRaise.
It turned out that I had let the beachball spin while I was researcing because after a few minutes I got the choice prompt, showing the one TextEdit window. I picked it and it popped to the front. So it's not AXRaise.
And BTW, will this method work to find windows on other desktops (workspaces, not just multiple display screens)?
Never test scripts in Keyboard Maestro, unless its environment is required by the script.
(For instance getting and setting non-global variables.)
Or – you know exactly what you're doing. Otherwise use Script Debugger.
You have nine million windows open – right? It'll take a while.
I just tested on my Mojave system
Have we not covered that ad nauseam? All bets are off when spaces are concerned. System Events' vision is limited – the only way AppleScript can see all app windows is when the app itself is AppleScriptable.
Try this with an error-handler and timeout:
Macro Image
with timeout of 300 seconds
try
--------------------------------------------------------
# Bring Named Window Forward.
--------------------------------------------------------
set searchString to text returned of (display dialog "Enter a Window Name:" default answer "")
set winNameList to {}
tell application "System Events"
repeat with theApp in (processes whose background only is false)
tell theApp
set appName to its name
set winList to name of (windows whose name contains searchString)
end tell
if length of winList > 0 then
repeat with winName in winList
set contents of winName to (contents of winName) & tab & " --» " & tab & appName
end repeat
set winNameList to winNameList & winList
end if
end repeat
end tell
if winNameList ≠ {} then
set theWindow to choose from list winNameList ¬
with title "Windows With Designated Name" with prompt ¬
"Pick One:" default items {item 1 of winNameList} ¬
without empty selection allowed
if theWindow ≠ false then
set theWindow to item 1 of theWindow
set AppleScript's text item delimiters to tab & " --» " & tab
set {theWindow, theApp} to (get text items of theWindow)
tell application "System Events"
tell process theApp
set frontmost to true
tell window theWindow
perform action "AXRaise"
end tell
end tell
end tell
end if
else
beep
end if
--------------------------------------------------------
on error errMsg number errNum
set errMsg to errMsg & linefeed & linefeed & "Num: " & errNum
if errNum ≠ -128 then
try
tell application (path to frontmost application as text) ¬
to set ddButton to button returned of ¬
(display dialog errMsg with title ¬
"ERROR!" buttons {"Copy Error Message", "Cancel", "OK"} ¬
default button "OK" giving up after 30)
if ddButton = "Copy Error Message" then set the clipboard to errMsg
end try
end if
end try
end timeout
If you're looking for sheer speed out of this baby you're SOL – AppleEvents are not well optimized...
Macro Name : Generic-Test 01
Search String : “Untitled”
# of Windows Found : 141
Time of Execution : 18.831947
Time of Execution includes dialog time for entering the search string and choosing the window to bring forward.
Sorry to Shanghai your evening with a seven-year-old script.
I modified my previous report to include that it eventually did complete. The nien million windows are not on this Desktop. This desktop has eight windows, including one Chrome window with 10 tabs. That doesn't seem unreasonable. Does it have to go through every running app?
Yes, but I keep hoping...
I had been assuming that KBM would let me open a window based on its title, and I hadn't realized that, using standard KBM, I would have to activate the appropriate app first. That was an unpleasant surprise. Trying to work around that lead me to this topic.
Would your script run significantly faster if it was limited to a given list of three or four apps?
Unfortunately you can't limit System Events to a specific desktop – but you can limit it to specific apps.
Change the apps to your preferred list, and run the script. See how long it takes.
set appList to {"BBEdit", "Script Debugger", "Google Chrome", "Finder"}
set winList to {}
tell application "System Events"
repeat with theApp in appList
set end of winList to windows of process theApp
end repeat
end tell
winList
Moving from the window references to something searchable is another story, but this is a start.
I really think you should just accept the limits of vanilla scripting and Keyboard Maestro and either use a dedicated utility – or start asking questions on the Script Debugger Forum about using the window manager with AppleScriptObjC.
That said – @ComplexPoint has updated his JavaScript above to activate the app of the found window.
You might want to play with this a bit:
JavaScript Code
(() => {
"use strict";
// Rob Trew @2023
// Ver 0.03
// Raise first window with a matching name
// (also raising its parent application)
const
sa = Object.assign(
Application.currentApplication(),
{includeStandardAdditions: true}
),
se = Application("System Events"),
o = ObjectSpecifier(),
subString = sa.displayDialog("Window name:", {
withTitle: "Activate window by name",
defaultAnswer: ""
})
.textReturned;
const
appWins = se.processes.whose({
_match: [o.backgroundOnly, false]
})()
.flatMap(proc => {
const
win = proc.windows.whose({
_match: [o.name, {_contains: subString}]
}).at(0);
return win.exists()
? [[proc, win]]
: [];
});
return 0 < appWins.length ? (() => {
const [proc, win] = appWins[0];
return (
proc.frontmost = true,
se.perform(win.actions.byName("AXRaise")),
`${proc.name()} :: ${win.name()}`
);
})() : "No matching window found";
})();
Anything is possible, but that would be a relatively big change, since there are many places that allow you to select an application and I aim for consistency. And from a UI standpoint, it's pretty ugly too, given where there is now a menu there needs to be a menu and room for a variable, actually probably a token field, and its associated popup menu, etc.