Activate (Focus) the First Window (Of Any App) That Contains "Search String"

How useful would it be a macro where I can type "budget" in a prompt and it brings into focus the Budget.xls window.

KM already has this feature, but it only works for a Specific app. It would be very useful it it would work for any app.

1 Like

Not “any app” but “any running apps”

Maybe I’m blind, but I cannot find the Action you show in your image. I have searched in the KM Editor and in the KM wiki and don’t see it. In the KM Editor, what Category is this action in, and what is the exact name of it as it appears there?

It’s the “Manipulate a Window” action.

I would never have guessed, and although the options (operations) are listed in the KM Wiki, they seem easy to overlook (at least to me).
For everyone's benefit, here are the main options of the Manipulate a Window Action:

In my opinion it’s more convenient to manipulate windows with BetterTouchTool and gestures with magic mouse.

Hey Leonardo,

That's doable with AppleScript and System Events.

--------------------------------------------------------
# Edited 2023/03/08 01:22 CST
# 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
--------------------------------------------------------

Try it from the Script Editor.app.

If you install Shane Stanley's MyriadTables Library I could even make it halfway pretty.

-Chris

5 Likes

This is awesome, I can’t believe you took the time to do it Chris. Thank you so much, I hope others find this useful!

Very nice script, Chris. :sunglasses: Thanks for sharing.

That would be a real treat! :+1:

Just for fun and parallel reading, a very stripped-down JavaScript version which jumps straight into activating the first match found, without offering a menu of options:

(function () {
    'use strict';

    var a = Application.currentApplication(),
        se = Application("System Events"),
        sa = (a.includeStandardAdditions = true, a),
        o = ObjectSpecifier();

    var lstWins = [].concat.apply(
              [], se.processes.whose({
                _match: [o.backgroundOnly, false]
            })
            .windows.whose({
                _match: [o.name, {
                    _contains: sa.displayDialog("Window name:", {
                            withTitle: "Activate window by name",
                            defaultAnswer: '',

                        })
                        .textReturned
                }]
            })()
        ),
        oWin = lstWins.length ? lstWins[0] : undefined;

    if (oWin) {
        se.perform(oWin.actions.byName('AXRaise'));
    }
})();
3 Likes

Or a version which at least offers a simple menu for single or multiple selections:

Ver 03: updated to make app of raised window frontmost, and add app name to menu


// ver 0.4 brings single matches straight to the front
// ver 0.3 includes app name (before window name) in menu
// ver 0.2 makes app frontmost, as well as raising window
(function () {
    'use strict';

    function raiseWin(oWin, appSE) {
        (appSE || Application("System Events"))
        .perform(oWin.actions.byName('AXRaise'));
    
        oWin.attributes.byName('AXParent')
            .value()
            .frontmost = true;
    }

    var strTitle = "Activate windows by name",
        a = Application.currentApplication(),
        se = Application("System Events"),
        sa = (a.includeStandardAdditions = true, a),
        o = ObjectSpecifier();

    a.activate();

    var strText = sa.displayDialog("Part of window name:", {
            withTitle: strTitle,
            defaultAnswer: '',

        })
        .textReturned,
        lstWins = [].concat.apply(
          [], se.processes.whose({
                _match: [o.backgroundOnly, false]
            })
            .windows.whose({
                _match: [o.name, {
                    _contains: strText
              }]
            })()
        ),
        lngWins = lstWins.length;

    // VARIANT: OFFER MENU WHEN MATCHES ARE MULTIPLE
    if (lngWins > 0) {
        if (lngWins > 1) {
            var lstNames = lstWins.map(function (w) {
                    return w.attributes.byName('AXParent')
                        .value.name() + ' > ' + w.name();
                }),

                varChoice = sa.chooseFromList(lstNames, {
                    withTitle: strTitle,
                    withPrompt: "Matching '" + strText + "'",
                    defaultItems: lstNames[0],
                    multipleSelectionsAllowed: true
                });

            if (varChoice) {
                varChoice.map(function (s) {
                        return lstNames.indexOf(s)
                    })
                    .forEach(function (i) {
                        raiseWin(lstWins[i], se);
                    });
            }
        } else raiseWin(lstWins[0], se);
    }

})();
2 Likes

Thanks for sharing, Rob. :+1:

I really appreciate translations of AppleScript to JXA. They really help my learning of JXA.
I hope you won’t mind a few questions (if any) that I may have after reviewing and testing your JXA code.

1 Like

:open_mouth: This is heaps cool, I just found out about the “Execute Javascript for automation” action. Thanks for sharing!

Ver 0.2 updated (above) to make app of raised window frontmont

Thanks to Chris Stone for providing the AXParent attribute route from window to app

Manipulate a Window is richer than most other software tools by themselves! I’m still posting questions that get answers pointing to Manipulate a Window, and I’ve been using KM since QuicKeys toppled over years ago.

I’m trying to follow the script and it looks like KBM is hard-coded. Before I try running it, I want to be able to at least read through it, and I'm stumbling.

I think there are a couple of changes that need to me made:

  • comment out the line that sets winName to KBM
  • replace the KBM in the phrase "whose name contains ..." with winName. (I'm not sure what punctuation may be needed arouind winName.)

Is that correct? Is there anywhere else where the script is still in testing mode?

I seem to have flubbed that a bit. (Original fixed as well.)

-ccs

--------------------------------------------------------
# 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
--------------------------------------------------------

In terms of the OP original question, the current version of Manipulate a Window requires that the app of the window being manipulated either be the app that owns the frontmost window or the app must be chosen from a menu while the macro is being written.

@peternlewis, would it be possible to add an option to Manipulate a Window to allow the action to identify the app on the fly, like it does with "Frontmost", and to pick the app based on a variable containing a text string of the app name?

Thanks for the update. (Seven years?)

And counting... :sunglasses: