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

Leader key improvements? #586

Closed
callum-oakley opened this issue Jul 29, 2016 · 17 comments
Closed

Leader key improvements? #586

callum-oakley opened this issue Jul 29, 2016 · 17 comments

Comments

@callum-oakley
Copy link
Contributor

I was just toying with the idea of using a leader key to enter symbols. So that instead of moving to another layer you could have a list of two or three letter codes for each symbol and instead type something like Hello LEADatWorldLEADex to produce something like Hello @World! I reckon given how much typing symbols slows me down, this could potentially be faster than the conventional stick them on another layer approach. Worth exploring for the fun of it anyway.

So, I tried to implement this using the current leader key functionality and ran in to some issues. Ideally you want this to interrupt the normal flow of typing as little as possible, so that you just type your symbol code as fast as possible and then immediately continue --- however the fact that there is a fixed timeout produces all sorts of problems whereby you end up actually getting Hello W@orld! or some similar variation. I haven't dug in to the code myself yet to see how the leader key is implemented, but would it be possible instead to have a leader key that works in roughly the following way:

  • leader key is hit, register we're in leader mode now
  • every subsequent keypress checks against the dictionary and
    • if any full code is matched, the corresponding output is immediately sent, and leader mode is terminated, without waiting for a timeout.
    • if no partial codes are matched, leader mode is terminated and the unmatched string is sent.

In fact with the above scheme a timeout wouldn't be needed at all.

@algernon
Copy link
Contributor

You need a timeout for the case where you input LEADER x, and want something to happen without further keypresses, yet, also allow LEADER x y to do something else.

@callum-oakley
Copy link
Contributor Author

True, but that functionality comes at the expense of the use case outlined above. Is there room for two different leader key behaviours? I think having all of your leader sequences being prefix codes would be a small price to pay for the extra responsiveness and removal of a timeout!

@callum-oakley
Copy link
Contributor Author

I think what I'm looking for is more like a customisable, firmware compose key. I believe compose keys generally fire as soon as the input matches a given string.

@callum-oakley
Copy link
Contributor Author

callum-oakley commented Jul 29, 2016

I've implemented a proof of concept KC_COMP compose key that works in a similar manner to KC_LEAD, but checks for matching strings every keystroke and exits compose mode if a match is found. It still has a timeout purely because that seems to be the easiest way to deal with entering an erroneous code.

see: https://github.com/hot-leaf-juice/qmk_firmware/tree/compose_experiment

currently using this as follows and working as expected:

void matrix_scan_user(void) {
    COMPOSE_DICTIONARY() {
        SEQ_ONE_KEY(KC_SPC) {
            STOP_COMPOSING();
            register_code(KC_ENT);
            unregister_code(KC_ENT);
        }

        SEQ_TWO_KEYS(KC_A, KC_T) {
            STOP_COMPOSING();
            register_code(KC_LSFT);
            register_code(KC_2);
            unregister_code(KC_2);
            unregister_code(KC_LSFT);
        }

        if (timer_elapsed(compose_time) > COMPOSE_TIMEOUT) {
            STOP_COMPOSING();
        }
    }
}

@callum-oakley
Copy link
Contributor Author

callum-oakley commented Jul 29, 2016

Having spent the last couple of hours trying to write code while inputting all my symbols as compose sequences I've decided that it probably isn't faster than using layers... so I'm happy to close this issue and leave it at that...

The compose key I've implemented totally works though so if anyone thinks it offers sufficiently different behaviour from the existing leader key I'm happy to polish up the code and write some documentation and submit a pull request?

edit: I'll close the issue tomorrow if nobody has expressed an interest

@algernon
Copy link
Contributor

Perhaps it could be rolled into LEADER, with the two behaviours switchable at compile time? I have a few leader sequences, and wouldn't mind trying your compose. Not sure if I want it, or if I'd like it, but I'd try.

@fredizzimo
Copy link
Contributor

Technically, I think both could be supported at the same time without any compiler options. But it would come at the cost of implementation difficulty.

If we know that a character is the final character in the sequence, it could terminate immediately without waiting and I'm pretty sure that's how VIM do it. But this would of course require knowledge of all sequences up-front. I haven't looked at the implementation but from the examples that I have seen the current implementation doesn't seem to support that idea though.

@callum-oakley
Copy link
Contributor Author

The current implementation of LEADER indeed doesn't support that idea. The implementation of COMPOSE works on the assumption that no sequence is a prefix of any other but does terminate immediately upon finishing a sequence. Although I have abandoned my original use case, I can confirm that it really adds a feeling of responsiveness to using sequences. It's really nice to get your output registered the moment you push the final key of the sequence.

I really like the idea of trying to roll both together, though that would certainly add to the complexity of the solution, since at the moment neither solution checks the list of sequences as a whole, they both simply scan through each in turn.

@hia3
Copy link

hia3 commented Jul 29, 2016

This is exactly what I was thinking - LEADER without timeouts. But then I've realized I want my letter codes be visible as I type them, and then I press LEADER (or rather "follower") button. I just store last N characters when process_record_user is called, then find longest match.

Speaking about symbols, I just tap b twice to get @.

@callum-oakley
Copy link
Contributor Author

callum-oakley commented Jul 29, 2016

I have rolled the compose functionality in to the leader key. I can't help thinking my solution is a bit of a hack so someone who has used more C than me might want to tidy it up or propose an entirely different way of doing it. However, the head of https://github.com/hot-leaf-juice/qmk_firmware/tree/compose_experiment now includes the kind of solution @fredizzimo was talking about, namely

  • if a sequence is matched, and it isn't a prefix of some other sequence, then the command for that sequence is sent immediately and leader mode terminated.
  • if a sequence is matched, and it is a prefix of some other sequence, then we wait for the timeout to see if any more letters are typed.
  • if the timeout is reached, we check to see if any sequences match in the same way the leader key used to work.

Here's what it looks like on a user's end:

LEADER_EXTERNS();

void matrix_scan_user(void) {
    LEADER_DICTIONARY() {
        partial_match = false;

        REGISTER_SEQUENCES() {
            REGISTER_ONE_KEY(KC_SPC);
            REGISTER_TWO_KEYS(KC_E, KC_X);
            REGISTER_TWO_KEYS(KC_A, KC_A);
            REGISTER_THREE_KEYS(KC_A, KC_A, KC_A);
        }

        DESCRIBE_SEQUENCE_BEHAVIOUR() {
            DESCRIBE_ONE_KEY(KC_SPC) {
                STOP_LEADING();
                register_code(KC_ENT);
                unregister_code(KC_ENT);
            }

            DESCRIBE_TWO_KEYS(KC_E, KC_X) {
                STOP_LEADING();
                register_code(KC_LSFT);
                register_code(KC_1);
                unregister_code(KC_1);
                unregister_code(KC_LSFT);
            }

            DESCRIBE_TWO_KEYS(KC_A, KC_A) {
                STOP_LEADING();
                register_code(KC_T);
                unregister_code(KC_T);
                register_code(KC_W);
                unregister_code(KC_W);
                register_code(KC_O);
                unregister_code(KC_O);
            }

            DESCRIBE_THREE_KEYS(KC_A, KC_A, KC_A) {
                STOP_LEADING();
                register_code(KC_T);
                unregister_code(KC_T);
                register_code(KC_H);
                unregister_code(KC_H);
                register_code(KC_R);
                unregister_code(KC_R);
                register_code(KC_E);
                unregister_code(KC_E);
                register_code(KC_E);
                unregister_code(KC_E);
            }
        }

        if (timer_elapsed(leader_time) > LEADER_TIMEOUT) {
            STOP_LEADING();
        }
    }
}

It should be noted that this is a breaking change, I don't know what the deal is with this kind of thing.

@ezuk
Copy link
Contributor

ezuk commented Jul 30, 2016

yeah, having it as a breaking change is one reason why I prefer to have it as KC_COMP. This implementatin is nice (the concept, the algo) but breaking changes aren't so awesome as then other people need to fix up their keymaps...

@callum-oakley
Copy link
Contributor Author

callum-oakley commented Jul 30, 2016

How about implementing KC_COMP using the "best of both worlds" approach above and then depricating KC_LEAD? (also, unrelatedly, I just noticed that the user's end can be made slightly prettier by absorbing the partial_match = false; in to the beginning of REGISTER_SEQUENCES())

@patrickwelker
Copy link

I really like the KC_COMP concept. The last status update is ~6 months old. How's the attitude toward implementing a compose key nowadays? (just checking)

@callum-oakley
Copy link
Contributor Author

Hi @pattulus, sorry for the delay. I've largely lost interest in the concept since it's not something I would currently make use of. That being said I believe the majority of the work is done – iirc my implementation pretty much worked, and just needs renaming to make it not a breaking change and some stress testing and cleaning up of course.

@skullydazed
Copy link
Member

I'm going to close this issue as we're trying to clean up our open issues. It does seem like an interesting concept and if someone wants to polish it up and present a PR we'd love to accept it.

@bentomas
Copy link

Just want to say that I'd really love the features discussed here. As it exists now KC_LEAD doesn't really work for me.

I can look into cleaning it up but I don't know any C, so it's a bit tricky for me to figure out.

BlueTufa pushed a commit to BlueTufa/qmk_firmware that referenced this issue Aug 6, 2021
* upgrade prettier

* Delete useless dependencies
@tennysontbardwell
Copy link

I'd like to reopen this and take it to the finish line. Pending tentative buy-in, I'd write and submit a pull request.

I propose we offer a new hook LEADER_SEQUNCE_CHANGED. Users could then match the sequence of keycodes as the user types, and manually terminate the leader mode vis leading = false. It would be up to users to avoid unreachable sequences such as KC_LEAD a and LC_LEAD a b both bound.

almaqyn pushed a commit to almaqyn/qmk_firmware that referenced this issue Nov 23, 2023
* Add piantor

* Add Piantor Vial and EstarDyn clone support

Rebased Vial config from [here](beekeeb/vial-qmk-piantor@36c0fa9#diff-50b215a89a3057054d008a63d5be69aee88134514ead8b432ab246662cc8fe53)
produced by Beekeeb, and added support for EstarDyn Pico clones which each have a WS2812 LED

---------

Co-authored-by: Leo Lou <louyuhong@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants