Skip to content

Commit

Permalink
feat: add rpt-any (#392)
Browse files Browse the repository at this point in the history
  • Loading branch information
jtroo committed May 20, 2023
1 parent 3a6925b commit c4d4b4f
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 19 deletions.
7 changes: 6 additions & 1 deletion cfg_samples/kanata.kbd
Original file line number Diff line number Diff line change
Expand Up @@ -524,12 +524,17 @@ If you need help, you are welcome to ask.
;; pressed key. Holding down the `rpt` key will not repeatedly send the key.
;; The intended use case is to be able to use a different finger to repeat a
;; double letter, as opposed to double-tapping a letter.
;;
;; The `rpt` action only repeats the last key output. For example, it won't
;; output a chord like `ctrl+c` if the previous key pressed was `C-c` - it
;; will only output `c`. There is a variant `rpt-any` which will repeat the
;; previous action and would work for that use case.
(deflayer misc
_ _ _ _ _ _ _ _ _ @é @è _ ì #|random custom key for testing|# _
_ _ @ab1 _ _ _ ins @{ @} [ ] _ _ +
@cw _ _ _ C-u _ del bspc esc ret _ _ _
@cwc C-z C-x C-c C-v _ _ _ @td @os1 @os2 @os3
rpt _ _ _ _ _ _
rpt rpt-any _ _ _ _ _
)


Expand Down
49 changes: 32 additions & 17 deletions docs/config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -678,23 +678,6 @@ Example specifying multiple config files in the command line:
kanata -c startup.cfg -c 2nd.cfg -c 3rd.cfg
----

[[repeat-key]]
=== Repeat key
<<table-of-contents,Back to ToC>>

The action `+rpt+` repeats the most recently typed key. Holding down this key
will not repeatedly send the key. The intended use case is to be able to use a
different finger or even thumb key to repeat a typed key, as opposed to
double-tapping a key.

.Example:
[source]
----
(deflayer has-repeat
rpt a s d f
)
----

[[layer-switch]]
=== layer-switch
<<table-of-contents,Back to ToC>>
Expand Down Expand Up @@ -828,6 +811,38 @@ These modifiers may be combined together if desired.
)
----

[[repeat-key]]
=== Repeat key
<<table-of-contents,Back to ToC>>

The action `+rpt+` repeats the most recently typed key. Holding down this key
will not repeatedly send the key. The intended use case is to be able to use a
different finger or even thumb key to repeat a typed key, as opposed to
double-tapping a key.

.Example:
[source]
----
(deflayer has-repeat
rpt a s d f
)
----

The `rpt` action only repeats the last key output.
For example, it won't output a chord like `ctrl+c`
if the previous key pressed was `C-c`.
The `rpt` action will only output `c` in this case.

There is a variant `rpt-any`
which will repeat any previous action
and would output `ctrl+c` in the example case.

----
(deflayer has-repeat-any
rpt-any a s d f
)
----

[[release-a-key-or-layer]]
=== Release a key or layer
<<table-of-contents,Back to ToC>>
Expand Down
2 changes: 2 additions & 0 deletions keyberon/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ where
/// the timeout expires, or when the pressed chord uniquely identifies an action (i.e. there are
/// no more keys you could press to change the result).
Chords(&'a ChordsGroup<'a, T>),
/// Repeat the previous action.
Repeat,
/// Fork action that can activate one of two potential actions depending on what keys are
/// currently active.
Fork(&'a ForkConfig<'a, T>),
Expand Down
124 changes: 124 additions & 0 deletions keyberon/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ where
pub last_press_tracker: LastPressTracker,
pub active_sequences: ArrayDeque<[SequenceState<'a, T>; 4], arraydeque::behavior::Wrapping>,
pub action_queue: ActionQueue<'a, T>,
pub prev_action: Option<&'a Action<'a, T>>,
}

/// An event on the key matrix.
Expand Down Expand Up @@ -809,6 +810,7 @@ impl<'a, const C: usize, const R: usize, const L: usize, T: 'a + Copy + std::fmt
last_press_tracker: Default::default(),
active_sequences: ArrayDeque::new(),
action_queue: ArrayDeque::new(),
prev_action: None,
}
}
/// Iterates on the key codes of the current state.
Expand Down Expand Up @@ -1115,8 +1117,16 @@ impl<'a, const C: usize, const R: usize, const L: usize, T: 'a + Copy + std::fmt
self.last_press_tracker.tap_hold_timeout = 0;
}
use Action::*;
if !matches!(action, Repeat | Layer(..) | DefaultLayer(..)) {
self.prev_action = Some(action);
}
match action {
NoOp | Trans => (),
Repeat => {
if let Some(ac) = self.prev_action {
self.do_action(ac, coord, delay, is_oneshot);
}
}
HoldTap(HoldTapAction {
timeout,
hold,
Expand Down Expand Up @@ -1245,6 +1255,15 @@ impl<'a, const C: usize, const R: usize, const L: usize, T: 'a + Copy + std::fmt
for action in *v {
custom.update(self.do_action(action, coord, delay, is_oneshot));
}
// Multi is probably the one action where it is desirable to repeat the top-level
// action instead of the final action. The final action meaning a hold action in
// `tap-hold` or the 3rd tap of a `tap-dance`.
//
// Set the prev_action again since it was probably overwritten by the
// `do_action` recursion.
if !matches!(action, Action::Repeat) {
self.prev_action = Some(action);
}
return custom;
}
Sequence { events } => {
Expand Down Expand Up @@ -3036,4 +3055,109 @@ mod test {
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());
}

#[test]
fn test_repeat() {
static LAYERS: Layers<5, 1, 2> = [
[[
k(A),
MultipleKeyCodes(&[LShift, B].as_slice()),
Repeat,
MultipleActions(&[k(C), k(D)].as_slice()),
Layer(1),
]],
[[
k(E),
MultipleKeyCodes(&[LShift, F].as_slice()),
Repeat,
MultipleActions(&[k(G), k(H)].as_slice()),
Layer(1),
]],
];
let mut layout = Layout::new(&LAYERS);

// Press a key
layout.event(Press(0, 0));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[A], layout.keycodes());
layout.event(Release(0, 0));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());

// Repeat it, should be the same
layout.event(Press(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[A], layout.keycodes());
layout.event(Release(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());

// Press a chord
layout.event(Press(0, 1));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[LShift, B], layout.keycodes());
layout.event(Release(0, 1));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());

// Repeat it, should be the same
layout.event(Press(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[LShift, B], layout.keycodes());
layout.event(Release(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());

// Press a multiple action
layout.event(Press(0, 3));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[C, D], layout.keycodes());
layout.event(Release(0, 3));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());

// Repeat it, should be the same
layout.event(Press(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[C, D], layout.keycodes());
layout.event(Release(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());

// Go to a different layer and press a key
layout.event(Press(0, 4));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());
layout.event(Press(0, 0));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[E], layout.keycodes());
layout.event(Release(0, 4));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[E], layout.keycodes());
layout.event(Release(0, 0));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());

// Repeat, should be the same as the other layer
layout.event(Press(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[E], layout.keycodes());
layout.event(Release(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());

// Activate the layer action and press repeat there, should still be the same action
layout.event(Press(0, 4));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());
layout.event(Press(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[E], layout.keycodes());
layout.event(Release(0, 2));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());
layout.event(Release(0, 4));
assert_eq!(CustomEvent::NoEvent, layout.tick());
assert_keys(&[], layout.keycodes());
}
}
6 changes: 5 additions & 1 deletion src/cfg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -995,11 +995,12 @@ fn parse_action_atom(ac: &Spanned<String>, s: &ParsedState) -> Result<&'static K
s.a.sref(s.a.sref_slice(CustomAction::MouseTap(Btn::Backward))),
)))
}
"rpt" | "repeat" => {
"rpt" | "repeat" | "rpt-key" => {
return Ok(s.a.sref(Action::Custom(
s.a.sref(s.a.sref_slice(CustomAction::Repeat)),
)))
}
"rpt-any" => return Ok(s.a.sref(Action::Repeat)),
"dynamic-macro-record-stop" => {
return Ok(s.a.sref(Action::Custom(
s.a.sref(s.a.sref_slice(CustomAction::DynamicMacroRecordStop(0))),
Expand Down Expand Up @@ -1819,6 +1820,7 @@ fn find_chords_coords(chord_groups: &mut [ChordGroup], coord: (u8, u16), action:
}
Action::NoOp
| Action::Trans
| Action::Repeat
| Action::KeyCode(_)
| Action::MultipleKeyCodes(_)
| Action::Layer(_)
Expand Down Expand Up @@ -1867,6 +1869,7 @@ fn fill_chords(
}
Action::NoOp
| Action::Trans
| Action::Repeat
| Action::KeyCode(_)
| Action::MultipleKeyCodes(_)
| Action::Layer(_)
Expand Down Expand Up @@ -2560,6 +2563,7 @@ fn add_key_output_from_action_to_key_pos(
}
Action::NoOp
| Action::Trans
| Action::Repeat
| Action::Layer(_)
| Action::DefaultLayer(_)
| Action::Sequence { .. }
Expand Down

0 comments on commit c4d4b4f

Please sign in to comment.