Automating Split View on Mac

I’m trying to automate showing two apps side by side in Split View using AppleScript. I managed to make one app fill one side of the screen with the script below, but can’t figure out a way to select the other app to fill the remaining half. Anyone have any ideas?

tell application "Things3" to activate
tell application "System Events"
	tell process "Things"'s front window
		try
			set fsButton to a reference to (the first button whose subrole is "AXFullScreenButton")
			perform action "AXShowMenu" of fsButton
			set tileLeft to menu item "Tile Window to Left of Screen" of menu 1 of fsButton
			perform action "AXPress" of tileLeft
		on error errMsg
			return errMsg
		end try
	end tell
	delay 2
	tell application "Calendar" to activate
	tell process "Calendar"'s window 1 to perform action "AXRaise" -- doesn't work
end tell

I thought the app selection view would be similar to Mission Control, and tried to use scripts I found on Stack Overflow and Mac Power Users, but process “Dock” doesn’t have any “Mission Control” group even after entering into the app selection mode.

I’ve even tried to reverse-engineer the app selection part using Xcode Accessibility Inspector and Automator recording tool, but didn’t get a clue.

Would appreciate any help on this. I know you can get a similar side-by-side view by manually adjusting the windows bounds or even using Shortcuts, but I’m specifically interested in Split View.

Thanks @Fredrik71 . That might as well be the only feasible solution. Wanted to see if I could make Split View work to avoid window sizes being irreversibly changed.

@Fredrik71 thanks for the ideas. The third one is what I’m going for and I managed to get the green dot menu revealed and clicked with the script I posted. Sadly I’m stuck at that stage and couldn’t find a way to select the other app to fill the remaining screen with. I’d love to explore Objective-C option but my Cocoa/AppKit knowledge is quite limited. Do you have any pointers to the libraries I should look into?

benyn. I wasn’t aware this was possible. If I move Safari to the left side of the screen using the button menu item as you describe, the Safari window size and position are remembered. Do I not understand something or do you intend to use the window > revert command?

BTW, the following works but the window size and position are remembered:

tell application "Safari" to activate
tell application "System Events" to tell process "Safari"
	set frontmost to true -- probably unnesessary
	click menu item "Move Window to Left Side of Screen" of menu "Window" of menu bar 1
end tell

delay 1 -- a shorter value will probably work

tell application "Calendar" to activate
tell application "System Events" to tell process "Calendar"
	set frontmost to true -- probably unnecessary
	click menu item "Move Window to Right Side of Screen" of menu "Window" of menu bar 1
end tell

Apple’s explanation of Split View can be found here. However, on my Ventura computer, the process described by Apple doesn’t seem to work in step 3. (the second window doesn’t change size).

@peavine Thanks. Sorry if my comment wasn’t clear. Yes, the “Tile Window to Left of Screen” action a.k.a. the green dot is what I want because the window sizes and positions are remembered. I was trying to avoid setting window sizes and positions manually since the original window sizes and positions won’t be remembered unless I write them down in a file.

I tried your script. Did you mean to say the window sizes and positions are NOT remembered? I couldn’t get the original sizes and positions. Also, my Safari and Calendar happened to be on different displays when I ran the script, and Safari took the left half of the first display and Calendar took the right half of the second display. :thinking:

I wasn’t aware of Window > Revert command. I also don’t see “Move Window to Left/Right Side of Screen” if I click the Window menu, but it worked in the script. I only see “Tile Window to Left/Right of Screen” on my Mac running Ventura. Didn’t know there were hidden commands I could access via AppleScript.

Although I just tried the script below but Window > Revert didn’t work.

tell application "System Events" to tell process "Safari"
	click menu item "Revert" of menu "Window" of menu bar 1
end tell

And click menu item is definitely neater and faster than my convoluted perform action "AXShowMenu"/"AXPress" commands, but I’m still stuck at that stage where the windows are laid out as thumbnail button on the right side of the screen in Mission Control style.

tell application "Safari" to activate
tell application "System Events" to tell process "Safari"
	click menu item "Tile Window to Left of Screen" of menu "Window" of menu bar 1
end tell

delay 1 -- a shorter value will probably work

tell application "Calendar" to activate
tell application "System Events" to tell process "Calendar"
	click menu item "Tile Window to Right of Screen" of menu "Window" of menu bar 1
end tell

@Fredrik71 That might be the easiest, but is it possible to revert the window sizes back to what they were before?

@benyn. Ventura has a Desktop & Dock preference entitled “Displays have Separate Spaces.” I have this set off, which is the reason my computer shows “Move Window to Left Side of Screen” and “Move Window to Right Side of Screen.” After running my script, macOS will remember the tiled position and size of the apps–not their original position and size. I only have one monitor and don’t know how all of this works on a computer with two monitors.

To see how the Revert command works, disable the System Preference “Displays have Separate Spaces.” Then copy the following script to Script Editor and run several times in succession. This works with Ventura only.

tell application "System Events" to tell process "Script Editor"
	try
		click menu item "Move Window to Left Side of Screen" of menu "Window" of menu bar 1
	on error
		click menu item "Revert" of menu "Window" of menu bar 1
	end try
end tell

I spent some more time on this and better understand the issue raised by the OP. My testing is with one monitor on a Ventura computer.

I enabled the System Preference entitled “Displays have separate Spaces.” I then modified the OP’s script slightly and ran it with Script Editor (with no other apps running). After running the script, Safari is tiled in the left side of the display and there are large icons in the right side of the display for Calendar and Script Editor. If I click on the Calendar icon, split view is enabled with Safari in the left pane and Calendar in the right pane.

The OP’s question appears to be how to automate the very last step of clicking on the Calendar icon. The only answer I am aware of is to use GUI scripting to click on a specific location of the screen, but that’s very kludgey and error-prone. If it were me, I would simply delete the last line of the script and manually click on the Calendar icon.

Sorry for the wasted time :frowning:

tell application "Safari" to activate
delay 1
tell application "Calendar" to activate
delay 1
tell application "System Events"
	tell process "Safari" to tell window 1
		try
			set fsButton to a reference to (the first button whose subrole is "AXFullScreenButton")
			perform action "AXShowMenu" of fsButton
			set tileLeft to menu item "Tile Window to Left of Screen" of menu 1 of fsButton
			perform action "AXPress" of tileLeft
		on error errMsg
			return errMsg
		end try
	end tell
	tell process "Calendar" to tell front window to perform action "AXRaise" -- doesn't work
end tell

FWIW I would use the following script and manually click on the Calendar icon.

tell application "Safari" to activate
delay 1 -- try smaller values or use repeat loop to check existence
tell application "Calendar" to activate
delay 1 -- try smaller values or use repeat loop to check existence
tell application "System Events" to tell process "Safari"
	set frontmost to true
	click menu item "Tile Window to Left of Screen" of menu "Window" of menu bar 1
end tell
1 Like

@peavine Thank you so much for this! Yes, you got exactly what my problem was. Sorry I should’ve been clearer about my settings and all that.

Yes, your script using click menu item is much better than my original script. I was delighted to discover the “AXShowMenu” action, but there’s some weird delay happening there, which doesn’t happen with the click menu item approach. At least on my machine.

It’s so sad that the last click step doesn’t seem to be automatable. The script below shows that, at least with Mission Control, the miniaturized app window icons are actually buttons buried under process "Dock"'s group "Mission Control"'s group1's group 1.

tell application "Mission Control" to launch

delay 1

tell application "System Events"
	tell process "Dock"'s group "Mission Control"'s group 1's group 1
		set windowNames to {}
		repeat with theButton in buttons
			set windowName to theButton's name as text
			set end of windowNames to windowName
		end repeat
		try
			click button 2
		on error
			error "Try again with at least two open windows on your primary display"
		end try
	end tell
end tell
return windowNames

If you run the above script with at least two app windows open on your main display, you’ll see that the window that was eclipsed by the foremost window coming on top because button 2 was clicked in the script. I attached screenshots below as an example. Since the Mission Control icons look very similar to what I see on the right side of the display, I was hoping to find these hidden groups and buttons, but I couldn’t find anything under “Dock”.

Do you happen to know a way to survey all available ui elements, including those belonging to “invisible” processes?

benyn. Unfortunately my GUI scripting skills are rudimentary at best, and I don’t know how to do the above. There are a number of forum members with great skills in this area and hopefully one of them will be able to help you. Sorry I couldn’t be of assistance.

No worries. Appreciate your tremendous help.

1 Like

I stumbled onto this thread a few days ago as I was trying to accomplish the same. I managed to put something together, but it’s quite ugly. First of all this relies on a mouse click but at least it’s controlled by the approach. The main steps are:

  1. Create a new empty Desktop and switch focus to it.
  • This is accomplished by targeting the + button in the Spaces Bar

  1. Once the new Desktop is active, launch 2 “Terminal” applications
  • This guaranties that there are only 2 visible windows in the new Desktop

  1. Use click menu item "Tile Window to Left of Screen" of menu "Window" of menu bar 1 to bring the first instance to the left of the screen
  • At this stage there will only be one remaining window centered & visible on the right

  1. Click the second instance of “Terminal” by clicking directly in the middle of the right split view area
  2. Remove the original Desktop created since Split View will create another space and name it based on the applications selected for the split
-- This script requires having Mission Control shortcuts enabled:
--   ^UpArrow, ^1, ^2, etc.. (as many as you require)
-- This script requires cliclick (use brew install cliclick)

global countSpaces

openNewSpace()
create2TerminalsSplitScreen()
closeMissionControl()

-- FUNCTIONS
on openNewSpace()
	tell application "System Events"
		--start mission control
		do shell script "open -a 'Mission Control'"
		tell process "Dock"
			-- set countSpaces to count buttons (spaces)
			tell list 1 of group 2 of group 1 of group 1
				set countSpaces to count of buttons
			end tell
			
			-- create new space
			click button 1 of UI element "Spaces Bar" of UI element 1 of group 1
			delay 2
			-- switch to new space
			key code (18 + countSpaces) using {control down}
			click button (countSpaces + 1) of list 1 of group 2 of group 1 of group 1
		end tell
	end tell
end openNewSpace

on create2TerminalsSplitScreen()
	tell application "Terminal" to activate
	delay 1
	tell application "System Events" to tell process "Terminal"
		set frontmost to true
		click menu item "New Window" of menu "Shell" of menu bar 1
		delay 0.5
		click menu item "Cycle Through Windows" of menu "Window" of menu bar 1
		click menu item "Tile Window to Left of Screen" of menu "Window" of menu bar 1
		
		-- Find the x,y target to click for the right split view area
		tell application "Finder"
			set screen_resolution to bounds of window of desktop
			set screen_width to item 3 of screen_resolution
			set screen_height to item 4 of screen_resolution
			set x_target to screen_width * 3 / 4 as integer
			set y_target to screen_height / 2 as integer
		end tell
		
		-- click in the middle of the window placeholder to Right of Screen
		do shell script "/usr/local/bin/cliclick w:500 c:" & x_target & "," & y_target
	end tell
end create2TerminalsSplitScreen

on closeMissionControl()
	tell application "System Events" to tell process "Dock"
		perform action "AXRemoveDesktop" of button (countSpaces + 1) of list 1 of group 2 of group 1 of group 1
		delay 1
		key code 126 using {control down}
	end tell
end closeMissionControl

Demo: Automating Split View on Mac

1 Like

Pat. Thanks for the script and for the great explanation and YouTube video.

I was curious if System Events intead of cliclick could be used in the script:

-- delete this line
do shell script "/usr/local/bin/cliclick w:500 c:" & x_target & "," & y_target

-- insert these lines
delay 0.5
tell application "System Events" to click at {x_target, y_target}

After making the above changes, the script did not work as expected, so perhaps the cliclick utility has to be used.

EDIT: I found some posts in the Late Night Software forum, which indicated that System Event’s click command is no longer reliable. So, perhaps cliclick is required.

tell application "Mail"
	activate
end tell
delay 0.2
tell application "System Events" to click UI element "Mail" of list 1 of application process "Dock"
delay 0.2
tell application "System Events" to set frontmost of process "Mail" to true

delay 0.2
tell application "System Events"
	tell application process "Mail"
		tell (first window whose subrole is "AXStandardWindow")
			set {position, size} to {{0, 0}, {714, 1140}}
		end tell
	end tell
end tell

delay 0.4

tell application "Calendar"
	activate
end tell
delay 0.3
tell application "System Events" to click UI element "Calendar" of list 1 of application process "Dock"
delay 0.3
tell application "System Events" to set frontmost of process "Calendar" to true
delay 0.3

tell application "System Events"
	tell application process "Calendar"
		tell (first window whose subrole is "AXStandardWindow")
			set {position, size} to {{715, 0}, {895, 1140}}
		end tell
	end tell
end tell

After more research, I found a much easier way to do this and it eliminates all the dependencies! This method is much better because rather than creating a new Desktop/Space (slow) to guaranty the Terminal instances are the only visible windows, I instead Hide all the other windows and restore them after the split is complete. Also, I removed the need for cliclick and used a few classes from the “Quartz” framework instead.

use framework "Quartz"

property updateMouseCursorPosition : false
property buttonCount : 1 -- left mouse button number
property mouseButtonDown : true -- mouse button pressed?
set targetPoint to current application's CGPointZero
set visibleApps to list

on mouseLeftClick at aPoint
	application's CGPostMouseEvent(aPoint, updateMouseCursorPosition, buttonCount, mouseButtonDown)
	application's CGPostMouseEvent(aPoint, updateMouseCursorPosition, buttonCount, not mouseButtonDown)
end mouseLeftClick

tell application "Finder" to close every window
tell application "System Events" to set visibleApps to every process whose visible is true

tell application "System Events" to repeat with theApp in visibleApps
	set visible of theApp to false
end repeat

tell application "Terminal" to activate
delay 1
tell application "System Events" to tell process "Terminal"
	set frontmost to true
	click menu item "New Window" of menu "Shell" of menu bar 1
	delay 0.5
	click menu item "Cycle Through Windows" of menu "Window" of menu bar 1
	click menu item "Tile Window to Left of Screen" of menu "Window" of menu bar 1
	
	delay 0.5
	-- Find the x,y target to click for the right split view area
	tell application "Terminal"
		set {x1, y1, x2, y2} to bounds of window 1
		set {winWidth, winHeight} to the size of window 1
		set targetPoint to {(x2 + winWidth / 2) as integer, (y2 / 2) as integer}
	end tell
end tell

delay 0.5

-- perform left click to select the second Terminal window as the the Split View partner
mouseLeftClick at targetPoint

delay 0.5

tell application "System Events" to tell process "Dock"
	key code 126 using {control down} -- Hide Mission Control
end tell

--Restore the apps back to visible
tell application "System Events" to repeat with theApp in visibleApps
	set visible of theApp to true
end repeat

Pat. I’m not familiar with the Core Graphics framework and whether it can be used to move the cursor and click on a specified point on the screen. There is a thread (here) that contains reservations, although the following seems to work. I’ll spend some additional time on this later.

use framework "Foundation"
use framework "CoreGraphics"
use scripting additions

mouseLeftClick({28, 14}) -- click on Apple icon on my computer

on mouseLeftClick(aPoint)
	current application's CGPostMouseEvent(aPoint, true, 1, true)
	current application's CGPostMouseEvent(aPoint, true, 1, false)
end mouseLeftClick