Skip to content

Commit

Permalink
First-up chord send for keyboard machine
Browse files Browse the repository at this point in the history
  • Loading branch information
user202729 committed Jun 27, 2023
1 parent 9057504 commit eaa6054
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 17 deletions.
34 changes: 31 additions & 3 deletions plover/gui_qt/config_keyboard_widget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@
<rect>
<x>0</x>
<y>0</y>
<width>117</width>
<height>38</height>
<width>159</width>
<height>66</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout">
<item row="1" column="0">
<widget class="QCheckBox" name="first_up_chord_send">
<property name="toolTip">
<string>When the first key in a chord is released, the chord is sent.
If the key is pressed and released again, another chord is sent.</string>
</property>
<property name="text">
<string>First-up chord send</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="arpeggiate">
<property name="sizePolicy">
Expand Down Expand Up @@ -47,8 +58,25 @@
</hint>
</hints>
</connection>
<connection>
<sender>first_up_chord_send</sender>
<signal>clicked(bool)</signal>
<receiver>KeyboardWidget</receiver>
<slot>on_first_up_chord_send_changed(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>79</x>
<y>46</y>
</hint>
<hint type="destinationlabel">
<x>79</x>
<y>32</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>on_arpeggiate_changed(bool)</slot>
<slot>on_first_up_chord_send_changed(bool)</slot>
</slots>
</ui>
5 changes: 5 additions & 0 deletions plover/gui_qt/machine_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,12 @@ def __init__(self):
def setValue(self, value):
self._value = copy(value)
self.arpeggiate.setChecked(value['arpeggiate'])
self.first_up_chord_send.setChecked(value['first_up_chord_send'])

def on_arpeggiate_changed(self, value):
self._value['arpeggiate'] = value
self.valueChanged.emit(self._value)

def on_first_up_chord_send_changed(self, value):
self._value['first_up_chord_send'] = value
self.valueChanged.emit(self._value)
52 changes: 38 additions & 14 deletions plover/machine/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,20 @@ def __init__(self, params):
"""Monitor the keyboard's events."""
super().__init__()
self._arpeggiate = params['arpeggiate']
self._first_up_chord_send = params['first_up_chord_send']
if self._arpeggiate and self._first_up_chord_send:
self._error()
raise RuntimeError("Arpeggiate and first-up chord send cannot both be enabled!")
self._is_suppressed = False
# Currently held keys.
self._down_keys = set()
# All keys part of the stroke.
self._stroke_keys = set()
if self._first_up_chord_send:
# If this is True, the first key in a stroke has already been released
# and subsequent key-up events should not send more strokes
self._chord_already_sent = False
else:
# Collect the keys in the stroke, in case first_up_chord_send is False
self._stroke_keys = set()
self._keyboard_capture = None
self._last_stroke_key_down_count = 0
self._stroke_key_down_count = 0
Expand Down Expand Up @@ -109,31 +118,46 @@ def _key_down(self, key):
assert key is not None
self._stroke_key_down_count += 1
self._down_keys.add(key)
self._stroke_keys.add(key)
if self._first_up_chord_send:
self._chord_already_sent = False
else:
self._stroke_keys.add(key)

def _key_up(self, key):
"""Called when a key is released."""
assert key is not None

self._down_keys.discard(key)
# A stroke is complete if all pressed keys have been released,
# and — when arpeggiate mode is enabled — the arpeggiate key
# is part of it.
if (
self._down_keys or
not self._stroke_keys or
(self._arpeggiate and self._arpeggiate_key not in self._stroke_keys)
):
return

if self._first_up_chord_send:
if self._chord_already_sent:
return
else:
# A stroke is complete if all pressed keys have been released,
# and — when arpeggiate mode is enabled — the arpeggiate key
# is part of it.
if (
self._down_keys or
not self._stroke_keys or
(self._arpeggiate and self._arpeggiate_key not in self._stroke_keys)
):
return

self._last_stroke_key_down_count = self._stroke_key_down_count
steno_keys = {self._bindings.get(k) for k in self._stroke_keys}
if self._first_up_chord_send:
steno_keys = {self._bindings.get(k) for k in self._down_keys | {key}}
self._chord_already_sent = True
else:
steno_keys = {self._bindings.get(k) for k in self._stroke_keys}
self._stroke_keys.clear()
steno_keys -= {None}
if steno_keys:
self._notify(steno_keys)
self._stroke_keys.clear()
self._stroke_key_down_count = 0

@classmethod
def get_option_info(cls):
return {
'arpeggiate': (False, boolean),
'first_up_chord_send': (False, boolean),
}

0 comments on commit eaa6054

Please sign in to comment.