hello noisneil, I think that like @ccstone said KM has no trigger like the one I'm looking for. Thank you for your help
Sure but with my suggestion all you have to do is hit i, c or p for indent, caps or pdf, respectively. It's the same functionality without needing to type the whole string. Perhaps you should give it a shot.
Have you had a look at BetterTouchTool (BTT)? With it you can do the most "absurd" key sequences without transferring anything dangerous to the front app.
Suppose you want to open a new tab in Chrome (normally: Command + T), with BTT you can set up Control + Option + Command for it, pressed in a row. Or whatever you want.
You can set the time in which these modifiers must be pressed and also how much time must pass (since the last keystroke) before you activate these modifiers. And much more...
The mentioned example would look like this in BTT.
Maybe this will help.
Thank you @Frankb !! I didn't know about BTT, I will take a look at it. It looks promising!
Thank you too @noisneil yes, I get what you are saying and it is not too bad, the problem comes when you have programs where tons of shortcuts are needed, like photoshop. Sometimes having multiple keystrokes can help to memorize, since maybe you have something like "merge all" "merge visible" "merge selected" "merge down" and so on... so it is easier to have a key sequence trigger like: ma = merge all; mv = merge visible, ms = merge selected, md = merge down... instead of picking random keys.
You are welcome! Speaking of "tons of shortcuts." I know the problem
Apart from Key Sequences, BTT can distinguish between left and right modifiers. So "Commad + a" triggers different macros/actions depending on which command key you use.
This gives me more options. But more important: I can remember the shorcuts better if I only use "right command + x" and "right option + x" in certain apps.
BTT might be the right tool for you, but here's a KM solution too:
With the following macros, you set the shortcut for all your merge macros to (for example) ⌘⇧M. The next key you press triggers a specific merge type.
For example, if you want to merge visible, you hit ⌘⇧M and then v.
MacroCat - Merge.kmmacros (51 KB)
"MacroCat" is short for Macro Categories.
Add the actions you want to perform into the Switch/Case group.
Trigger MacroCat - Merge with ⌘⇧M and then hit one of the following keys to perform a merge action:
v (merge visible)
a (merge all)
s (merge selected)
d (merge down)
For actions other than Merge, duplicate this macro, rename it, set a suitable hotkey and edit the Switch/Case group. The typed key can be any letter or number. If the keypress is found in the Switch/Case group, it will perform the appropriate actions; if you type a key that is not set to perform an action, nothing will happen.
MacroCat - Watch for Keypress simply captures the keypress to be used by the main macro. If you duplicate the main macro for other uses, each duplicate can reference this macro; there's no need to duplicate it too.
I've been working on this as well because I've wanted to use something like this for a while.
I noticed a few other requirements from the OP that appeal to me:
- Don't allow keystrokes to press through to the current application
• These could interfere with the current app
- Allow sequences larger than 2
• While it's true they only need '⌘⇧M > Key' for the macros mentioned – at some point there will be conflicts and a need to expand
- Input timeout after 3 seconds
• Allows the user to exit the input sequence
- Ability to use modifier keys
• More customization
Here is my solution: Initiator Keystroke Trigger.kmmacros (113.9 KB)
• You get to call commands via character sequences without disturbing the current app
• Lots of control and granularity
• Adding a new sequence is not quick
• It's easy to make a mistake when adding
- Trigger with whatever you choose (currently Ctrl-Option-Cmd+I)
- After the audible tone, type one of the demo input sequences: "ind", "pdf", "caps", or "⌥a⌥b".
- Terminate your sequence with 'Return'.
It's currently configured to launch 'Display Text' actions that say what would have been launched. To customize your sequences, take a look at the walkthrough to see what's going on in the macros.
Basically, you can use what's already there to build your own sequences.
I gotta say that this one is very bulky and requires some manual labor up front when adding new commands – but it does work almost exactly as desired and is pretty nice once up and running.
The main concept here is to assign a macro for every "level". For example, the "caps" command has 4 levels, "pdf" and "ind" have 3 and "⌥a⌥b" has 2. This walkthrough will show how each is encoded in the macro so you can make your own.
Level 1 Setup
The first major thing the macro does is save the current focused application. This is necessary so we can switch somewhere else while a key sequence is inputted – satisfying requirement #1 above:
Then we make the aforementioned switch to the Keyboard Maestro Engine app, which doesn't seem to care that we are focusing it or pressing buttons while focused:
From there you'll hear a sound signaling that you can enter a sequence now. I added this sound because at first I was entering commands too fast and needed some indicator of when to start pressing keys. It's being played asynchronously so we don't have to wait for the sound to end before we can start typing.
Listening for our first keys
After setup, the macro listens for expected keys pressed within a 3 second timeout – requirement #3:
If you click the gear on the 'Pause Until' action above, you'll notice the timeout is set to 3 seconds and both 'Timeout Aborts Macro' and 'Notify on Timeout' are disabled (since the timeout here is a feature and not a bug):
The next section consists of actions specific to each key at level 1.
Here is the start of the 'caps' chain:
The 'InitiatorString' variable is used to keep track of state. When the user eventually finishes entering a command, 'InitiatorString' will be checked.
Then the macro throws execution to the next level, the 'Initiator Trigger L2' macro.
Before we go there, note that there are other keys at this level to understand.
The one that sticks out is the '⌥a' in the '⌥a⌥b' sequence:
Here we want to be able to know if the key and modifier are pressed (requirement #4). This is why I had to use an 'If' action instead of a Switch. The Switch can't check for modifiers unfortunately.
The last key to look for at this level is the 'Escape' key:
No official actions here, but pressing 'Escape' allows the user to abort input without having to wait for a 3 second timeout.
Finally, at the end of the level 1 macro, and all of them, execution runs into this:
'ReturnToInitialApplication' is set to YES by default at the start of every sequence at level 1 (Initiator Trigger L1). Note that if the macro you're calling opens another app and continues there, then you'll want to be sure to set 'ReturnToInitialApplication' to NO right before executing that macro. More on this in the Level 4 section below.
The Next Level
Level 2 is more of the same – just continue adding the next link in whatever sequence you're building.
One difference worth noting is what must appear at every level starting at level 2: a special 'Pause Until' action.
This had to be added after first testing this macro because I noticed that sometimes keypresses were bleeding into the next level. I'd press a 'D' in level X and it would still be observed in level X+1 somehow. To stop it, this 'Pause Until' action will wait for there to be no keypresses.
Unfortunately, this meant hardcoding every key that'll be supported:
all the way to...
Specifically, supported keys are: a-z, 0-9, F1-F19, 'Return', & 'Escape'.
Of course you can add more. I will add symbols and Numpad support at some point, but I got tired of entering each one manually. Just add them at the end of the list. The good news is that you only have to specify them once and then you can copy/paste this 'Pause Until' action at the start of every macro at Level 2 or above.
Notice that you can't add modifier keys here in the 'Pause Until' action. Fortunately, when ⌥ is pressed with a key, let's say 'X', the 'X' registers as being pressed down, so this 'Pause Until' will still work even if modifiers are also being pressed at the moment.
If you want to check for a modifier key, you have to handle this below the 'Pause Until', just as was done in the above example where I showed the start of the ⌥a⌥b sequence.
Level 2 then continues on in the same way that Level 1 does recording the keys pressed and calling the next level.
The other Level 2 difference is that now we are appending onto the 'InitiatorString' variable instead of setting it:
This is the first level that includes a sequence termination and macro call. Ideally, we'd want sequences with 3 characters (like "ind" or "pdf") to terminate on level 3 – but we actually end up needing an extra character to terminate everything. That's why here at level 3 we're terminating a sequence with 2 characters: "⌥a⌥b".
The reason sequences are 1 keypress longer than their character count is because the macro needs to know when we're done sending input. A 'Return' key is then sent to terminate the whole thing.
Otherwise, all sequences would have to be the same length. You couldn't have a sequence of length 2 and a sequence of length 4 without signaling when to stop taking input. They'd all have to stop in the same level, and we want the flexibility of being able to create sequences at variable lengths (requirement #2).
Here's how the "⌥a⌥b" sequence terminates in Level 3:
We respond to the 'Return' keypress and check to see if the 'InitiatorString' matches the known terminating sequence at this level. If it does, we activate the initial application so that the macro will return from the focus on the KM Engine BEFORE executing. This is required if you want the macro to execute in the app you're currently in.
This section has 2 sequences that terminate – "ind" and "pdf":
Notice how "ind" uses 'Activate Initial Application' and "pdf" doesn't. Basically I made the assumption that a macro named 'Export to PDF' would open the Preview app and go there. It's an example of how to make sure the macro takes you to another application instead of the default – which is to return to it.
This is done by setting 'ReturnToInitialApplication' to NO right before calling the macro intended to run at the end of a sequence.
On the other hand, "ind" does return to the initial application first before executing the macro. This is so execution can continue in the intended app.
Finally, the last sequence is terminated in 'Initiator Trigger L5' ("caps"). Nothing special here other than it being the longest sequence. This macros will handle sequences of any length – but you'll have to add another level in the macro chain every time you add a command with a larger sequence. It currently supports sequences with 1-4 characters, which will get you a long way, but it helps to know you can get more.
..and that's it!
Go ahead and edit the macros to create whatever sequences you want!
Each new key needs to be added 2-3 times per each level:
a. In the collection of 'Pause Until key is down' specific to that level
b. In its own 'If key is down'
c. [L2+ only] In the first action entitled 'Pause Until None of the supported keys are down...'. Note that a-z, 0-9, F1-F19, 'Return', & 'Escape' are already added.
'If key is down' contents (before terminating)
a. 'InitiatorString' must be set in L1 with the first letter and then be appended to in L2+
b. Call the next level macro, Initiator Trigger L(X+1)
'If key is down' contents (while terminating)
a. A sequence ends in (character sequence count +1) with the 'Return' key
b. Call any macro from there based on the contents of 'InitiatorString'
c. If you want to return to the app you were in before inputting a sequence, then call the 'Activate Initial Application' macro before calling your sequence ending macro
d. If the macro you call switches applications, then set 'ReturnToInitialApplication' to 'NO' before calling that particular macro
• Some sort of visual component. I'd like to at least see what's been typed so far and if possible see what commands are still available in real-time while typing. Some kind of updating HUD.
• An audio beep after entering an unsupported sequence
Wow, thanks for your kindness! I have to try that, you made a good work there
That's a lot of work right there.
Personally, I think this is overkill and, in removing one problem, presents new ones. My previous suggestion is more than adequate for most needs, I think. However, may I humbly submit two alternative approaches to using strings as a trigger? This is primarily with a view to simplifying the setup.
Pros: No lengthy setup procedure or need to wait before entering the trigger string. Any letter or number combination, of any length, can be used.
Cons: No modifiers allowed. Requires an extra piece of software.
I have opted for the actions to run upon hitting Return, but if preferred this can easily be set as a timeout instead.
You will need this small, free app, KeyboardLocker, which temporarily disables keyboard input, preventing the typed string keystrokes from interfering with the app you're working in.
As in my previous suggestion, trigger using ⇧⌘M, and then type the desired string followed by the Enter Key. If the typed string is found in the Switch/Case group, the related actions will be performed; if not, nothing will happen.
MacroCat String - Merge.kmmacros (27 KB)
NB: to manually exit KeyboardLocker, simply hit ⌘Q.
...and here's an even simpler approach using a prompt. This does require the use of the Enter Key, so may or may not be desirable, depending on personal preference.
MacroCat Prompt - Merge.kmmacros (42 KB)
Definitely more concise. Most users probably won't need the modifier keys anyways.
The KeyboardLocker is a good find, although I was a tad distracted with the 'Keyboard Locker Activated' message. Still usable enough though.
I also don't understand how KM can still listen for keys if Keyboard Locker is active - but hey, it works
I'm guessing it might be because the keystrokes aren't actually coming from the keyboard...?
- Open a palette for one action with a shortcut
- Type the trigger
Eg: „⌃a“ (opens palette), then „a“ or „⌘c“
- Nothing dangerous is transferred to the front app.
- There is a visual help if you have forgotten the trigger
- Shortcuts can be used twice. If the palette is visible ⌘c does not trigger copy
- Triggers are limited to single letters (a, b, c), numbers (1, 2, 3) or shortcuts.
This is the best and most logical solution and is what I would use if I didn't have a Stream Deck. @AIvaro was quite specific about the way he wanted to trigger the macros, so I focused on trying to solve that particular problem, but this is a perfect scenario for using palettes.
Thanks noisneil You're right, it probably doesn't work for Alvaro, but it works for me.
By the way, I just noticed that "special" keys also work as triggers. So Spacebar, fn, Enter, Delete, esc, Arrows.
@Frankb Sorry I've just woken up so my brain isn't working yet... Do you mean that "special keys" work for selecting macros in a palette?
The palette and the macros in the palette
so you could type fn (opens palette) then again fn triggers macro
Sorry, not true. fn opens the palette, then another key is needed, but can be a "special keys"
eg. fn + enter, or a letter, number...
Those keys are probably best avoided for triggering macros directly, as they are commonly used for in-app functions, but in theory any key can be used as a hotkey, yes. The fn key would be an exception, provided you have access to your function keys without it or if the function keys aren't needed.
@noisneil I tried your new approach, it is nice and simple to configure, great idea! However, probably the problem is that this feature should be included in the KM package itself to handle it right with care. I do really appreciate your effort and kindness, but those are some cons I found while using your approach:
- Since it is based on an external tool, it is a bit messy. If you mistake the spelling of your shortcut you have to manually close KeyboardLocker by pressing ⌘Q. Probably a timer could be added to abort the current key sequence and quit KeyboardLocker if 3sec has been elapsed or something like this.
- Pressing enter to validate the input is not very nice according to my taste, (but I understand it has some advantages too). It would be nice if as soon as a string matches the keyboard is unlocked and the macro fired.
- The KeyboardLocker notification is distracting, but this is a minor problem, Im pretty sure this can be fixed or another similar programs can be used for the same purpose.
In conclusion, I think your approach is great, probably the best so far... but it could be even better if KM add this behavior as a new kind of trigger.
I am trying to understand @kraftyDevil script and adapt it to my needs to see how it performs. I also tried BTT and it is nice, but it is not exactly what I expected either.
I think it is nice to be able to share this kind of knowledge, it can be helpful for many people and luckily help to improve KM itself.
Thank you! I will come back to this post soon to add my own conclusions about all suggested methods
eg, this works. fn opens palette, triggers are Enter, Spacebar, esc