You can use JavaScript in Automator to save the positions of each window in each app to a file. Then you can recall those positions later if you want to resume working with the same window locations. This script will save as many position settings as you like and then allow you to recall them easily. You can even set it up as a keyboard shortcut.
You can also watch this video at YouTube.
Watch more videos about related subjects: Automator (50 videos).
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. Today let's create a script that will allow you to save and recall the positions of Windows on your Mac.
MacMost is brought to you thanks to a great group of supporters. Go to MacMost.com/patreon. There you can read more about it, join us, and get exclusive content.
So the general idea is say you've got some windows open like I have a Pages window here on the left and a Safari window here on the right. You like this arrangement. You're working with this arrangement in windows and you'd like to automatically be able to go to this arrangement the windows have and have them set to the same sizes and positions next time you want to do this work. So it would be great to be able to save them and then recall them. That's what this script is going to do.
So let's work on this in Script Editor but we're going to move it to Automator before we're done. So in Script Editor here's the script. It's not that long but I'm not going to type it out for you. I'm going to have it here and we're going to go through it line by line.
So the very first line is this one here. It's going to declare a folder, a position for a folder that's going to be in your User Folder and it's going to be called Window Positions. You can call it whatever you want. This is where we're going to save the files that are going to have the locations of all the windows. So it's useful to have this here at the beginning of the script just all typed out. Now let's not forget to create it. So right now I'm going to go in the Finder here. I'm in my Home folder and I'm going to create a New Folder called Window Positions. I've got that already to go.
Now you've got a couple of lines here that are standard for any Java script that you do. This is Java Script not Apple Script. You can see here I've switched it in the Editor. So this will basically take the first application, the one that's running and up front when you execute this script. It's going to include Standard Additions which means we can get access to all this stuff that we can script on our Mac. So now we get to work.
The first thing we're going to do is we're going to go and get a list of all the files in the folder we just created. So the File List is equal to Applications System Event dot folder dot byName with that filePath. This path that we created right here. So we've got all the files in there. All the Disk Items, files, folder, etc. and get those name. Now we have that in File List. We have a list of all the names in there. Now we're going to check to see if it's greater than zero. Initially it's not going to be because there's nothing in there now. So it is zero. But if it is greater than zero then there's a file in there. At least one. Let's choose it.
We're going to set a variable which file equal to app choose from list, show the files that are in there, and then use the prompt which positions. The default item is going to be the very first on. The Cancel button. We're going to rename the cancel button as Save Cancel because basically it's going to ask you to choose a set of window positions and if you hit the cancel button then it will switch to a mode where you can save the current positions. Now if the result of that are no file, not which file, nothing has been chosen then we're going to Save. So hit the Cancel button, or the Save/Cancel button as we've renamed it, or in the case where there was nothing there to begin with, this is the first time we're using the script, we're automatically going to go and call function called Save Window Positions. It's going to save everything out as a text file.
Otherwise we're going to go to load window Positions. So a file has been selected. We want to set the positions of the windows. Let's do that. Let's pass in the name that was selected. The name of the file that was selected. So let's look at these functions here. We have two of them. Save Window Positions. So this is the one that's going to save it all out.
The first thing we're going to do is we're going to ask the user for a file name. So we're going to say, okay, prompt equals. Then display dialog. Ask what name we want to use. Give a default there. We're going to put this in a try catch sequence here. So if they hit Cancel, so there's an error, then it's just going to return. In other words the script is going to finish gracefully. Nothing is going to happen. You've canceled out and no action will be taken.
Otherwise we're going to put in a variable called positionsName prompt dot textReturned so the text return from this prompt. Now we're going to go through and get all of the positions of all of the windows. So we're start a variable here called Save Positions. It's going to be empty. We're going to loop through everything. So we're going to get all the apps. appList is Application System Events dot applicationProcesses dot whose visible is true. Only the apps that are visible. Then we're going to get the name. So we're going to get a list, an app list, of all the names of the apps that are visible.
Then we're going to go and loop through them. So it's going to loop through them number wise. If there are seven apps visible it's going to go zero, 1, 2, 3, 4, 5, 6 and we're going to get the name of each one. So the app name is everything in the appList get the number. So zero, number 1, number 2. So say if you've got Pages and Safari it will first do Pages and then do Safari.
Then we're going to loop through all the windows. So the window list is get the application with that name dot windows. The list of all the windows. We're just going to go through those by number too. So windowNum is going to be all the windows in the list. So if there are two windows it's going to be zero and then 1 as you go through the loop. Then we're going to get the window. Which window equals in the windowList the number. So the window zero and window 1. Which window is going to actually have all the properties of the window. So name of the window. The bounds of the window which tell us the location and width and height.
Then we're going to add to savePositions, that variable we created up here that's empty, we're going to push a new element on to it. First is app and the appName. So Pages, Safari, etc. Then window and the windowNum. So zero, 1, 2, 3. Then bounds whichWindow dot bounds property of it. So the x location and the y location, width and height. When we're done savePositions is going to have this great list of all of the windows of all of the apps that are visible with the location and size of each window.
Then we're going to save it out. So we create a file that is open for access with the original filePath all the back from the beginning of the script and the Name that we chose. Set it to write positions. We're going to set it so we start writing at the beginning of the file so it will overwrite an existing file. Then we're going to write it out, app.write, and we're going to write to that file that was selected and then close the file. Now we write out savedPositions but not before passing it through the function JSON.stringify. That takes the array that has all this information, this stuff here. The name, the window number, and the boundary for each window. We're going to write it out as a pretty looking string. One that can be saved as a text file with no problem. Then when we read it in you'll see we'll convert it back to a regular Java script array.
So remember now if you chose a window name then instead of saving it called loadWindowPositions with the filename. So the file that was selected . So now we can open up the file and read from it the path, that same path we set at the beginning, plus the name. We have to put that inside of a path function here. We are going to store the text from in that file in fileText. Then we're going to use JSON.parse which is the opposite of JSON.stringify to convert from that nice looking text in the file back to an array we can use in our script. So now we have savePositions returned exactly as it was when we saved it originally.
Then we're going to loop through each element in the array and get the data from it. So from savedPositions each one from zero, 1, 2, 3. Every window is there as a separate piece of information. Then we're going to get the Application name from window so that dot app property that we created back here when we made this array and said dot app. App is a property, window is a property, bounds is a property. We created that so we're going to get the app name, so refer to the application, window number, and there's that window property I just referred to, and then set the boundaries of that to the bounds part. So using app, window, and bounds from the original thing that we wrote out, app, window, and bounds.
We set it. So in other words set that window to the original position that we recorded in the file. Now we have this IF statement around here to prevent errors. Why? Because say you had three Safari windows open and you recorded the positions of all of them. Now you try to run this script again and set back those three window positions. But you only have two Safari positions open. It's going to try to set that third Safari window and there's going to be an error. So it's just checking to see if hey have we gone beyond the number of windows that we have here. We have three positions for three Safari windows and only have two Safari windows to check. So it'll do the first two fine and then the third one it's just not going to pass this IF statement. So it will never try to set it. So this just prevents errors.
That's it! Let's go and try to run it. I've got these windows saved here. Notice I'm also putting the Script Editor window here. So the positions of these three windows. We'll run it. It's going to go and ask for a name so we'll do Test1 and hit OK. It saved it out. Let's go and take a look here in the Finder. There it is. Test1. I can select it and you can see there what it saved. You can read what's there. App, Finder, window, 0, bounds. There's the location of that. You can go through all this. There's Pages. app, Pages, windows, 0, bounds.
So we now have divisions from all the windows there. Let's try running it again. But before we do let's mess things up a bit. Let's move this window here. Let's move this window here. Now this Script Editing window here is also going to be changed. Remember it was like down here. So let's run this. So now it actually detects some files in that folder. Test1 is there. Great. Do you want it? If I hit Save/Cancel it would prompt me to save another file. Let's go and just select this one. Now we can see it loads up those window positions that it sets. The Pages window, the Safari window, and even the Script window tol be in their original location.
So the next thing we want to do is we want to have this as a Service so we don't have to open up Script Editor to run it. So I'm going to select all of the Java Script and I'm going to run Automator. I'm going to create a new Quick Action. It used to be called a Service in Automator. In this all I'm going to do is I'm going to set it to No Input is Received. Works in Any Application. Let's search for Java Script. I'm going to use Run Java Script here. I'm going to select it all. Delete what's in there and Paste in our big script. Then we're going to Save. So I'm going to do File, Save, and we'll call it Set Window Positions. Save. It's saved as a Quick Action.
Now we can quit Automator. We can quit Script Editor. Now we've got here go to Pages Services. We can see Set Window Positions. So let's move these windows again here. We'll do Services, Set Window Positions and it's going to run and say, oh great. There Test1 get it.
When you try this you're probably going to see these permissions when you first ran it in the Script Editor. But you're also going to see them here. It's asking is it okay. I was using Safari here and now Safari wants to go and set window positions. Of course you're going to say Allow and it's going to do that for anything that's running. You have to hit OK. But you only have to do that the first time. Notice all the window positions are now set back. The ordering is not the same so this one isn't in front. But all the positions are set back to where they were.
So we can record a new set of them. Let's say I want to move these around. I want to put Safari on the left and Pages on the right. I can now go Pages, Services, Set Window Positions and then I'm not going to select this one. I'm going to hit the Save/Cancel button and call this Test2. It's going to save the positions for these windows. Going to ask for permission here again for only that one time. So now I can mess these windows up. I can go here say Services, Set Window Positions and choose Test2. You can see it puts them all back.
Of course now that it's a Quick Action so it's in the Services menu I can go to System Preferences and then I can go to Keyboard, Shortcuts, Services, and I'll find this here Set Window Positions and I can add a Shortcut. So I can do a keyboard shortcut that will do this for me.
So this works best when you just have one window per app. You have a Pages window and you have a Safari window. When you have multiple windows you run into a little trouble because there's no way for the script to know which window is which. So if you have two Page's documents open you may end up with one and the other switching locations. But even with that limitation it's still pretty useful for a lot of people. Plus this is a great example of using Java Script for Automation. It may set you on the path to creating your own script to do your own thing.
// Set this to be the folder where you save the files. Remember to create it! var filePath = '/users/macmost/Window Positions/'; var app = Application.currentApplication(); app.includeStandardAdditions = true; // get the list of files in the special folder var fileList = Application("System Events").folders.byName(filePath).diskItems.name(); if (fileList.length > 0) { // choose a file var whichFile = app.chooseFromList(fileList, {withPrompt: "Which Positions?", defaultItems: fileList[0], cancelButtonName: 'Save/Cancel'}); } if (!whichFile) { // hit the save/cancel button, so save saveWindowPositions(); } else { // otherwise a file was selected, so load loadWindowPositions(whichFile[0]); } function saveWindowPositions() { // prompt for the filename try { var prompt = app.displayDialog('Save positions under what name?', {defaultAnswer: "My Window Positions"}); } catch (e) { // do nothing if cancelled return; } var positionsName = prompt.textReturned; var savedPositions = []; // loop through all applications var appList = Application("System Events").applicationProcesses.whose({visible: true}).name(); for (appNum in appList) { var appName = appList[appNum]; // loop through all windows for application var windowList = Application(appName).windows; for (windowNum in windowList) { var whichWindow = windowList[windowNum]; // put the app name, window number and location in the list savedPositions.push({app: appName, window: windowNum, bounds: whichWindow.bounds()}); } } // open a new file, overwrite if needed var file = app.openForAccess(filePath + positionsName, { writePermission: true }); app.setEof(file, { to: 0 }); // write data to file app.write(JSON.stringify(savedPositions), {to: file}); app.closeAccess(file); } function loadWindowPositions(filename) { // get saved positions from file var fileText = app.read(Path(filePath + filename)); var savedPositions = JSON.parse(fileText); // loop through windows in list for (windowNum in savedPositions) { var windowData = savedPositions[windowNum]; // if there is a window for that app, then set its positions if (windowData.window < Application(windowData.app).windows.length) { Application(windowData.app).windows[windowData.window].bounds = windowData.bounds; } } }
Gary, Great stuff. I have AppleScripts for this but look forward to trying it with JavaScript. Thanks for posting the video.
When we are doing scripts like this where do we save them so they run? I am new to this kind of thing.
Michael: Depends. For this, you can save them where you like. Create a "Scripts" folder or something in your Document folder. For Quick Actions (AKA Services), they automatically go in a special folder.