Keeping windows 22 pixels from screen edges

A 22 pixel border may sound like an unusual goal, but I just started using Edgies (oneriver.jp) which allows you to keep Stickies-like notes with tabs on the edges of the screen.

To keep the tabs from overlapping and obscuring windows and documents, I rewrote some scripts that I use frequently (like one that opens a tabbed Safari window in the left half of my screen) to maintain that border.

Then I started experimenting with all open windows. I handle Finder windows separately because I couldn’t work them into the section that deals with other applications without breaking the script. Some applications (Previews, for example, but I haven’t tried this with very many yet) don’t resond to the script, and I haven’t tried to script floating windows.

The script isn’t unduly resource intensive - run from the editor on a dozen windows it briefly (about 1 second) hit 6% - and I usually have only a few windows open at once, so I’m considering adding an “on idle” handler once I’m satisfied with it.

Any comments or suggestions would be appreciated.

Thanks,

j

tell application "Finder"
	set runningApps to name of processes whose visible is true and name is not "Finder"
	set {d1, d2, d3, d4} to (desktop's window's bounds)
end tell

--Non-Finder windows
repeat with anApp in runningApps
	
	tell application anApp
		try
			set everyWindow to (every window whose visible is true)
			
			repeat with theWindow in everyWindow
				
				set {b1, b2, b3, b4} to get bounds of theWindow
				
				if b1 < 22 then
					set b1 to 22
					set bounds of theWindow to {b1, b2, b3, b4}
				end if --b1 < 22 then
				if b2 < 44 then
					set b2 to 44
					set bounds of theWindow to {b1, b2, b3, b4}
				end if --b2 < 44 then
				if (d3 - b3) < 22 then
					set b3 to (d3 - 22)
					set bounds of theWindow to {b1, b2, b3, b4}
				end if --(d3 - b3) < 22 then
				if (d4 - b4) < 22 then
					set b4 to (d4 - 22)
					set bounds of theWindow to {b1, b2, b3, b4}
				end if --(d4 - b4) < 22 then
				
			end repeat --with theWindow in everyWindow
			
		end try
	end tell --application anApp
	
end repeat --with anApp in runningApps

--Finder windows
tell application "Finder"
	
	set {d1, d2, d3, d4} to (desktop's window's bounds)
	
	set everyWindow to every window whose visible is true
	
	repeat with theWindow in everyWindow
		
		set {b1, b2, b3, b4} to get bounds of theWindow
		
		if b1 < 22 then
			set b1 to 22
			set bounds of theWindow to {b1, b2, b3, b4}
		end if --b1 < 22 then
		if b2 < 66 then
			set b2 to 66
			set bounds of theWindow to {b1, b2, b3, b4}
		end if --b2 < 66 then
		if (d3 - b3) < 22 then
			set b3 to (d3 - 22)
			set bounds of theWindow to {b1, b2, b3, b4}
		end if --(d3 - b3) < 22 then
		if (d4 - b4) < 22 then
			set b4 to (d4 - 22)
			set bounds of theWindow to {b1, b2, b3, b4}
		end if --(d4 - b4) < 22 then
		
	end repeat --with theWindow in everyWindow
	
end tell --application "Finder"



Several comments come to mind, CapJ;

Apps are not uniform about ‘bounds’. Preview, for example uses ‘position’ for the Top Left corner of the window, and ‘size’ for the Bottom Right relative to the Top Left. I’ve seen this in other apps too.


tell application "System Events" to tell window 1 of process "Preview"
	set TL to position -- screen coordinates of top left of Preview window
	set BR to size -- coordinates of bottom right of window with respect to top left corner
end tell

Secondly, your script ‘screams’ for a handler since most of the Finder code repeats ‘OtherAp’ code. You could even distinguish among apps that don’t report ‘bounds’, returning ‘missing value’. They do report position and size.

Hi, capitalj.

I personally would prefer to keep windows the same size if possible, but of course it’s up to you in your script. :slight_smile:

I’ve been fooling around with your code a little this morning. The version below attempts to deal with unscriptable applications by using GUI Scripting. It works with everything I’ve tried so far except for Stickies, whose window sizes can’t be changed. (And that only matters if the window’s at the bottom or the right of the screen.) I see Adam’s mentioned some apps that use position and size instead of bounds. There’s some code in this section that might help with them.

The thinking about the window bounds has been moved into its own handler. The commented-out lines, when enabled, keep the window at its original size, if that’s possible within the specified margins. They also happen to solve the Stickies problem.

I hope this is of help/interest. :slight_smile:

on checkBounds(currentBounds, margins)
	set {b1, b2, b3, b4} to currentBounds
	set {m1, m2, m3, m4} to margins
	
	if (b1 < m1) then
		-- set b3 to m1 + (b3 - b1)
		-- if (b3 > m3) then set b3 to m3
		set b1 to m1
	end if
	if (b2 < m2) then
		-- set b4 to m2 + (b4 - b2)
		-- if (b4 > m4) then set b4 to m4
		set b2 to m2
	end if
	if (b3 > m3) then
		-- set b1 to m3 - (b3 - b1)
		-- if (b1 < m1) then set b1 to m1
		set b3 to m3
	end if
	if (b4 > m4) then
		-- set b2 to m4 - (b4 - b2)
		-- if (b2 < m2) then set b2 to m2
		set b4 to m4
	end if
	
	return {b1, b2, b3, b4}
end checkBounds

tell application "Finder"
	set runningApps to name of processes whose visible is true and name is not "Finder" and name is not "DragThing"
	set {d1, d2, d3, d4} to (desktop's window's bounds)
end tell

set margins to {d1 + 22, d2 + 44, d3 - 22, d4 - 22} -- The bounds within which the windows must stay.

--Non-Finder windows
repeat with anApp in runningApps
	
	try
		tell application anApp
			set everyWindow to (every window)
			
			repeat with theWindow in everyWindow
				set currentBounds to (get bounds of theWindow)
				
				set adjustedBounds to my checkBounds(currentBounds, margins)
				if (adjustedBounds is not currentBounds) then set bounds of theWindow to adjustedBounds
			end repeat --with theWindow in everyWindow
		end tell --application anApp
	on error -- Non-scriptable application.
		tell application "System Events"
			tell application process anApp
				set everyWindow to every window
				
				repeat with theWindow in everyWindow
					set {{p1, p2}, {s1, s2}} to theWindow's {position, size}
					set currentBounds to {p1, p2, p1 + s1, p2 + s2}
					
					set adjustedBounds to my checkBounds(currentBounds, margins)
					if (adjustedBounds is not currentBounds) then
						set {b1, b2, b3, b4} to adjustedBounds
						set theWindow's position to {b1, b2}
						set theWindow's size to {b3 - b1, b4 - b2}
					end if
				end repeat
			end tell
		end tell
	end try
	
end repeat --with anApp in runningApps

--Finder windows
tell application "Finder"
	set everyWindow to every window whose visible is true
	
	repeat with theWindow in everyWindow
		
		set currentBounds to bounds of theWindow
		
		set adjustedBounds to my checkBounds(currentBounds, margins)
		if (adjustedBounds is not currentBounds) then set bounds of theWindow to adjustedBounds
		
	end repeat --with theWindow in everyWindow
	
end tell --application "Finder"

I plan on answering the cry for handlers. I temporarily dealt with uncooperative apps by using “try.” The code you posted will be a big help - it might have taken a while for me figure it out.

I’ve never been consistent with window size - I do keep certain things uniform (Script Editor documents, for example) but other things are pretty random - partly because I use FolderGlance, partly because my 17" screen seems roomy enough most of the time, but I find myself reconsidering my approach.

Since my code will only resize windows, I think I’ll prefer the uncommented version of yours (only tried it commented so far.) The only immediate change I’d make to it is to correct one number. For Finder windows m2 should be (d2 + 66) and I need to experiment with other applications to see how much variation there will be.

Thank you both for your insight. I’ll get back to work.

j

Extremely slick, Nigel :smiley:

For some reason it failed with margins not defined (even though the variable was shown correctly in the SD4 value pane) in the on error portion of non-Finder windows the first two times I ran it in Script Debugger 4, but when I logged ‘everyWindow’ in that section to see what was troubling it, it worked, and continued to do so after I stopped logging, so I think SD4 may have been doing something I can’t now discover.

It’s perfectly happy with dual screens too, and if I shove a window outside either screen’s bounds it hauls it back in. It deals successfully with two pallets on screen belonging to CopyPaste (leaves them where they are), and with a floating window (iPulse) that is non-standard (visible as circular) - leaves it alone too. BTW - it worked perfectly in the Script Editor on the first go.

I adjusted the margins because I keep my dock on the left directly under the menu bar (not centered), and it’s set to stay the same width, grows down, has yellow intead of black indicators of running apps to show better against a solid blue desktop, and is transparent (just icons show), so now I can correct apps that refuse to anticipate that possibility and insist on left adjusting their windows to the left edge of my desktop under my dock. Great! Another script goes into my (NG) subset.

Ah, sorry, cj. I missed that.

Well. Thanks, Adam. But I’ve only provided some input for this particular one. It’s capitalj’s baby. :slight_smile:

but there is a strong Garvey resemlbance. :slight_smile:

I converted my script to bit by bit (I learn better that way) to incorporate Nigel’s code and after a few typos and failed experiments that occasionally sent windows flying off the screen, I’ve essentially put the same thing in a different order. I considered expanding the bottom margin, but I keep my dock hidden (more room for art and photography projects) and 22 pixels is enough so that I don’t accidentally pop it up.

I changed it so Finder windows at the top of the screen will drop below the margin, but had to put this in the Finder tell block

set {d1, d2, d3, d4} to (desktop's window's bounds)
	set finderMargins to {d1 + 22, d2 + 66, d3 - 22, d4 - 22}

or when saved as an application it errored because those variables were undefined.

It works on every application I’ve tested it with so far, and on some floating windows (Get Info) but not others (Force Quit Applications) but I didn’t, and have given up on, solving that - I don’t think it will be a problem anyway. I’m using it with a Butler shortcut while I play with it some more. I’ll add an "on idle " handler later, I think.

Thanks for the help.


tell application "Finder"
	
	set runningApps to name of processes whose visible is true and name is not "Finder" --and name is not "DragThing" (commented because I don't have it)
	set {d1, d2, d3, d4} to (desktop's window's bounds)
	set margins to {d1 + 22, d2 + 44, d3 - 22, d4 - 22} -- The bounds within which the windows must stay. Finder margins are variables set separately in the Finder tell block    	
end tell

--Adjust non-Finder windows
repeat with anApp in runningApps
	
	try
		tell application anApp
			set everyWindow to (every window)
			
			repeat with theWindow in everyWindow
				set currentBounds to (get bounds of theWindow)
				
				set adjustedBounds to my checkBounds(currentBounds, margins)
				if (adjustedBounds is not currentBounds) then set bounds of theWindow to adjustedBounds
			end repeat --with theWindow in everyWindow
			
		end tell --application anApp
		
		
		
		-- Adjust non-scriptable application windows
	on error
		tell application "System Events"
			tell application process anApp
				set everyWindow to every window
				
				repeat with theWindow in everyWindow
					set {{p1, p2}, {s1, s2}} to theWindow's {position, size}
					set currentBounds to {p1, p2, p1 + s1, p2 + s2}
					
					set adjustedBounds to my checkBounds(currentBounds, margins)
					if (adjustedBounds is not currentBounds) then
						set {b1, b2, b3, b4} to adjustedBounds
						set theWindow's position to {b1, b2}
						set theWindow's size to {b3 - b1, b4 - b2}
					end if --(adjustedBounds is not currentBounds) then
					
				end repeat --with theWindow in everyWindow
			end tell --application process anApp
		end tell --application "System Events"
	end try
	
end repeat --with anApp in runningApps



-- Adjust Finder windows
tell application "Finder"
	
	set {d1, d2, d3, d4} to (desktop's window's bounds)
	set finderMargins to {d1 + 22, d2 + 66, d3 - 22, d4 - 22} -- The bounds within which the windows must stay.
	set everyWindow to every window whose visible is true
	
	repeat with theWindow in everyWindow
		
		set currentBounds to bounds of theWindow
		
		set adjustedBounds to my checkFinderBounds(currentBounds, finderMargins)
		if (adjustedBounds is not currentBounds) then set bounds of theWindow to adjustedBounds
		
	end repeat --with theWindow in everyWindow
	
end tell --application "Finder"



on checkBounds(currentBounds, margins)
	
	set {b1, b2, b3, b4} to currentBounds
	set {m1, m2, m3, m4} to margins
	
	if (b1 < m1) then
		set b3 to m1 + (b3 - b1)
		if (b3 > m3) then set b3 to m3
		set b1 to m1
	end if --(b1 < m1) then
	if (b2 < m2) then
		set b4 to m2 + (b4 - b2)
		if (b4 > m4) then set b4 to m4
		set b2 to m2
	end if --(b2 < m2) then
	if (b3 > m3) then
		set b1 to m3 - (b3 - b1)
		if (b1 < m1) then set b1 to m1
		set b3 to m3
	end if --(b3 > m3) then
	if (b4 > m4) then
		set b2 to m4 - (b4 - b2)
		if (b2 < m2) then set b2 to m2
		set b4 to m4
	end if --(b4 > m4) then
	
	return {b1, b2, b3, b4} --adjusted window bounds
end checkBounds



on checkFinderBounds(currentBounds, finderMargins)
	set {b1, b2, b3, b4} to currentBounds
	set {m1, m2, m3, m4} to finderMargins
	
	if (b1 < m1) then
		set b3 to m1 + (b3 - b1)
		if (b3 > m3) then set b3 to m3
		set b1 to m1
	end if --(b1 < m1) then
	if (b2 < m2) then
		set b4 to m2 + (b4 - b2)
		if (b4 > m4) then set b4 to m4
		set b2 to m2
	end if --(b2 < m2) then
	if (b3 > m3) then
		set b1 to m3 - (b3 - b1)
		if (b1 < m1) then set b1 to m1
		set b3 to m3
	end if --(b3 > m3) then
	if (b4 > m4) then
		set b2 to m4 - (b4 - b2)
		if (b2 < m2) then set b2 to m2
		set b4 to m4
	end if --(b4 > m4) then
	
	return {b1, b2, b3, b4} --adjusted window bounds
end checkFinderBounds

Ah, yes. I can see the similarity, especially around the tell blocks. :wink:

Hi, capitalj.

Your handlers checkBounds() and checkFinderBounds() appear to be identical apart from the names of their second parameter variables. Parameter variables are local to the handlers. They don’t need to have the same names as parameters used in the calls, so you really only need one of the handlers

-- From the Finder 'tell' block at the end of the script.
tell application "Finder"
 
	set adjustedBounds to my checkBounds(currentBounds, finderMargins)

end tell

on checkBounds(currentBounds, margins)
	-- The value of the 'currentBounds' used in the call is passed to the handler's local 'currentBounds' variable.
	-- The value of the 'finderMargins' used in the call is passed to the handler's local 'margins' variable.
	
end checkBounds

Huh, I wonder how many times I’ve done that. Thanks for pointing it out.

The shorteneded script, for those (like me) who find it helpful to see the difference:


tell application "Finder"
	
	set runningApps to name of processes whose visible is true and name is not "Finder" --and name is not "DragThing" (commented because I don't have it)
	set {d1, d2, d3, d4} to (desktop's window's bounds)
	set margins to {d1 + 22, d2 + 44, d3 - 22, d4 - 22} -- The bounds within which the windows must stay. Finder margins are variables set separately in the Finder tell block

end tell

--Adjust non-Finder windows
repeat with anApp in runningApps
	
	try
		tell application anApp
			set everyWindow to (every window)
			
			repeat with theWindow in everyWindow
				set currentBounds to (get bounds of theWindow)
				set adjustedBounds to my checkBounds(currentBounds, margins)
				
				if (adjustedBounds is not currentBounds) then set bounds of theWindow to adjustedBounds
			end repeat --with theWindow in everyWindow
			
		end tell --application anApp
		
		
		
		-- Adjust non-scriptable application windows
	on error
		tell application "System Events"
			tell application process anApp
				set everyWindow to every window
				
				repeat with theWindow in everyWindow
					set {{p1, p2}, {s1, s2}} to theWindow's {position, size}
					set currentBounds to {p1, p2, p1 + s1, p2 + s2}
					set adjustedBounds to my checkBounds(currentBounds, margins)
					
					if (adjustedBounds is not currentBounds) then
						set {b1, b2, b3, b4} to adjustedBounds
						set theWindow's position to {b1, b2}
						set theWindow's size to {b3 - b1, b4 - b2}
					end if --(adjustedBounds is not currentBounds) then
					
				end repeat --with theWindow in everyWindow
				
			end tell --application process anApp
		end tell --application "System Events"
	end try
	
end repeat --with anApp in runningApps



-- Adjust Finder windows
tell application "Finder"
	
	set {d1, d2, d3, d4} to (desktop's window's bounds)
	set margins to {d1 + 22, d2 + 66, d3 - 22, d4 - 22} -- The bounds within which the windows must stay.
	set everyWindow to every window whose visible is true
	
	repeat with theWindow in everyWindow
		set currentBounds to bounds of theWindow
		set adjustedBounds to my checkBounds(currentBounds, margins)
		
		if (adjustedBounds is not currentBounds) then set bounds of theWindow to adjustedBounds
	end repeat --with theWindow in everyWindow
	
end tell --application "Finder"



on checkBounds(currentBounds, margins)
	
	set {b1, b2, b3, b4} to currentBounds
	set {m1, m2, m3, m4} to margins
	
	if (b1 < m1) then
		set b3 to m1 + (b3 - b1)
		if (b3 > m3) then set b3 to m3
		set b1 to m1
	end if --(b1 < m1) then
	if (b2 < m2) then
		set b4 to m2 + (b4 - b2)
		if (b4 > m4) then set b4 to m4
		set b2 to m2
	end if --(b2 < m2) then
	if (b3 > m3) then
		set b1 to m3 - (b3 - b1)
		if (b1 < m1) then set b1 to m1
		set b3 to m3
	end if --(b3 > m3) then
	if (b4 > m4) then
		set b2 to m4 - (b4 - b2)
		if (b2 < m2) then set b2 to m2
		set b4 to m4
	end if --(b4 > m4) then
	
	return {b1, b2, b3, b4} --adjusted window bounds
end checkBounds




Hi.

This seems much faster, but if two or more Finder windows with the same name are open (2 home folders, for example) only the first is moved. If I put the Finder tell block back in it works fine, but seems a bit slower. I’m trying refer to the windows a different way (id, index, etc.,) but I haven’t foun the solution. Any ideas?

thanks

tell application "Finder"
	set {d1, d2, d3, d4} to (desktop's window's bounds)
	set margins to {d1 + 22, d2 + 44, d3 - 22, d4 - 22} -- The bounds within which the windows must stay. Finder margins are variables set separately in the Finder tell block    	
end tell

tell application "System Events"
	
	set runningApps to name of processes whose visible is true and name is not "Edgies"
	--end tell
	
	
	repeat with anApp in runningApps
		
		try
			tell application "System Events"
				tell application process anApp
					set everyWindow to every window
					
					repeat with theWindow in everyWindow
						set {{p1, p2}, {s1, s2}} to theWindow's {position, size}
						set currentBounds to {p1, p2, p1 + s1, p2 + s2}
						set adjustedBounds to my checkBounds(currentBounds, margins)
						
						if (adjustedBounds is not currentBounds) then
							set {b1, b2, b3, b4} to adjustedBounds
							set theWindow's position to {b1, b2}
							set theWindow's size to {b3 - b1, b4 - b2}
						end if --(adjustedBounds is not currentBounds) then
						
					end repeat --with theWindow in everyWindow
					
				end tell --application process anApp
			end tell --application "System Events"
		end try
		
	end repeat --with anApp in runningApps
	
end tell


on checkBounds(currentBounds, margins)
	
	set {b1, b2, b3, b4} to currentBounds
	set {m1, m2, m3, m4} to margins
	
	if (b1 < m1) then
		set b3 to m1 + (b3 - b1)
		if (b3 > m3) then set b3 to m3
		set b1 to m1
	end if --(b1 < m1) then
	if (b2 < m2) then
		set b4 to m2 + (b4 - b2)
		if (b4 > m4) then set b4 to m4
		set b2 to m2
	end if --(b2 < m2) then
	if (b3 > m3) then
		set b1 to m3 - (b3 - b1)
		if (b1 < m1) then set b1 to m1
		set b3 to m3
	end if --(b3 > m3) then
	if (b4 > m4) then
		set b2 to m4 - (b4 - b2)
		if (b2 < m2) then set b2 to m2
		set b4 to m4
	end if --(b4 > m4) then
	
	return {b1, b2, b3, b4} --adjusted window bounds
end checkBounds

Hi, capitalj.

The script’s coming on well. :slight_smile:

Individual items in System Events are “ as in the Finder “ referenced by name, so similar items with the same name can cause confusion, as you’ve realised. Two variations that work for me (but are minimally tested!) are:

  1. Loop through the bulk reference to the windows rather than resolving it into a list of individual references first. (ie. dispense with the variable everyWindow.)
tell application "System Events"
	tell application process anApp

		repeat with theWindow in every window
  1. The same thing really, but phrased differently:
tell application "System Events"
	tell application process anApp

		repeat with i from 1 to (count every window)
			{{p1, p2}, {s1, s2}} to window i's {position, size}

			-- Also replace the other two instances of 'theWindow's' with 'window i's'

PS. The inner tell application “System Events” isn’t needed.

Slowly.

Thanks for the suggestions, I’ll give them a shot - I think I tried #1, but I’m not positive, and #2 looks like a what I have been working towards (but not quite getting there.) And I could have sworn I removed the extra tell Application “System Events.” Oh well.

Thanks again for the help.

j

Hi Nigel.

I chose this one because it’s more familiar.


repeat with theWindow in every window

I’m testing return times with an idle hander now.

Thanks again,

j