You are not logged in.
This script tiles apps on the active display. To test this script, open and run it in a script editor with at least one other visible app. Note should be made that:
* The user can change the tiling grid pattern by editing the windowSetting list and can change the buffer between apps by editing the wB variable.
* The user can exclude apps from being tiled by adding them to the doNotTile list.
* Other than those in the doNotTile list, all apps including those without a visible window are tiled.
* Some apps have minimum/maximum window sizes and may not tile as expected.
Applescript:
-- Revised 2022.05.22
use framework "AppKit"
use framework "Foundation"
use scripting additions
on main()
set {appCount, openApps} to getAppData()
set windowBounds to getWindowData(appCount)
tileApps(openApps, windowBounds)
end main
on getAppData()
set doNotTile to {} -- intended for use with apps that do not have a visible window
set theWorkspace to current application's NSWorkspace's sharedWorkspace()
set activeApp to theWorkspace's frontmostApplication()
set openApps to theWorkspace's runningApplications()
set thePredicates to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT localizedName IN %@" argumentArray:{doNotTile}
set openApps to (openApps's filteredArrayUsingPredicate:thePredicates)'s mutableCopy()
tell application "Finder" to set finderWindow to (exists Finder window 1)
if finderWindow = false then openApps's removeObjectAtIndex:0
set appCount to openApps's |count|()
if appCount < 2 or appCount > 9 then
display alert "An error has occurred" message "Less than 2 or more than 9 apps are open" as critical
error number -128
end if
if (openApps's containsObject:activeApp) as boolean = true then -- tile active app first
openApps's removeObject:activeApp
openApps's insertObject:activeApp atIndex:0
end if
set openApps to (openApps's valueForKey:"processIdentifier") as list
return {appCount, openApps}
end getAppData
on getWindowData(windowCount)
set wB to 5 -- window buffer
set windowSetting to {{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}, {2, 2, 3}, {2, 3, 3}, {3, 3, 3}}
set windowSetting to item (windowCount - 1) of windowSetting
set columnCount to (count windowSetting)
set {x1, y1, x2, y2} to visibleDisplayBounds()
set wFH to ((y2 - y1) - (wB * 2)) div 1 -- window heights
set wHH to ((y2 - y1) - (wB * 3)) div 2
set wTH to ((y2 - y1) - (wB * 4)) div 3
if columnCount = 2 then -- column widths
set wW to ((x2 - x1) - (wB * 3)) div 2
else
set wW to ((x2 - x1) - (wB * 4)) div 3
end if
set windowBounds to {}
set {x, y} to {x1, y1}
repeat with i from 1 to columnCount -- loop through columns
repeat with j from 1 to item i of windowSetting -- loop through windows in a column
if item i of windowSetting = 1 then
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wW))), (y + (j * wB) + ((j - 1) * wFH))}, {wW, wFH}}
else if item i of windowSetting = 2 then
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wW))), (y + (j * wB) + ((j - 1) * wHH))}, {wW, wHH}}
else
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wW))), (y + (j * wB) + ((j - 1) * wTH))}, {wW, wTH}}
end if
end repeat
end repeat
return windowBounds
end getWindowData
on tileApps(openApps, windowBounds)
tell application "System Events"
repeat with i from (count openApps) to 1 by -1
tell (first process whose unix id is (item i of openApps)) to tell every window
set position to item 1 of item i of windowBounds
set size to item 2 of item i of windowBounds
end tell
end repeat
end tell
end tileApps
on visibleDisplayBounds()
set theScreen to current application's NSScreen's mainScreen()
set {{aF, bF}, {cF, dF}} to theScreen's frame()
set {{aV, bV}, {cV, dV}} to theScreen's visibleFrame()
return {aV as integer, (dF - bV - dV) as integer, (aV + cV) as integer, (dF - bV) as integer}
end visibleDisplayBounds
main()
Last edited by peavine (2022-05-23 09:33:19 am)
2018 Mac mini - macOS Monterey - Script Debugger 8
Offline
For windows that are "active" but do not show (e.g. Boinc, AAMPS, etc) on the desktop, what modifications would be necessary to include them or exclude them in the script?
Offline
For windows that are "active" but do not show (e.g. Boinc, AAMPS, etc) on the desktop, what modifications would be necessary to include them or exclude them in the script?
I've modified the script in post 1 to include a doNotTile list.
2018 Mac mini - macOS Monterey - Script Debugger 8
Offline
This script is similar to that above, differing in that it displays a dialog in which the user can select the apps to tile. It should be noted that:
* If one of the selected apps has the focus, that app will be tiled in the first position at the upper-left of the screen.
* The order of the apps shown in the dialog is the order in which they will be tiled.
* The script has a do-not-tile list but this is only intended for use with apps that do not have visible windows.
Applescript:
use framework "AppKit"
use framework "Foundation"
use scripting additions
on main()
set {appCount, openApps, tileAllApps} to getAppData()
set windowBounds to getWindowData(appCount)
tileApps(openApps, windowBounds, tileAllApps)
end main
on getAppData()
set doNotTile to {} -- intended for use with apps that do not have a visible window
set theWorkspace to current application's NSWorkspace's sharedWorkspace()
set activeApp to theWorkspace's frontmostApplication()
set openApps to theWorkspace's runningApplications()
set thePredicates to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT localizedName IN %@" argumentArray:{doNotTile}
set openApps to (openApps's filteredArrayUsingPredicate:thePredicates)'s mutableCopy()
tell application "Finder" to set finderWindow to (exists Finder window 1)
if finderWindow = false then openApps's removeObjectAtIndex:0
set appCountOne to openApps's |count|()
if appCountOne < 2 or appCountOne > 9 then errorAlert("Less than 2 or more than 9 apps were found")
if (openApps's containsObject:activeApp) as boolean = true then
openApps's removeObject:activeApp
openApps's insertObject:activeApp atIndex:0
end if
set dialogList to (openApps's valueForKey:"localizedName") as list
set selectedApps to (choose from list dialogList with prompt "Select two or more apps to tile:" default items (item 1 of dialogList) with multiple selections allowed) -- delete item 1 of to preselect all apps
if selectedApps = false then error number -128
set thePredicate to current application's NSPredicate's predicateWithFormat:"localizedName IN %@" argumentArray:{selectedApps}
set openApps to ((openApps's filteredArrayUsingPredicate:thePredicate)'s valueForKey:"processIdentifier")
set appCountTwo to openApps's |count|()
if appCountTwo < 2 or appCountTwo > 9 then errorAlert("One or more than 9 apps were selected")
set tileAllApps to appCountOne = appCountTwo
set openApps to openApps as list
return {appCountTwo, openApps, tileAllApps}
end getAppData
on getWindowData(windowCount)
set wB to 5 -- window buffer
set windowSetting to {{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}, {2, 2, 3}, {2, 3, 3}, {3, 3, 3}}
set windowSetting to item (windowCount - 1) of windowSetting
set columnCount to (count windowSetting)
set {x1, y1, x2, y2} to getDisplayBounds() -- usable display bounds
set wFH to ((y2 - y1) - (wB * 2)) div 1 -- window heights
set wHH to ((y2 - y1) - (wB * 3)) div 2
set wTH to ((y2 - y1) - (wB * 4)) div 3
if columnCount = 2 then -- column widths
set wW to ((x2 - x1) - (wB * 3)) div 2
else
set wW to ((x2 - x1) - (wB * 4)) div 3
end if
set windowBounds to {}
set {x, y} to {x1, y1}
repeat with i from 1 to columnCount -- loop through columns
repeat with j from 1 to item i of windowSetting -- loop through windows in a column
if item i of windowSetting = 1 then
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wW))), (y + (j * wB) + ((j - 1) * wFH))}, {wW, wFH}}
else if item i of windowSetting = 2 then
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wW))), (y + (j * wB) + ((j - 1) * wHH))}, {wW, wHH}}
else
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wW))), (y + (j * wB) + ((j - 1) * wTH))}, {wW, wTH}}
end if
end repeat
end repeat
return windowBounds
end getWindowData
on tileApps(openApps, windowBounds, tileAllApps)
tell application "System Events"
repeat with i from (count openApps) to 1 by -1
tell (first process whose unix id is (item i of openApps))
tell every window
set position to item 1 of item i of windowBounds
set size to item 2 of item i of windowBounds
end tell
if tileAllApps = false then
set frontmost to true
delay 0.1
end if
end tell
end repeat
end tell
end tileApps
on getDisplayBounds()
set theScreen to current application's NSScreen's mainScreen()
set {{aF, bF}, {cF, dF}} to theScreen's frame()
set {{aV, bV}, {cV, dV}} to theScreen's visibleFrame()
return {aV as integer, (dF - bV - dV) as integer, (aV + cV) as integer, (dF - bV) as integer}
end getDisplayBounds
on errorAlert(dialogMessage)
display alert "An error has occurred" message dialogMessage as critical
error number -128
end errorAlert
main()
2018 Mac mini - macOS Monterey - Script Debugger 8
Offline
Just as a side note, for those looking for a simpler solution, recent version of macOS have the ability to tile windows to the left and right side of the screen. And, for those running Monterey, the Shortcuts app includes "Get Organized" shortcuts that will tile apps. After trying these, I decided to use the second script above, but in the end this is just a matter of personal preference.
Last edited by peavine (2022-02-01 07:41:34 am)
2018 Mac mini - macOS Monterey - Script Debugger 8
Offline
This script differs from that in post 4 above in that the user can set the width of a window column as a percentage of the usable display width. To simplify somewhat, the script only supports a tiling grid two columns wide and three rows high. All of the user settings are contained in the first few lines of the getWindowBounds and getAppData handlers.
Applescript:
-- Revised 2022.02.06
use framework "AppKit"
use framework "Foundation"
use scripting additions
on main()
set {appCount, openApps, tileAllApps} to getAppData()
set windowBounds to getWindowBounds(appCount)
tileApps(openApps, tileAllApps, windowBounds)
end main
on getAppData()
set doNotTile to {} -- intended for use with apps that do not have a visible window
set theWorkspace to current application's NSWorkspace's sharedWorkspace()
set activeApp to theWorkspace's frontmostApplication()
set openApps to theWorkspace's runningApplications()
set thePredicates to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT localizedName IN %@" argumentArray:{doNotTile}
set openApps to (openApps's filteredArrayUsingPredicate:thePredicates)'s mutableCopy()
tell application "Finder" to set finderWindow to (exists Finder window 1)
if finderWindow = false then openApps's removeObjectAtIndex:0
set appCountOne to openApps's |count|()
if appCountOne < 2 then errorAlert("One or no active apps were found")
if (openApps's containsObject:activeApp) as boolean = true then
openApps's removeObject:activeApp
openApps's insertObject:activeApp atIndex:0
end if
set dialogList to (openApps's valueForKey:"localizedName") as list
set selectedApps to (choose from list dialogList with prompt "Select two or more apps to tile:" default items dialogList with multiple selections allowed)
if selectedApps = false then error number -128
set thePredicate to current application's NSPredicate's predicateWithFormat:"localizedName IN %@" argumentArray:{selectedApps}
set openApps to ((openApps's filteredArrayUsingPredicate:thePredicate)'s valueForKey:"processIdentifier")
set appCountTwo to openApps's |count|()
if appCountTwo < 2 or appCountTwo > 6 then errorAlert("One or more than 6 apps were selected")
set tileAllApps to appCountOne = appCountTwo
set openApps to openApps as list
return {appCountTwo, openApps, tileAllApps}
end getAppData
on getWindowBounds(windowCount)
set wB to 5 -- window buffer
set windowSetting to {{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}} -- tiling grid for 2 thru 6 apps
set windowRatio to {55, 55, 50, 55, 50} -- column width percent for 2 thru 6 apps
set windowSetting to item (windowCount - 1) of windowSetting
set windowRatio to item (windowCount - 1) of windowRatio
set {x1, y1, x2, y2} to getDisplayBounds() -- usable display bounds
set wFH to ((y2 - y1) - (wB * 2)) div 1 -- window heights
set wHH to ((y2 - y1) - (wB * 3)) div 2
set wTH to ((y2 - y1) - (wB * 4)) div 3
set wLW to ((x2 - x1) - (wB * 3)) * windowRatio div 100 -- window widths
set wRW to ((x2 - x1) - (wB * 3)) * (100 - windowRatio) div 100
set windowBounds to {}
set {x, y} to {x1, y1}
repeat with i from 1 to 2 -- loop through columns
repeat with j from 1 to item i of windowSetting -- loop through windows in a column
set columnWindowCount to item i of windowSetting
if i = 1 then
if columnWindowCount = 1 then
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wLW))), (y + (j * wB) + ((j - 1) * wFH))}, {wLW, wFH}}
else if columnWindowCount = 2 then
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wLW))), (y + (j * wB) + ((j - 1) * wHH))}, {wLW, wHH}}
else
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wLW))), (y + (j * wB) + ((j - 1) * wTH))}, {wLW, wTH}}
end if
else
if columnWindowCount = 1 then
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wLW))), (y + (j * wB) + ((j - 1) * wFH))}, {wRW, wFH}}
else if columnWindowCount = 2 then
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wLW))), (y + (j * wB) + ((j - 1) * wHH))}, {wRW, wHH}}
else
set end of windowBounds to {{(x + (i * wB) + ((i - 1) * (wLW))), (y + (j * wB) + ((j - 1) * wTH))}, {wRW, wTH}}
end if
end if
end repeat
end repeat
return windowBounds
end getWindowBounds
on tileApps(openApps, tileAllApps, windowBounds)
tell application "System Events"
repeat with i from (count openApps) to 1 by -1
tell (first process whose unix id is (item i of openApps))
tell every window
set position to item 1 of item i of windowBounds
set size to item 2 of item i of windowBounds
end tell
if tileAllApps = false then
set frontmost to true
delay 0.1 -- test different values
end if
end tell
end repeat
end tell
end tileApps
on getDisplayBounds()
set theScreen to current application's NSScreen's mainScreen()
set {{aF, bF}, {cF, dF}} to theScreen's frame()
set {{aV, bV}, {cV, dV}} to theScreen's visibleFrame()
return {aV as integer, (dF - bV - dV) as integer, (aV + cV) as integer, (dF - bV) as integer}
end getDisplayBounds
on errorAlert(dialogMessage)
display alert "An error has occurred" message dialogMessage as critical
error number -128
end errorAlert
main()
The following script is similar to that above, the primary difference being that apps not selected by the user in the dialog are hidden rather than placed behind the tiled apps.
Applescript:
use framework "AppKit"
use framework "Foundation"
use scripting additions
on main()
set {appCount, openApps, skipApps} to getAppData()
set windowBounds to getWindowBounds(appCount)
tileApps(openApps, windowBounds, skipApps)
end main
on getAppData()
set doNotTile to {} -- intended for use with apps that do not have a visible window
set theWorkspace to current application's NSWorkspace's sharedWorkspace()
set activeApp to theWorkspace's frontmostApplication()
set openApps to theWorkspace's runningApplications()
set thePredicates to current application's NSPredicate's predicateWithFormat:"activationPolicy == 0 AND NOT localizedName IN %@" argumentArray:{doNotTile}
set openApps to (openApps's filteredArrayUsingPredicate:thePredicates)'s mutableCopy()
tell application "Finder"
if (exists Finder window 1) then
set finderWindow to true
else if visible = false then
set finderWindow to true
else
set finderWindow to false
end if
end tell
if finderWindow = false then openApps's removeObjectAtIndex:0
if (openApps's |count|()) < 2 then errorAlert("One or no active apps were found")
if (openApps's containsObject:activeApp) as boolean = true then
openApps's removeObject:activeApp
openApps's insertObject:activeApp atIndex:0
end if
set dialogList to (openApps's valueForKey:"localizedName") as list
set selectedApps to (choose from list dialogList with prompt "Select two or more apps to tile:" default items dialogList with multiple selections allowed)
if selectedApps = false then error number -128
set appCount to (count selectedApps)
if appCount < 2 or appCount > 6 then errorAlert("One or more than 6 apps were selected")
set thePredicate to current application's NSPredicate's predicateWithFormat:"NOT localizedName IN %@" argumentArray:{selectedApps}
set skipApps to ((openApps's filteredArrayUsingPredicate:thePredicate)'s valueForKey:"processIdentifier")
set thePredicate to current application's NSPredicate's predicateWithFormat:"localizedName IN %@" argumentArray:{selectedApps}
set openApps to ((openApps's filteredArrayUsingPredicate:thePredicate)'s valueForKey:"processIdentifier")
return {appCount, openApps as list, skipApps as list}
end getAppData
on getWindowBounds(windowCount)
set wB to 5 -- window buffer
set windowSetting to {{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}} -- tiling grid for 2 thru 6 apps
set windowRatio to {55, 55, 50, 55, 50} -- percent column width for 2 thru 6 apps
set windowSetting to item (windowCount - 1) of windowSetting
set windowRatio to item (windowCount - 1) of windowRatio
set {x1, y1, x2, y2} to getDisplayBounds() -- usable display bounds
set wFH to ((y2 - y1) - (wB * 2)) div 1 -- window heights
set wHH to ((y2 - y1) - (wB * 3)) div 2
set wTH to ((y2 - y1) - (wB * 4)) div 3
set wLW to ((x2 - x1) - (wB * 3)) * windowRatio div 100 -- window widths
set wRW to ((x2 - x1) - (wB * 3)) * (100 - windowRatio) div 100
set windowBounds to {}
repeat with i from 1 to 2 -- loop through columns
repeat with j from 1 to item i of windowSetting -- loop through windows in a column
set columnWindowCount to item i of windowSetting
if i = 1 then
if columnWindowCount = 1 then
set end of windowBounds to {{(x1 + wB), (y1 + wB)}, {wLW, wFH}}
else if columnWindowCount = 2 then
set end of windowBounds to {{(x1 + wB), (y1 + (j * wB) + ((j - 1) * wHH))}, {wLW, wHH}}
else
set end of windowBounds to {{(x1 + wB), (y1 + (j * wB) + ((j - 1) * wTH))}, {wLW, wTH}}
end if
else
if columnWindowCount = 1 then
set end of windowBounds to {{(x1 + (i * wB) + ((i - 1) * (wLW))), (y1 + wB)}, {wRW, wFH}}
else if columnWindowCount = 2 then
set end of windowBounds to {{(x1 + (i * wB) + ((i - 1) * (wLW))), (y1 + (j * wB) + ((j - 1) * wHH))}, {wRW, wHH}}
else
set end of windowBounds to {{(x1 + (i * wB) + ((i - 1) * (wLW))), (y1 + (j * wB) + ((j - 1) * wTH))}, {wRW, wTH}}
end if
end if
end repeat
end repeat
return windowBounds
end getWindowBounds
on tileApps(openApps, windowBounds, skipApps)
tell application "System Events"
repeat with i from 1 to (count skipApps)
tell (first process whose unix id is (item i of skipApps))
set visible to false
delay 0.1
end tell
end repeat
repeat with i from (count openApps) to 1 by -1
tell (first process whose unix id is (item i of openApps))
tell every window
set position to item 1 of item i of windowBounds
set size to item 2 of item i of windowBounds
end tell
if (visible = false) then set visible to true
end tell
end repeat
end tell
end tileApps
on getDisplayBounds()
set theScreen to current application's NSScreen's mainScreen()
set {{aF, bF}, {cF, dF}} to theScreen's frame()
set {{aV, bV}, {cV, dV}} to theScreen's visibleFrame()
return {aV as integer, (dF - bV - dV) as integer, (aV + cV) as integer, (dF - bV) as integer}
end getDisplayBounds
on errorAlert(dialogMessage)
display alert "An error has occurred" message dialogMessage as critical
error number -128
end errorAlert
main()
Last edited by peavine (2022-02-10 01:23:26 pm)
2018 Mac mini - macOS Monterey - Script Debugger 8
Offline