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:

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:

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:

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:

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:

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:

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:

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.




















