TeXitoi / keyberon

A rust crate to create a pure rust keyboard firmware.
MIT License
1.08k stars 79 forks source link

Option to filter keys when using HoldTap mods/layers #35

Closed octol closed 2 years ago

octol commented 3 years ago

Related to #33 it would also be useful to be able to ignore certain keys when using holdtap

This is primarily useful when using holdtap on alphas, such as with homerow mods, where it helps to ignore keys adjacent to mods for reliability. Personally I would like to ignore all keys on the same half of my board so that mods are always pressed with the opposite hand.

Note: there is no built-in support for this in QMK as far as I know, so people implement this manually, such as here

TeXitoi commented 3 years ago

This example looks like a pile of patch to fix something. Not so understandable.

I see the point on having shift active on one side, but I don't really get the idea behind it. Is it something like activate HoldOnOtherKeyPress, but with OtherKeyPress being only a subset of the keys? Do you have personally unintended behavior, and in which case, and with which configuration (using keyberon)?

I must say that I'm quite septic on home row mods, but I admit I didn't tried it, and also maybe my workflow is not really the good case for home row mods (bépo layout making home keys used a lot, emacs making Control + "succession of keys, not really on the same side of the keyboard", ...). I'm looking forward to "combo home row mods", as "index and middle doing shift", mostly to allow layer change + one or several mod activated without some strange contortion of the fingers.

octol commented 3 years ago

This example looks like a pile of patch to fix something. Not so understandable

The linked example or the feature description?

The idea is to make holdtap more reliable by filtering cases where you unintentionally roll keys or when your finger is lagging to release the key. You can probably lower timeouts with something like this, and tweak the other holdtap settings to more aggressively triggers mod/layers, making the typing experience less laggy.

Observing my own typing there are specific combination of keys that are highly over-represented in accidentally triggering mod behaviour. Especially when the mod is on my pinky

bépo layout making home keys used a lot

The motivation for this is to make homerow mods more usable in particular for layouts where the homerow keys are used a lot

TeXitoi commented 3 years ago

The linked example or the feature description?

The linked example. The feature description is quite fuzzy, I don't know what to do if I had to implement it with only this input.

Especially when the mod is on my pinky

Maybe you shouldn't use your pinky as a mod ;-) What's you're configuration? Can you give an example, something like "typing "qs" a lot with my layout, and I have "S" quite frequently, with "q" as shift on hold", but "qs" is always "qs" as "S" is done with the shift on the right pinky home.

Shift must definitively not be on pinky, it's too much used.

Do you use only timing? Or you are experimenting with my branch adding configuration on HoldTap? HoldOnOtherKeyPress fixed my problem with the enter key.

The motivation for this is to make homerow mods more usable in particular for layouts where the homerow keys are used a lot

I use thumb only modifier at the moment, and it feels right for me. I don't even double the mods as, on a 6×3 layout, pressing with the thumb doesn't handicap the others fingers to touch any key. But I have troubles with multi modifiers and layers, especially something like Control arrows, as arrows are on a layer on the same thumb as Control.

octol commented 3 years ago

The linked example. The feature description is quite fuzzy, I don't know what to do if I had to implement it with only this input

In the example it seems the author is using Colemak DH and has E and I next to each other on the home row. The linked line 213 talks about ignoring the right shift which is on E when tapping I, to avoid writing "I" when "ei" was intended.

Those two keys are on the middle and ring finger so I can imagine it pretty common to almost type those together when they are subsequent letters in a word.

Shift must definitively not be on pinky, it's too much used.

Hehe yeah that's from the GergoPlex default keymap, and so far I've been avoiding more centrally placed mods for reliability. But I agree, ideally I'd like to move the mods closer the the centre.

Do you use only timing? Or you are experimenting with my branch adding configuration on HoldTap? HoldOnOtherKeyPress fixed my problem with the enter key.

I can do some testing with your branch come try to come up with some more precise examples

I use thumb only modifier at the moment, and it feels right for me. I don't even double the mods as, on a 6×3 layout, pressing with the thumb doesn't handicap the others fingers to touch any key. But I have troubles with multi modifiers and layers, especially something like Control arrows, as arrows are on a layer on the same thumb as Control.

Agree, on the keyseebee with the 4 thumb keys on each side you don't really need much holdtap at all if you don't want to. But for something like the Miryoku layout I think this feature might help

manna-harbour commented 3 years ago

Here's another example for QMK: https://github.com/manna-harbour/qmk_firmware/blob/bilateral-combinations/docs/tap_hold.md#bilateral-combinations

TeXitoi commented 3 years ago

I put an non_exhaustive in HoldTapConfig to be able to add something for that. I think we can do something like

pub enum HoldTapAction {
    Undecided,
    Hold,
    Tap,
}
pub enum HoldTapConfig {
    // ...
    Custom(fn(&[Event]) -> HoldTapAction),
}

And we will call the custom function at each event before the timeout to allow the function to take the decision itself.

For:

The last mod-tap hold will be converted to the corresponding mod-tap tap if another key on the same hand is tapped during the hold, unless a key on the other hand is tapped first.

the implementation would be, for the left ModTap, with 6 columns per hands:

fn hold_tap_left_config(events: &[Event]) -> HoldTapAction {
    // if the first press event is within the left half, force tap, else let the timing decide
    match event.iter().find(|e| e.is_press()) {
        Some(Event::Press(_, j)) if j < 6 => HoldTapEvent::Tap,
        _ => HoldTapEvent::Undecided,
    }
}

What do you think?

octol commented 3 years ago

What do you think?

I like it! :)