-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Explicit vs implicit "triggers" before/after keymaps are considered/used #100
Comments
I am in full agreement that the solution feels quite delicate. It's more of, "Oh, it's interesting that this actually works," rather than "Yeah, we did that on purpose." Any solution that would make it more robust and explicit would be welcome. As in your example here, the solution could still exist in what would essentially be a "trigger" or "tripwire" keymap, but that could also coincidentally be a useful keymap like the special escape keys keymap, where some keys will still need to do something a little different from the "default" escape action. I still think a good name for the condition would be "noMatch". This kind of exit action of course always has the potential to create a loop where it just keeps happening for every key press if the condition that activates the keymap isn't correctly deactivated by the exit action. I wonder if there is a good way to put a fence around that, or if it just has to be left up to the user to avoid that problem. |
Oh now that's very different... I was imagining generic before/after triggers for the keymap being considered... whether it matches a combo or not is something much, much more specific. In fact with the existing system one can quite easily build before/after callbacks just by wrapping keymaps with much smarter conditionals: smart_keymap("global", { #...
before = lambda _: # ...
after = lambda _: # ... You'd just compile that down to a keymap that inside it's conditional did: # pseudo-code
run(:before)
result = run(:conditional)
run(:after)
return result So I'll probably leave this open for now while we gather more info on what types of things users would actually do with such a trigger system. |
I also think you need to be sure to understand the difference between combo/actions and the triggers... the triggers can change state (toggle a variable), but they don't generate output. You could hack them to do so, but the would purposely not have this functionality - as I don't think output in the middle of the processing pipeline is a good thing. |
I did have trouble getting the tripwire to cause This feels like both the trigger and the "all possible combos" dict would both be necessary to finally perform a default "exit" action. But that would also mean the tripwire keymap would still need to be its own thing, separate from the other escape keymap. By nature the dict would include all the combos from the escape keymap, so it would need to go below the escape keymap.
That sounds pretty interesting. I'll have to think about that. |
But even with a huge dict of all possible combos, there is the fundamental problem that while it would catch all of those combos and have them do "something", it still cannot have that combo go on and do what it would normally do. I ran into this problem with the tab navigation shortcuts I use frequently in several different applications. Shift+Cmd+Braces. Everything else in the escape keymap works fine. Apps are fine with receiving Ctrl+Z/X/C/V/A and keys like Esc/Del/Backspace and so on. They don't need to be changed to something else. But with the tab nav shortcuts I have to transform them to something like Ctrl+PgUp/PgDn for some apps and Shift+Ctrl+Tab/Ctrl+Tab for other apps. So I can't include any combo like that in the escape keymap. The result would be correct in some apps but not in others. Because, as always, the input cannot go on to do what it would have done as input. We can only repeat it as output. |
The condition functions are all evaluated BEFORE the keymap to be used for output is even chosen, so allowing them to generate output themselves is just strange and not part of the overall design flow, hence my calling it a hack.
Why can't the original shortcuts handle this? keymap("General GUI", {
C("Cmd-Tab"): special_mode_clear_then(C("Ctrl-Tab")), It's not super tidy (or modular), but it works. Or you could wrap entire keymaps: special_mode_clear_then(
keymap( ... )
) Which would get be a nicer abstraction for doing the above (wrapping every combo in the map with a wrapper function) |
I'm also considering more of a high-level callback: def do_keymap_enter_and_exits(keymaps):
# check global state and if certain keymaps are not
# present we should fire their "exit" handlers
add_callback("after_decide_keymap", do_keymap_enter_and_exits) Trying to imagine perhap a few super high-level hooks that would allow people to build out more extended functionality via plugins. Except that wouldn't help here, since you need more of an |
This is thinking about it slightly wrong. Technically it could, with a wrapper as in your example. But there are multiple places where the tab nav shortcut (and one or two other closely related tab nav shortcuts) have to exist in the config to map onto different output combos depending on context. And the tab nav shortcuts are just one example of the hundreds of combos that have nothing to do with the special character scheme and have variable transformed output, making them incompatible with the escape keymap. By extension this wrapper function would need to be on every existing combo in the entire config that doesn't already have an exit action attached (like the escape keymap and dead key combos that are part of the special character scheme). Which leads to... special_mode_clear_then(
keymap( ... )
) This is funny because it mirrors something I was pondering, like whether I could just wrap the entire config in a kind of "smart" condition. But this would not work by itself since there are many combos that do not exist anywhere in anyone's config for the simple reason that they don't need to be transformed into something else. Nobody bothers to put combos that already work fine into their config. So even if there were a way to wrap the entire config (right now it would have to be the same conditional wrapper around every individual keymap) it would have to be combined with a function to generate "all possible combos" at the very end of the config file, so that it catches everything that "falls through" the rest of the config without matching anything. But this is the point where it makes more sense to just build a completely customized (Thing with the escape keymap is that the ones at the top that I started with mostly have to do something different from the default, so they will always need to be explicitly defined. I can't entirely get rid of the whole keymap.) It's pretty fascinating at this point that AHK just doesn't seem to have any problem with this sort of re-injection of input as input. I really wonder how they do that so easily. Then again, AHK definitely has its own set of bizarre problems, due to some weird Windows quirks.
Yes. Something like that. |
Right, so you could just wrap all those places. I was pointing out that this is an EASY way to solve that one last problem with your existing setup. You don't need to wrap the world (because you still have the trigger guard). I was just pointing out that it's not so hard to wrap the world. I'm not suggesting any of this is particularly perfect or optimal, just highlighting what is already possible to do. Good point about non-mapped combos...
Who says they do it "easily"? It's not impossible, it's just not the architecture we have right now, and I'm not [yet] convinced it's actually needed either. I think it adds a whole layer of confusion just to solve a small edge case. |
Related: #92 The context is allowing uses to build their own keymaps that mimic nested keymaps, but with slightly different/programmatic behavior.
Potential callbacks per keymap:
Or perhaps just two with context:
context:
match
: booleanCurrently @RedBearAK is using a "trigger keymap" to support their deadkeys config:
First we deal with "escaping" because of normal keypresses, then a fake keymap sets the dead key character to None as the mapper passes over it's conditional to consider if it should be ac active keymap or not. This is all a bit "hacky" and implicit... I'm considering if this should be handled with explicit callbacks:
And of course it's worth mentioning this could all be done inside a single lambda/function as well, though again that's slightly more implicit/hacky... changing state inside a conditional vs it being a "pure" conditional.
Creating this as a discussion topic. I like that we are powerful enough to do this, but I'm not sure we've found the correct abstraction yet - and I worry the current way is going to break in the future when internals change since it's based too much on the internal behavior.
The text was updated successfully, but these errors were encountered: