0

I am writing an AppleScript using Javascript (JXA) that uses Finders search bar to find files.

I want to move the focus from the search text field onto the file listing (so that arrow buttons and space for preview works).

I have tried a few things:

  • Clicking somewhere else in the window (ignored for some reason).
  • .click() the UI rows in the Finder hierarchy (ignored).
  • .select() the rows (they are selected, but greyed out as a secondary focus to the text field).
  • Setting AXFocused to false on the text field.
  • Setting AXFocused to true on the rows.
    let ta = process.windows[0].toolbars.at(0).groups.at(4).textFields.at(0);
    let attrs = ta.attributes();
    
    // How to set this attribute to false?
    for(const a of attrs) {
        console.log(a.name());
        if(a.name()==="AXFocused"){
            console.log(a.properties());
            a.setProperty("value", null);
            a.setProperty("value", undefined);
            a.setProperty("value", 0);
            a.setProperty("value", "0");
            a.setProperty("value", "false");
            a.setProperty("value", false);
            
            // Does not work - property is always true.
            console.log(a.properties());
        }
    }  

Can this be done? Is there a focus function that I am not aware of?

Examples:

This is how my current script opens the Finder window: enter image description here

This is what I am trying to achieve (move the focus from the text area to the list): enter image description here

zino
  • 1,222
  • 2
  • 17
  • 47
  • One work around is to open the Go To window and close it with the escape key. Is there a more direct way? – zino Jan 27 '22 at 02:06
  • 1
    1. Finder has a `selection` property, so you should be able to set the selected file directly via Finder scripting; no fragile GUI Scripting required. 2. JXA has…issues. I suggest you figure out first how to do it in AppleScript, which supports Apple events correctly. If it can’t be done in AS, it’s probably a limitation of the scriptable app (in this case System Events GUI Scripting suite). If you can do it in AS but not in JXA, then it’s probably a defect in JXA (in which case, you’re SOOL there and should stick to AS). – foo Jan 28 '22 at 05:01
  • Thanks @foo, I am using the selection API but the selected rows are greyed out (with the search text keeping the active focus). This means rows can be "selected" but not have the active selection. – zino Jan 29 '22 at 17:31
  • 1
    OK, I got you. You’re right, there’s no way on `Finder window` to access the displayed search results (the `target` property just returns garbage). Obviously Finder devs didn’t bother to upgrade its AS support when they added the in-window search control. So you will have to use GUI Scripting. And as I say you are best to figure out if/how it can be done in AppleScript. If AS can’t do it, JXA certainly can’t. And if AS can do it, you can try to translate it to JXA; and if you can’t do that then you know JXA is the problem and will just have to use AS. – foo Jan 29 '22 at 17:54

1 Answers1

0

If you attempt to set the AXFocusedUIElement attribute on the AXUIElement for the application to the AXUIElement you wish to focus, HIServices will return a code 0 — indicating that the operation should have worked.

Unfortunately, this does not seem to be the case.

The only successful workaround I've been able to implement has been cursor manipulation via the ObjC bridge:

/**
 * Left-click the mouse cursor at the given point.
 * @param param0 As [x, y], the point at which to click.
 * @param restore Return cursor to its original position after click?
 * @returns {void}
 */
function click([x, y], restore = true) {
  if (typeof $.CGPointMake != 'function') ObjC.import('Cocoa');

  const point = $.CGPointMake(x, y);
  const restoreTo = $.CGEventGetLocation($.CGEventCreate($()));

  const {
    kCGHIDEventTap: kTap,
    kCGEventMouseMoved: kMoved,
    kCGMouseButtonLeft: kLeft,
    kCGEventLeftMouseUp: kUp,
    kCGEventLeftMouseDown: kDown,
  } = $;

  const evtDown = $.CGEventCreateMouseEvent($(), kDown, point, kLeft);
  $.CGEventPost(kTap, evtDown);
  const evtUp = $.CGEventCreateMouseEvent($(), kUp, point, kLeft);
  $.CGEventPost($.kTap, evtUp);

  if (!restore) return;
  else delay(0.1);

  const evtRestore = $.CGEventCreateMouseEvent($(), kMoved, restoreTo, kLeft);
  $.CGEventPost(kTap, evtRestore);
}

This function takes the coordinates of a point on-screen and will click the cursor at that location — effectively giving focus to the UI element which is drawn there. The cursor will then return to its original position, unless the restore argument is set to false.

I tried using the CGEventPostToPID function so that the user wouldn't see any visible cursor movement, but that appears to be broken as of the latest version of Ventura.

stephancasas
  • 615
  • 3
  • 12