Building an Automator Script To Snap Windows Into Position

If you would like to precisely position your application windows without using any third-party tool, you can build a versatile script in Automator to snap windows to portions of your screen. You can give yourself multiple options such as the Left Third, Right Two-Thirds, corners or anything you can come up with. Then you can put the workflow into the Services menu where you can quickly trigger it with a keyboard shortcut. The framework in this JavaScript/JXA script can be used to develop your own customizable window organizing tool.
You can also watch this video at YouTube.
Watch more videos about related subjects: Automator (50 videos).

Video Transcript

Hi, this is Gary with MacMost.com. On this episode I'm going to show you how to write a script in Automator so that you can snap windows to positions on your screen.
MacMost is brought to you thanks to a great group of supporters. Join us and get exclusive content at MacMost.com/patreon.
So I know there are third party tools that allow you to snap windows to different parts of your screen. You can do the same thing at Automator and with a little scripting you can make it quite advanced. So let's start off here in Automator. I'm going to create a new document and I'm going to make it a Quick Action. Now the only thing I'm going to add to it is the ability to write a JavaScript script. So I'm going to use Run JavaScript and drag that over into here. Everything we're going to do is going to be in here. I'm going to expand this space a bit. We're going to put all our of code in here. The Workflow is not going to receive any input. We're not going to have any selected text or anything like that and it's going to work in any application.
Now to get started I'm going to get the current app. So the one that's currently running because that's the one we're going to change the position of the window for. So we're going to put two lines here.
App = Application.currentApplication();
app.includesStandardAdditions = true;
This is typical in starting a JavaScript or JXAscript in Automator. Now we want to set the position for the front-most window for this app. So to do that it's pretty simple. I'm going to do app.windows[0] for the first window and I'm going to set the bounds equal to a boundary object which has four properties {x: so we'll say x is 0 for the upper left hand corner with y:0 as well. We'll do width and we'll set it to something like 300 pixels and height we'll set that to say 500 pixels. All this will do then is set the boundary for the current window to zero zero to 300, 500. We can get rid of the return input there. We don't need that. We can just try this script out by hitting the Run button. Since the front-most window is Automator itself it should effect Automator. You can see, sure enough, it set it there.
Now what's interesting here is you can just change these numbers to whatever you want and you can be done. You can create a variety of different Automator scripts that can set things to the top left corner, the top right corner, the left third, the right two-thirds. Whatever you need. But let's get a little more complex and make a script that's a little more versatile. So I'm going to put a little window that pops up and asks you where you want the window position. So I'm going to do a variable called areaChoices and I'm going to set it equal to a list of choices. We'll make those our three choices for now. 
Now I'm going to get the user choice using app.chooseFromList. You have to use a reference to an app in order to bring up chooseFromList. So we'll set the first parameter to be areaChoices. Then we'll do some more options here. We'll do withPrompt, Where? and we'll close that off. Comment this line out now so it doesn't actually do anything. I'll hit Run and you can see it comes up with this list here and I can select one and hit OK or I can double click one and the result is put into Area. So what I can do here is say if (area = "Left Third") and we need to do double equals there to test. Then we can do something like this and else if (area =="Right Two-Thirds") then we can do the same thing.
I'm going to copy and paste but we'll change the numbers. We'll have it start at 300 pixels and be say 600 pixels wide. So now it will choose between these two. If I run this now, if I do left third, it's going to do that. If I run it again and I choose right two thirds you can see it puts it there. It's not putting it exactly at one third or exactly the right two thirds because all I'm doing is just guessing using these numbers. So let's stop guessing and actually get the real values.
So here's the code I'm going to use to get the pixel dimensions of the screen. It's going to work regardless of how many displays I've got. So let's walk through it. The first line is going to run a Shell Script. The shell script is going to do this special assistant profiler here to actually get the text about all of our displays and graphics card. It's the same thing that you will see if you go to About This Mac and then go to System Information and then look at your display information. The text it's going to return is going to be this text. I'm actually going to show it to you. I'm going to paste in this return here so I'll run this. I'm going to have to just choose one here, it doesn't matter which one. We'll see every line here. You could see what it looks like. It does look like what you get in About This Mac. 
So what we want to do then is we want to put that into display info. Split it every time we see the text UI Looks Like. So let's look at that again here. We can see here the text UI Looks Like appears three times. One for each screen. It kind of kicks off the information we want about the screen. The resolution is great but if we have the resolution set to be something different, which I do on my retina display, then it's not going to match. UI Looks Like is the same as the resolution here for the first and for the second. But for the third screen you can see the resolution is much higher and I have set the UI Looks Like to be smaller. So that's the real resolution of the screen is 1280 by 720 and it's what we want to use in our script to figure out what is half or one third of the width of the screen.
So every time it sees this it is going to divide this up into a different item in the array using Split and the split command is basically going to make sure that there's one element that's before the first display. The second element is going to be the first display. The third element is going to be the second display and the fourth element is going to be the third display. We get all the text from UI Looks Like right after that all the way down. Now notice here it says Main Display Yes. It only says that on the third display. That's key. That allows to identify the third display as the main display.
So after we work with all of that we're going to loop through those four items. We're going to look for the text Main Display. If it's greater than negative 1 it means that it exists in this item. It's only going to be there for the last item. In that case it's going to run this. It's going to split all that text up with spaces. After UI Looks Like we get 1280 space X space 720. So that first item represented by zero is the width. The third item represented by 2 is the height. The X would be the 1 in-between that. So it's 0, 1, 2. 1280 720 put into display width, display height. Great we're done!
So we Break. What we should have here is a correct displayWidth and a displayHeight. We'll test it. So I will put that in a Return. I will Run. It doesn't matter which one we pick. You can see here the return value is 1280, 720. Hooray! We've figured out the display width and the display height. Now we can use that down here. So we can say, okay x and y upper left hand corner. But the width, let's set that to displayWidth divided by three. Or better yet multiplied by .333. The height we'll set that to displayHeight. So full height. That'll be the left third. Then we'll go and we'll say the right two-thirds we'll start that at displayWidth x .333. So one third over and then we will go displayWidth x .6667 two thirds of the width. The height we'll do displayHeight.
So now when we run this we go and say left third gives us the left third. We run it again two thirds give us the  right two thirds. So that's exactly what we want. So far so good. We're going to run into a problem when we try to run this as a script on anything but Automator. We are going to need another way to tell what app is actually the front most app. When we run this as a service it's going to still think that the Automator process is the frontmost app. 
So we'll keep this here but we'll add some more lines. This is what this looks like here. We're going to get front app name by going to Application("System Events") looking at the processes there. Then we will use the process is .whose {frontmost: is true. It's a little weird syntax here but it actually gives us the name of the frontmost process. We actually have to look at the first element in the array that's returned to get the name of it. So the value then should be returned. Let's try it out. Ah, Automator. That's good. So it's working. The front app is then going to be Application front app name. You would think that you'd be able to take this off and get the front app right there in that line. But it doesn't work. 
So now we have the front app. Great. So instead of setting the app, the main app, the one that's running which would be the Automator process, we're going to set the front app here. It should work the same in Automator. Great, it does. Now let's save this script. Save. We're going to call it Window Position. It saves it as a quick action. So now it should appear if we go to anything, the application you're running, and there's under Services, Window Position. So let's try it out in another app, like say Safari. So here we are in Safari. Go to Safari. Go to Services and Window Position. It's going to give us a little dialogue there. I'll say left third and there it goes. It set it to the left third. The great thing is I can open up another window here in Safari and now this is the frontmost window. So when I go Service, Window Position I can say right two thirds and it locks this window to the right two thirds of the screen.
So next I can go in System Preferences and I can do Keyboard, Shortcuts, App shortcuts and I can add one. I'll say All Applications, Window Position, and I'll make the keyboard shortcut something like Shift F1. There you go. So now I can go into Safari here. Let's move this window around. If I look under Services I can see Window Position is Shift F1. So I don't have to choose that anymore. I can just do Shift F1. It'll pop up and I can set the position of the window. Now what you're going to get every time you use the script is you're going to get a little dialogue that asks for permission. I had already run the script for Safari so you didn't see it. But let's try it on Maps here. Go to Services. Here I can just use Shift F1 and it's going to say Maps wants to access System Events. All of that. So you say okay. Now I can use the script. That's the basics.
You also want to add another else statement for the top left corner. So in that case you want to have say half the displayWidth and half the displayHeight. Note that display Height does not take into account the Menu Bar height at the top so you may want to subtract some or work with that just to get things exactly as you want. Of course if you do have multiple screens and you want to set things to be in different areas of different screens you can work with negative values or values that are greater than the displayHeight or displayWidth to position it on the different screens where you want it.

Here is the final script:

function run(input, parameters) {
	
	app = Application.currentApplication();
	app.includeStandardAdditions = true;

	var frontAppName = Application("System Events").processes.whose({frontmost: {'=': true }})[0].name();
	var frontApp = Application(frontAppName);

	var areaChoices = ["Left Third", "Right Two-Thirds", "Top Left Corner"];
	var area = app.chooseFromList(areaChoices, {withPrompt: "Where?"});

	var displayText = app.doShellScript("/usr/sbin/system_profiler SPDisplaysDataType");
	var displayInfo = displayText.split("UI Looks like: ");
	
	for (var info of displayInfo) {
		if (info.indexOf("Main Display: Yes") > -1) {
			var infoPieces = info.split(" ");
			displayWidth = infoPieces[0];
			displayHeight = infoPieces[2];
			break;
		}
	}
	
	if (area == "Left Third") {
		frontApp.windows[0].bounds = {x: 0, y:0, width: displayWidth*.3333, height: displayHeight};
	} else if (area == "Right Two-Thirds") {
		frontApp.windows[0].bounds = {x: displayWidth*.3333, y:0, width: displayWidth*.6667, height: displayHeight};
	}		
}

Comments: One Comment

    inghilt munoz
    6 years ago

    It has been extremely useful, and the explanation very clear. many thanks 💚

Comments are closed for this post.