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.
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.
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
@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
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
@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.
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:
Create a new emptyDesktop and switch focus to it.
This is accomplished by targeting the + button in the Spaces Bar
Once the new Desktop is active, launch 2 “Terminal” applications
This guaranties that there are only 2 visible windows in the new Desktop
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
Click the second instance of “Terminal” by clicking directly in the middle of the right split view area
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
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