MacSparky's window sizing and positioning....revisited

David’s recent video on using Keyboard Maestro (KM) to creating a series of keyboard shortcuts based on how VIM uses the keys surrounding the ‘k’ key was very interesting (not sure if you have to be a MacSparky subscriber to view this), and of course dropped me down a deep rabbit hole of time wasting exploration. I thought someone might be interested in this, so I figured I would spend some time writing up what I have been playing with.

Consumer warning: This post is going to be far too long. Proceed at your own risk.


Some time ago, I had written a rather extensive set of KM macros, subroutines, etc, that could be used to divide the screen (or screens, because it handled multiple screens) into any number of rows and columns and position windows into any part of that grid, You could call the subroutine with parameters for the number of rows and columns, the row, column position of the top left corner of the window, and the number of rows and columns to span, and the subroutine would determine the actual screen coordinates on the specify screen for the window, which would them be used for move and resize operation.

I still use these macros a bit (I have some setup macros that custom size and position various application windows) but frankly this was more a fun exercise than something that has seen much use for me, and Moom is so much easier for this purpose, so I have now purchased Moom and willl soon start playing with it too, so I decided to start from scratch with this new exploration.


David’s approach was to use a keyboard trigger (Meta-k, where Meta is of course the hyper key, usually caps lock, mapped to the modifier combination of ⇧⌥⌃⌘ using Better Touch Tool (BTT, which is how I have been doing it) or Karabiner Elements (how I used to do it). In his approach, Meta-k is set up as the hotkey trigger for multiple KM macros to do various window movement/sizing operations; this results in a pop-up conflict palette and by naming the individual macros appropriately, single keys can be used to select from the pop-up palette. Therefore, typing Meta-k followed by j, for example, moves the window to the left 1/2 of the screen.

While I am a major fan of conflict palettes, and in fact I have a substantial number of macros assigned to trigger with Meta-a (super easy for me to type), I wanted something I little different for my implementation, and so instead I chose to create a macro group that would be triggered for a single action via a hot key:

Within the macro group are then all of the various window moving and resizing; here’s a sample:

CleanShot 2026-04-09 at 08.40.47@2x

The macros are numbered, because that keeps them in the desired order (without the numbers they would be ordered alphabetically) which is necessary for the palette’s appearance. The icons were created simply in KM using symbols that were readily available and a color background; all easily doable when the macros is created.

Here’s the top left macro:

Setting up the move and resize action is simple enough from presents already included with KM.

Something very cool about this approach. You will note that this macro is triggered by a single letter hotkey, u. What is really nice with KM is that when you set up a macro group to pop up a palette like this, the hot keys assigned to the macros within the group are only active when he palette is active, so you don’t have windows jumping around every time you start typing. This is, in my opinion, an incredibly inspired feature of KM!

When I active the palette, by having 9 items and setting it up with three columns, I get a nice mnemonic display:

CleanShot 2026-04-09 at 08.45.22@2x

That gives me a nice visual reminder of how to move windows.

This is a nice implementation, but there is just one thing that irks me. It appears that the KM window move and resize action first moves the window, and then resizes it as a separate operation, so the move/resize is kind of jerky in motion, and while it’s hard to argue that is all of that big of a deal, it bothered me enough for me to feel like more time should be wasted spent.


Alfred is my. launcher of choice, and so my next stop was to see if I could create this workflow with Alfred. The recently added automations workflow objects in Alfred provide a great deal of functionality, including the ability to move and resize windows, either by pixel coordinates or with presets:

It’s easy enough to start up the workflow with a hotkey tigger in Alfred, but the problem to solve is how to get Alfred to perform an action when just a single key is typed. If you are an Alfred user, you know that once Alfred is activated, you typically select an item from its dropdown list via mouseclick, scrolling with arrow keys and typing enter, or a ⌘ plus number sequence, but not by just typing any key like j.

My solution was to add an external trigger to the workflow, triggerable by appplescript or url:

I then tie the hotkey trigger for the workflow to a script filter that takes the query and retriggers Alfred via the url, as follows:

import sys
import subprocess
import os

try:
	query = sys.argv[1]
	if len(query) == 2:
		if ord(query[0]) == 79 and ord(query[1]) == 770:
			query = 'left_two_thirds'
		elif ord(query[0]) == 79 and ord(query[1]) == 768:
			query = 'right_two_thirds'
		else:
			query = ''
	if query != '':
		trigger = f'open alfred://runtrigger/{os.getenv("alfred_workflow_bundleid")}/window_control_entry/?argument={query}'
		subprocess.run(trigger, shell=True)
except:
	pass

sys.stdout.write('{"items": []}')

I think the code is mostly straightforward, but basically it takes the query (delivered via the sys.argv list. As long as there is a query and it is not blank, that query is than passed (in the subprocess.run call to Alfred via a url call with the open shell command.

Because the script filter is run over and over with each letter typed into the Alfred entry window, the first character typed after invoking this workflow will cause the script to run with a single character as its query, and jump to the external trigger entry point.

Having the script return an empty item list (sys.stdout.write('{"items": []}')) to Alfred means that you see just the Alfred text entry window with no drop down at all.

If you have written Alfred script filter based workflows, you will know that as you start typing, the window remains open until you action something directly, which isn’t happening here, so if you invoked the workflow via hotkey and then typed a j to move the window to the upper left corner, you could keep on typing so we need a way to get the Alfred entry window to close after one key is typed. We will get to that.

Once the external trigger is invoked, it has the key typed as its query input. I use a conditional to divide the actions up into categories:

CleanShot 2026-04-09 at 09.07.07@2x

based on the key typed matching using regexes:

and each branches to a secondary conditional:

that then invokes the proper window move and size action:

I did this two-tiered branching mostly because it made the layout on screen of the workflow more ordered and understandable.

I suspect the workflow would be faster (at least in processor time) if I used one large conditional and direct string matching instead of regexes, but in user time I am guessing the benefit would be negligible so I will probably just leave it as is for now.

After the window move/resize action, there are a bunch of connectors that just make it so the lines are easier to follow, leading to a final step of typing the escape key:

CleanShot 2026-04-09 at 09.12.17@2x

This typed key is picked up by the open Alfred entry window, which then closes. (Yes, I know there is also a “close Alfred” action, but if you do that, whatever was typed is retained the next type you action Alfred, while if you use the escape key, the entry is cleared, which is the desired effect).

There is an else clause to the first layer of branching taken if the key typed is not recognized as one of the enabled actions, which uses an AppleScript to sound the system alert tone and then goes to the escape key entry to close Alfred as well.

There is a bit more to this, with some additions I didn’t do with the KM approach. Alfred has automations to not only move/size to left half, top half, top left quarter, etc, but also left 1/3, middle 1/3, left 2/3, etc, and I wanted to take advantage of that.

Conveniently, when you type ⌥ plus another key, you often get a new character. For example, typing ⌥-s actually types ß; ⌥-j types ∆ and so forth. So, the conditional also recognizes these characters, and when ⌥-j (or ∆) is typed, the window moves to the left 1/3 of the screen.

You see that here in the sub-conditional that handles 1/3 screen windows:

Obviously I wanted to also do the 2/3 screen actions, and I figured I would use shift option, eg ⇧⌥-j would be the left 2/3 of the screen. And here is where it all fell apart.

You will see that there is something different in the way that 2/3 screen sizing is handled:

Instead of a single character, I have left_two_thirds and right_two_thirds as the matches.

For reasons I don’t know, while ⇧⌥-j results in a readable character when typed (here is is: Ô), and if you do the following in a python interpreter window in Terminal

>test = 'Ô'
>len(test)
1

You see that you get a single character string (presumptively a unicode character in this case, but Python 3 handles strings a unicode automagically), for reasons I don’t know, the Python script in the Alfred script filter gets the shift option keystrokes as a two character sequence. I found it hard to figure out exactly what the individual characters were, but it was easy to convert them to numbers with the ord function, and that’s where the part of the script that tests for a query length of two comes in, and then translates the two character sequence via the ord values into the strings left_two_thirds and right_two_thirds.

Since a successful call to the next phase of the workflow results in closing the Alfred entry window, I will not (in theory) have any other circumstance where a two character query is being processed by the script; even if that does happen, the script won’t recognize it and so (hopefully) nothing meaningful will get passed to the next phase of execution.

You might wonder why I didn’t translate all of the keystrokes into more readable commands, eg when a j is typed, translate that to left half screen and so forth. No good reason; I just didn’t spend the time, but if there is a good reason for that (maybe some additional processing later on that would make it more convenient to have things expressed that way) it would be easy to do by just creating a dictionary keyed by single characters…

That seems to wrap up the Alfred part of this novel, at least for now. I can go back and add all of the option and shift option triggers to my KM approach if I decide to stick with that vs the Alfred approach, but I don’t see way to keep the 3x3 pop up grid graphic when I add more macros to that macro group, or have the palette change when option is held down, and that would be nice if I could figure it out…

Oh, right: I also want to play with the grid action in Alfred to see if there is some way to get a nice popup like I have with the KM implementation when this workflow is triggered…I’ve never used that before, so stay tuned…


I did mention using Better Touch Tool (BTT) to enable the meta key, so it does seem as though we need to see how to do all of this using BTT as well, don’t we? Here goes…

There are two different ways that seem to allow the same outcome. First is to create a custom named trigger, which has an action of showing a “custom content menu”:

The other option is to use a floating custom menu, which has a ton of options for the menu configuration (but most of which aren’t really needed for this particular application):

You can see the first two configured items in the custom floating menu; for the custom content menu, I have filled that out in more detail:

Each menu item fires off a built in BTT action to move and resize a window.

The icons (different sizes and colors) are simple images created in Photoshop for this purpose (which I suppose I could use elsewhere, like on StreamDeck buttons, for instance).

The floating menu other the custom content menu is setup to be triggered by Meta-w in this case:

Here is the custom content menu:

I rather like the appearance of this, and keyboard navigation (arrow keys to move up and down the menu items, for example) seems to me to be more mature than with the floating menu, although I have not played with the latter enough to say anything with certainty.

You will notice on that popup menu that there are hotkeys attached to menus items, which at least for now don’t seem to work. With the custom context menu, there is a way to attach hotkeys to each item which does not seem to be the case for a floating menu, but again I am likely missing something. I could add in what BTT calls a conditional action group, active only when one of these menus are active, that would attach the same keyboard keys (j k l etc) to various of these menu items so that you can use the activation key (Meta-w) following by a keystroke to move windows, just as with the Alfred and KM implementations. I haven’t gotten around to doing that yet.

I find BTT to be unbelievably functional, but the user interface is, for me, very hard to navigate and has a lot of quirks (sometimes things will appear that are actually not related to the selected item in another pane, for example) and it is often hard to figure out exactly how to accomplish something. I find the work involved to create a lot of things in BTT is much greater than with KM, Alfred, etc, but on the other hand, there are very complex things (like the custom context menu) that you really cannot do with the other apps. As a result, I have very little configured in BTT, but if I decide to keep the window navigation in BTT I will work on adding in the keyboard shortcuts to be active when the custom content menu is on screen.

Some things I haven’t yet figured out:

Firstly, there is the following configuration for items in the custom context menu:

CleanShot 2026-04-14 at 13.06.42@2x

I am not sure what “Key to Trigger Item” does. I would have thought that when the custom menu was visible, typing this key would trigger that menu item, but so far that does not seem to work.

Secondly, when I use this method to, for example, reposition the window to the right 1/2 of the screen, if I then try to drag the window to another location, it readjusts to its previous size. Presumably BTT is somehow keeping track of this window and doing that (?), but what I would prefer is that when the window is resized and repositioned and then I drag it elsewhere, it should keep the size intact. Perhaps there is a BTT setting I haven’t yet found that would control that behavior.


What about LeaderKey?

I haven’t actually used LeaderKey, or its now in beta successor, Tuna, but if we are going to talk about managing windows via the keyboard with a trigger key (meta-k) that essentially creates a keyboard mode when keys now trigger actions instead of just keystrokes, then it seems that LeaderKey is tailor made for this use.

I suppose at some point it would be worth looking into this application. I don’t know how many different ways this cat needs to be skinned, but if Brett Terpstra uses it, should I use it too?


And then there was Moom.

Sooner or later, I have to ask myself: Self, why did you do all of this if you already have a license for Moom anyway?

Firstly, I bought Moom, but I’m just beginning to play with it, and I do like the exploration, programming, and discovery part of implementing things myself, even if I don’t wind up using KM, Alfred, etc in the long run for the purpose.

Secondly, I haven’t actually been able to figure this out in Moom yet. Here’s what I’ve gotten to so far.

Moom has the ability to create custom settings:

CleanShot 2026-04-14 at 13.10.01@2x

and you can attach a hotkey, here Meta-, but when you actually invoke that hotkey, you get a popup which is basically all of the menu items “below” that hotkey item in the custom settings:

Here I created a folder of custom actions, and put in an action to reposition the window the left 1/2:

CleanShot 2026-04-14 at 13.16.39@2x

Even though I have enabled keyboard actions which seems to be required to use non-modified keyboard controls for custom items, it does not work - when I use Meta-\ to open the custom items display, typing “J” does nothing.


I’m not quite sure where to go with Moom, which is in a way quite disappointing since it would be ideal to use the app specifically designed for window management for this window management task.


Amazingly, that’s it for this posting. If you made it this far, congratulations!

I will look forward to comments, suggestions, improvements, etc from the community.

6 Likes

Moom support guy here …

J is a single-key shortcut, what we call a restricted shortcut, and it can only be used in Moom’s keyboard mode. But if you’re looking to use Moom in an “activate, then use single-key shortcuts” mode, then keyboard mode is what you want anyway.

Here is where I’d link to our online help, but the forum won’t let me … so open the in-app help, and go to the Keyboard chapter. (Or online: our website slash moom slash help slash keyboard dot html.)

If that’s not your question, please let me know what you’re trying to do…

-rob.

@rob: Thanks so much! That was very helpful in setting me on the right path.

Using Moom, I have the following setup in the Keyboard category:

As you can see, I removed all of the “default” options in the keyboard section, so that only the custom options that I created are active.

I unchecked showing the keyboard control indicator, but that is a matter of preference. What is awesome with Moom is that when you activate keyboard control via the hotkey (indicated here as Meta-= while I figure out which system to settle on), the window that will be affected is highlighted (well, the rest of the screen is dimmed slightly) which is an awesome effect!!

Here you can see a portion of my custom controls, although I will be adding more:

One interesting thing: I check the keyboard option to show the cheat sheet, but when I do so, I only get:

CleanShot 2026-04-16 at 08.52.02@2x

It seems that the custom setups that are triggered with the use of the ⌥ or ⇧⌥ modifiers do not appear in the cheat sheet. I’m not sure why (? bug). If I change any of those to use a regular keystroke without any modifiers, it does show up in the cheat sheet. I am not yet sure if I am misunderstanding something or setting something up wrong, but the controls work fine.

Another nice thing with Moom: I found that if I don’t action a key after using the hotkey to enter keyboard control mode, after a few seconds Moom leaves keyboard control mode automatically. I don’t know if there is a way to change that delay time, but either way, it’s another very nice feature.

Based on this, I believe I am settling in on Moom as the preferred way (for me) to implement window movement. That does make sense, after all, since that is what Moom is designed to do!

Here’s a cleaner version:


In Moom, I have the Command + arrow keys set to half-screen. Holding Command and pressing a single arrow key snaps the window to a half — left, right, top, or bottom. Holding Command and pressing two arrow keys simultaneously snaps to a quarter: up + left gives the upper-left quarter, down + left the lower-left, and so on.

It’s a feature, not a bug :).

As I noted above, single key shortcuts are those you can use in Moom’s keyboard mode. Anything else is a global shortcut, and can be used anywhere, any time … except in keyboard mode, where only single-key shortcuts work.

As a visual reminder, when you assign a single-key shortcut, it gets a yellow background, which matches the yellow background of the cheat sheet. If the background isn’t yellow, that command won’t be listed on the cheat sheet.

Does that help explain it?

-rob.

@rob:

Your explanation was clear, I just misunderstood.

So that’s a bit disappointing, because the goal is to be able to use a single hotkey (eg Meta-k) to enter a mode (eg keyboard mode) where single keystrokes of some sort can effect a window movement.

It seems that Moom (by design) does not support that, IF you want to be able to use modified keystrokes (eg ⌥J to move the left 1/2, but J to move the left 1/2, and ⇧⌥J to move the left 2/3) within that structure.

I might just do something a bit different: Remove all the hotkeys entirely from Moom, and set up KM macros that use AppleScript to fire off Moom custom movements, which would also me to have this work the way I want, eg having all of the hotkeys, whether single key or modified keystrokes, become active only when a mode is entered with a single hotkey.

That way I will also use the AppleScript-triggered Moom custom actions to also tie window movement onto my StreamDesk…

I still prefer the way that Moom handles window movement to the other options…even if I cannot accomplish everything I want just in Moom.

I wonder: Would you guys at Moom be willing to consider an option that allows modified keystrokes to NOT be global hotkey actions?

But … Option-J is not a single keystroke, it’s a key combo, which doesn’t meet the definition for something that works in our single-key keyboard mode. It would be, by definition, a bug if it worked there :).

You can set Moom up to make every shortcut local, so they only work in keyboard mode. If that’s what you want, quit Moom, open Terminal, and paste this:

defaults write com.manytricks.Moom "All Hot Keys Are Local" -bool YES

When you relaunch Moom, any defined hot key will only work once keyboard mode is active. The downside is you cannot have any shortcuts that work outside of the controller. If you try this and decide you hate it, quit Moom and delete the setting:

defaults delete com.manytricks.Moom "All Hot Keys Are Local"

-rob.

You’re mentioning/pinging a different Rob (me) instead of @MTrob:wink:

@MTrob Well, that’s a faux pas, isn’t it. I guess I just followed your signed name…sigh.

Thank you for the “cheat code” which I might decide to take advantage of. Of course, if I use that, I assume that means that I cannot trigger keyboard mode with a hot key? I’ll do a bit of experimenting to learn that.

Yes, you are of course correct that ⌥-Anything is by definition not a single keystroke. That should have been obvious to me, but was not.

I have gone back to my earlier reported Keyboard Maestro palette and changed all of its macros to trigger Moom custom window positioning setups, and it works just as I need it to do - except that the highly convenient window highlighting that happens when you use Moom directly no longer happens. If your suggestion above allows me to trigger keyboard mode with a hotkey and then have any other hotkey work only locally (which I as assuming will not be the case, but will try) that will be great. Otherwise, I have a good hybrid setup, with the ability to trigger the Moom window control from StreamDeck as well via AppleScript, not to mention all of the other multi-window setups that I plan to add to Moom, so all told a very satisfactory solution.

Thanks again for your help!

Immediate followup:

If I didn’t mess things up, it appears that using your defaults write statement as above has made it so that key combinations like ⌥J are NOT now captured by Moom (eg that is now a local hotkey) while I am still able to trigger Moom’s keyboard mode via a (global) hotkey.

Unless I have (again) misunderstood things, it appears that this solution makes Moom do exactly what I want: Trigger keyboard mode via Meta- and then have all key combos accessible including with modifiers, and NOT have those modifiers as global hotkeys.

I understand I will not be able to make other custom setups via global hotkeys, but that works fine for me - I may well just attach various setups to hotkeys ONLY active in keyboard mode, which is what I was going to do via Keyboard Maestro.

Plus, having all of the window movement and setups in Moom still allows the option to trigger them via AppleScript so they can be used elsewhere as well.

Great solution, and I thank you for your continued to help. Moom was, in fact, a great purchase!

Added bonus: Once you make all hotkeys local, the cheatsheet shows them all as well.

The more I am playing with Moom, the more I appreciate it!

Yes, the global trigger for keyboard mode still works, as you discovered. And because Moom is treating all shortcuts as local, they’ll show up on the cheat sheet (and they all have yellow backgrounds in the shortcut field).

Glad it’s working for you!

-rob.

I’ve followed an alternate approach to trigger window sizing and placement, using just Keyboard Maestro. This won’t be usable for everyone, as I sacrificed six modifier key/chords for each of the numeric keypad keys 1-9. (Accessibility note: you need two hands to pull this off)

This gives me six different window sizes (15%, 25%, 33%, 50%, 66%, 75%) and then of course full screen. The keypad keys let me specify the position in any corner or along any edge.

The point of this being I can quickly hit a couple of keys concurrently and get the window sized and positioned immediately.

That’s always on the same display, of course. But as a bonus with BTT I can swipe the trackpad a certain way and the window will move to the next display and maintain the same aspect ratio. (One of my displays is landscape, the other portrait.)

I’m only sharing because I think it a very efficient way to trigger these moves.