Set Variable to Selection Without Involving Clipboard

Here's the code I've been working on so far to capture the selected text in a generalised situation. Notes follow below.

use sys : application "System Events"

-- Use the following delay to choose an application window
-- and highlight some text.  Then ensure that the window remains
-- in focus until the script terminates.
delay 5

set P to the first process whose frontmost is true
set _W to a reference to the first window of P

set _U to a reference to ¬
	(UI elements of P whose ¬
		name of attributes contains "AXSelectedText" and ¬
		value of attribute "AXSelectedText" is not "" and ¬
		class of value of attribute "AXSelectedText" is not class)

tell sys to if (count _U) ≠ 0 then ¬
	return the value of ¬
		attribute "AXSelectedText" of ¬
		_U's contents's first item

set _U to a reference to UI elements of _W

tell sys to repeat while (_U exists)
	tell (a reference to ¬
		(_U whose ¬
			name of attributes contains "AXSelectedText" and ¬
			value of attribute "AXSelectedText" is not "" and ¬
			class of value of attribute "AXSelectedText" is not class)) ¬
		to if (count) ≠ 0 then return the value of ¬
		attribute "AXSelectedText" of its contents's first item
	
	set _U to a reference to (UI elements of _U)
end repeat

"No selection found."

System info: AppleScript version: "2.7", system version: "10.13.3"

Notes

The script remains a work in progress, so don't expect a robust piece of deployment-standard code. I apologise that it's not the most readable script in its present format.

There's a 5-second delay at the start of the script to allow a user to select an application window, highlight some text, and then allow the script to run to completion.

It hunts for selected text in the front window of the frontmost application, as I thought this was a fairly reasonable condition under which real-life situations would fall.

It returns the selected text (if found), or a message stating that no selected text was found. It is a simple tweak should you want the actual UI element that contains the selected text to be returned instead (which might be useful in situations where text would be inserted to replace that which was highlighted).

Limitations

  1. The first limitation is obvious: as my script relies on referencing the front window of a frontmost application for the root of its UI-tree traversal, it currently cannot identify applications whose processes don't register themselves as having focus, such as some menu bar applications that remain background only (iTerm, SnippetsLab, Alfred, Spotlight, ...). However, if the variable P is set to one of these specific processes, it successfully obtains any highlighted text. Therefore, a workaround to this problem is probably very achievable.

  2. It also won't traverse menu bar elements. My initial script did do this, and was able to return text highlighted in the search box of the Help menu. However, the time cost of searching the menus weighed against the probability that selected text would be found there made its sacrifice seem reasonable.

  3. It can't traverse the desktop, should a user decide to select a file and edit the filename, highlighting the text in the editing field that appears. The desktop falls under Finder's process, and lacks a window reference under System Events (although it has one under Finder's application inheritance tree). [FIXED]

  4. Selecting text on a Safari webpage (and, it seems, Chrome as well, although I work mostly with Safari) goes unnoticed. Upon inspection, it seems that the UI elements that make up the AXWebArea of a Safari document all hold their own, unique set of attributes, tailored to the HTML DOM. This means all the static text and text area objects have no AXSelectedText attribute. A solution to this is to do JavaScript in the front document: document.getSelection().toString(); However, the rest of Safari's UI is compliant, so highlighting text in the search bar, for instance, will be detected.

Feel free to feedback any other issues that crop up, together with how to reproduce them. Or, better yet, feel free to improve the code should anyone feel so inclined. I'm not pledging myself to get this script working seamlessly; this was more just to highlight the possibilities, and I think the more realistic solutions to the OP's original dilemma have been provided. But it's an interesting educational exercise.

Reading over these issues, #1-3 are actually a subset of the same problem, which stems from seeking out an active application window to traverse. So, a three-bird-one-stone fix might very well pop into my head next week.

3 Likes