Skip to content
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

feat(combos): add partial combo holds and slow release positions #1809

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

theol0403
Copy link
Contributor

@theol0403 theol0403 commented May 22, 2023

This PR contains two enhancements for combos. While the usecase is relatively niche, I hope I can convey the power of these features.

New properties:

Partial Holds

When a combo is pressed, and then one key is released, adding partial-hold-position to the combos bindings array allows you to control what happens to the remaining keys that are being held.

Slow Release Positions

slow-release-positions is an array that lets you precisely control the conditions to maintain slow-release.

Documentation

Slow Release

If you want the combo binding to be released when all positions are released, instead of when any position is released, enable slow-release. This is useful for combos that are used to toggle a layer, for example.

However, you may want to continue to hold the combo when one position is held but not the other. For example, if the keys corresponding to the combo positions 0 and 1 are &mo NAV and &kp A, and the combo behavior is &kp LEFT, you may want to continue holding LEFT while you hold A and release NAV, but not if you hold NAV and release A. To solve this, you can specify slow-release-positions to select which keys must be held to maintain slow-release. In this example, you would specify slow-release-positions = <1>. In other words, the combo will be held as long all keys in slow-release-positions are held, and released when any key in slow-release-positions is released.

Partial Holds

After pressing a combo, you may want to specify the behavior that is activated when the combo is partially released. For example, if the keys corresponding to the combo positions 0, 1, and 2 are &tog NAV, &kp A, and &kp LSFT and the combo behavior is &kp LEFT, you may want to activate &mo NAV when you release A or LSFT but continue to hold NAV, or activate LSFT when you release NAV or A but continue to hold LSFT.

To do this, you can add partial-hold-position to the bindings array, optionally along with associated behaviors. When no explicit behavior is specified, it will default to the behavior belonging to the key position.

In this example, you would specify:

combo_nav {
    timeout-ms = <50>;
    key-positions = <0 1 2>;
    bindings
        = <&kp LEFT>
        , <&partial-hold-position 0 &mo NAV>
        , <&partial-hold-position 2> // defaults to &kp LSFT
        ;
};

Partial Hold with Slow Release Positions

Partial holds compliment slow-release-positions, by letting you control what happens when a combo is partially released. The best motivating example is a combo that is used to "accelerate" an existing thumb momentary layer with a key on that layer, allowing you to mash the keys together at the same time. The following layout is an example of this:

#define NAV 1
/ {
    combos {
        compatible = "zmk,combos";
        combo_nav {
            timeout-ms = <50>;
            key-positions = <0 1>;
            bindings = <&kp LEFT>, <&partial-hold-position 0>;
            slow-release;
            slow-release-positions = <1>;
        };
    };

    keymap {
        compatible = "zmk,keymap";

        default_layer {
            bindings = <
                &mo NAV &kp A &kp B
            >;
        };

        nav_layer {
            bindings = <
                &mo NAV &kp LEFT &kp RIGHT
            >;
        };
    };
};

In this example, you can press LEFT by pressing NAV, pressing LEFT, then releasing NAV. However, this requires a slight pause to ensure NAV was pressed before LEFT, so that A isn't pressed instead. What if you wanted to be able to mash the keys together at the same time, and achieve a consistent result? This is what the combo does — it allows you to press LEFT up to 50ms before NAV and still activate LEFT.

However, the introduction of the combo means that you can no longer release LEFT while holding NAV then press RIGHT. This is because the combo will be released when LEFT is released, and B will be pressed instead. To solve this, you can use partial-hold-position to specify that NAV should be pressed when LEFT is released. Finally, you can use slow-release-positions to specify that the combo should be held as long as LEFT is held, allowing you to use key-repeat while holding LEFT but releasing NAV.

Discussion

Before I write tests, I would love to hear feedback on the concept and implementation. There were a few partial-hold implementations I tried, including having a partial-hold-positions array. However, that implementation did not allow setting a custom behavior to the position. Also, I'm open to suggestions for naming - I also considered "partial combo releases".

@caksoylar caksoylar added enhancement New feature or request combos labels May 30, 2023
@theol0403
Copy link
Contributor Author

For those interested, I finished the write-up for my layout which makes heavy use of this PR.

https://github.com/theol0403/anachron-zmk-config

@theol0403
Copy link
Contributor Author

Bump - other than me adding tests, this is ready to go. I don't see the benefit of any code changes besides maintainer comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
combos enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants