-- "Tile and toggle frontmost app window positions and sizes" script by PhilHints, updated April 2010.
-- This script is tailored for a screen size of 1280x800, hidden dock.
-- Modify _screenWidth and _screenHeight if your screen is not this size.
-- Change these if your screen is not 1280x800. My dock and desktop are hidden. Think about leaving spaces for those...
property _screenWidth : 1280
property _screenHeight : 800
-- Change these if you wish to tile more windows...
property _windowsLimit : 12
property _windowsTileNumbers : {5, 6, 7, 8, 9, 10, 11, 12}
-- This remembers the window sizes from last time (if the they were the same windows).
property _boundsBefore : {}
-- This remembers the window names from last time.
property _namesBefore : {}
global _count -- Count of all frontmost app windows on run.
global _namesNow -- Names of all frontmost app windows on run.
global _boundsNow -- Bounds of all frontmost app windows on run.
global _process -- Frontmost process for System Events.
global _idNow -- id of all frontmost app windows on run.
global _idQuickLook -- If Quick Look is open (doesn't have a window id)
property _idQuickLook : {}
global _boundsTiled -- Bounds of all frontmost app windows after first tiled.
global _frontMostApp -- Name of frontmost app.
property _detailsOriginal : {} -- Name,id,position,size of all frontmost app windows before first tiled.
property _idOriginal : {} -- id of all frontmost app windows before first tiled.
property _detailsTiled : {} -- Name,id,position,size of all frontmost app windows after first tiled.
property _sameId : false -- Check for same window ids as last run.
property _sameWindows : false -- Check for same window names as last run.
property _sameBounds : false -- If same windows, check for same window bounds as last run.
try
set _frontMostApp to displayed name of (info for (path to frontmost application))
if text -1 thru -4 of _frontMostApp = ".app" then
set _atd to AppleScript's text item delimiters
set AppleScript's text item delimiters to ".app"
try
set _frontMostApp to text item 1 of _frontMostApp
end try
set AppleScript's text item delimiters to _atd
end if
-- Using System Events, I only get the VISIBLE app windows in the current space.
tell application "System Events"
set _process to process _frontMostApp
tell _process to set _count to count of windows
if _count = 0 then return
tell _process to set _windowDescriptions to description of windows
if _frontMostApp = "Finder" then
-- Check for Quick Look, which has no window id…
if _windowDescriptions contains "Quick Look Panel" then set _idQuickLook to {99999999}
end if
end tell
-- I reckon System Events doesn't know the unique id number of the windows, but the frontmost app does.
tell application _frontMostApp
if _frontMostApp ≠ "Finder" then
set _idNow to id of (windows whose visible = true)
else
set _idNow to _idQuickLook & id of windows
end if
-- The windows in the current space seem to be first in the _idNow list…
copy (items 1 thru _count of _idNow) to _idNow
end tell
tell application "System Events"
tell _process
if _count ≤ _windowsLimit then
-- Window names on run (_namesNow).
set _namesNow to {}
repeat with i from 1 to _count
set _namesNow to _namesNow & name of item i of windows
end repeat
-- Bounds of all frontmost app windows on run (_boundsNow).
set _boundsNow to {}
repeat with i from 1 to _count
set _boundsNow to _boundsNow & {{position of window i} & {size of window i}}
end repeat
else
tell me
tell application _frontMostApp to display dialog "Too many windows, " & _windowsLimit & " is the maximum." with title "Tile and Toggle Frontmost App Windows" giving up after 4
end tell
return
end if
end tell
end tell
-- Check to see if _idNow is the same as _idOriginal even if windows are in a different order.
if (count of _idNow) = (count of _idOriginal) then
set _sameId to true
repeat with i from 1 to count of _idNow
if item i of _idNow is not in _idOriginal then
set _sameId to false
exit repeat
end if
end repeat
if _sameId = true then
set _namesNow to _namesBefore
-- Same windows, so check to see if _boundsNow is the same as _boundsBefore even if bounds are in a different order.
set _sameBounds to true
repeat with i from 1 to count of _boundsNow
if {item i of _boundsNow} is not in _boundsBefore then
set _sameBounds to false
exit repeat
end if
end repeat
if _sameBounds = true then
set _boundsNow to _boundsBefore
end if
end if
end if
-- There are 3 CONDITIONS (if/then blocks) that call the 3 handlers:
-- CONDITION 1
-- If the names or ids of the windows are **different** from last time, the script 'resets'.
-- The window bounds are remembered for the next run.
-- Then the windows are tiled.
if _namesNow ≠ _namesBefore or _sameId = false then
_windowRoutine1() -- At the end of the script.
tell application _frontMostApp to activate
return
end if
-- CONDITION 2
-- If the names of the windows are the **same** as last time, and if the windows **are** at their original bounds, the script tiles the windows.
if _namesNow = _namesBefore and _boundsNow = _boundsBefore then
_windowRoutine2()
tell application _frontMostApp to activate
return
end if
-- CONDITION 3
-- If the names of the windows are the **same** as last time, and if the windows are **not** at their original bounds, the script puts the windows back to their original bounds.
if _namesNow = _namesBefore and _boundsNow ≠ _boundsBefore then
_windowRoutine3()
tell application _frontMostApp to activate
return
end if
on error a number b
say a
end try
on _windowRoutine2()
tell application "System Events"
tell _process
-- if the names of the windows are the same, System Events doesn't seem to know which one to move.
-- I use the window id number from the app to tell System Events which window to move.
repeat with i from 1 to _count
repeat with j from 1 to count of _detailsTiled
item i of _idNow = (item 2 of item j of _detailsTiled)
if result is true then
set position of window i to (item 3 of item j of _detailsTiled)
set size of window i to (item 4 of item j of _detailsTiled)
exit repeat
end if
end repeat
end repeat
end tell
end tell
end _windowRoutine2
on _windowRoutine3()
tell application "System Events"
tell _process
-- if the names of the windows are the same, System Events doesn't seem to know which one to move.
-- I use the window id number from the app to tell System Events which window to move.
repeat with i from 1 to _count
repeat with j from 1 to count of _detailsOriginal
item i of _idNow = (item 2 of item j of _detailsOriginal)
if result is true then
set position of window i to (item 3 of item j of _detailsOriginal)
set size of window i to (item 4 of item j of _detailsOriginal)
exit repeat
end if
end repeat
end repeat
end tell
end tell
end _windowRoutine3
on _windowRoutine1()
tell application _frontMostApp
if _frontMostApp ≠ "Finder" then
set _idOriginal to id of (windows whose visible = true)
else
set _idOriginal to _idQuickLook & id of windows
end if
copy (items 1 thru _count of _idOriginal) to _idOriginal
end tell
tell application "System Events"
tell _process
copy contents of _boundsNow to _detailsOriginal
-- Group window names, ids, positions and sizes together.
repeat with i from 1 to _count
set beginning of item i of _detailsOriginal to item i of _idOriginal
set beginning of item i of _detailsOriginal to item i of _namesNow
end repeat
-- Remember original window bounds.
set _boundsBefore to {}
repeat with i from 1 to _count
set _boundsBefore to _boundsNow
end repeat
end tell
--Set _namesBefore for the next run of the script.
set _namesBefore to _namesNow
-- The window sizes are my personal preferences. I'll explain some of the reasoning...
-- Makes an almost screen size window, so files can still be dragged behind it.
if _count is 1 then
set position of window 1 of _process to {64, 22}
set size of window 1 of _process to {_screenWidth - 128, _screenHeight - 22}
end if
-- If there are 2 or 3 or 4 windows, it's easy to split the windows across the width of the screen while retaining decent size windows.
-- Change properties _screenWidth and _screenHeight for different size screens. (See top of script.)
if {2, 3, 4} contains _count then
repeat with i from 1 to _count
set position of window i of _process to {(_screenWidth / _count) * (i - 1), 22}
set size of window i of _process to {(_screenWidth / _count), (_screenHeight - 22)}
end repeat
end if
-- Windows 5-12 are split vertically too.
if _windowsTileNumbers contains _count then
repeat with i from 1 to (_count / 2 round rounding up)
set position of window i of _process to {(_screenWidth / (_count / 2 round rounding up)) * (i - 1), 22}
set size of window i of _process to {(_screenWidth / (_count / 2 round rounding up)), ((_screenHeight - 22) / 2)}
end repeat
repeat with i from (_count / 2 round rounding up) + 1 to _count
set position of window i of _process to {(_screenWidth / (_count / 2 round rounding up)) * (i - ((_count / 2 round rounding up) + 1)), ((_screenHeight - 22) / 2) + 22}
set size of window i of _process to {(_screenWidth / (_count / 2 round rounding up)), ((_screenHeight - 22) / 2)}
end repeat
end if
-- Remembers the new bounds of the windows once tiled (_boundsTiled).
tell _process
set _boundsTiled to {}
repeat with i from 1 to _count
set _boundsTiled to _boundsTiled & {{position of window i} & {size of window i}}
end repeat
copy contents of _boundsTiled to _detailsTiled
repeat with i from 1 to _count
set beginning of item i of _detailsTiled to item i of _idOriginal
set beginning of item i of _detailsTiled to item i of _namesNow
end repeat
end tell
end tell
end _windowRoutine1