Merge branch 'tap-dance' into next

main
Ilya Zhuravlev 2021-07-03 22:28:57 -04:00
commit b777d861d7
9 changed files with 420 additions and 61 deletions

View File

@ -0,0 +1,61 @@
from PyQt5.QtCore import pyqtSignal
from any_keycode_dialog import AnyKeycodeDialog
from keyboard_widget import KeyboardWidget
from kle_serial import Key
from tabbed_keycodes import TabbedKeycodes
from util import KeycodeDisplay
class KeyWidget(KeyboardWidget):
changed = pyqtSignal()
def __init__(self):
super().__init__(None)
self.padding = 1
self.keycode = 0
key = Key()
key.row = key.col = 0
key.layout_index = key.layout_option = -1
self.set_keys([key], [])
self.anykey.connect(self.on_anykey)
def mousePressEvent(self, ev):
super().mousePressEvent(ev)
if self.active_key is not None:
TabbedKeycodes.open_tray(self)
else:
TabbedKeycodes.close_tray()
def mouseReleaseEvent(self, ev):
ev.accept()
def on_keycode_changed(self, keycode):
""" Unlike set_keycode, this handles setting masked keycode inside the mask """
if self.active_mask:
if keycode > 0xFF:
return
keycode = (self.keycode & 0xFF00) | keycode
self.set_keycode(keycode)
def on_anykey(self):
if self.active_key is None:
return
dlg = AnyKeycodeDialog(self.keycode)
if dlg.exec_() and dlg.value >= 0:
self.set_keycode(dlg.value)
def set_keycode(self, kc):
if kc == self.keycode:
return
self.keycode = kc
KeycodeDisplay.display_keycode(self.widgets[0], self.keycode)
self.update()
self.changed.emit()

View File

@ -57,6 +57,12 @@ CMD_VIAL_QMK_SETTINGS_GET = 0x0A
CMD_VIAL_QMK_SETTINGS_SET = 0x0B
CMD_VIAL_QMK_SETTINGS_RESET = 0x0C
CMD_VIAL_DYNAMIC_ENTRY_OP = 0x0D
DYNAMIC_VIAL_GET_NUMBER_OF_ENTRIES = 0x00
DYNAMIC_VIAL_TAP_DANCE_GET = 0x01
DYNAMIC_VIAL_TAP_DANCE_SET = 0x02
# how much of a macro/keymap buffer we can read/write per packet
BUFFER_FETCH_CHUNK = 28
@ -207,6 +213,7 @@ class Keyboard:
self.reload_keymap()
self.reload_macros()
self.reload_rgb()
self.reload_dynamic()
def reload_layers(self):
""" Get how many layers the keyboard has """
@ -374,6 +381,22 @@ class Keyboard:
self.backlight_effect = self.usb_send(
self.dev, struct.pack(">BB", CMD_VIA_LIGHTING_GET_VALUE, QMK_BACKLIGHT_EFFECT), retries=20)[2]
def reload_dynamic(self):
if self.vial_protocol < 4:
self.tap_dance_count = 0
self.tap_dance_entries = []
return
data = self.usb_send(self.dev, struct.pack("BBB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_DYNAMIC_ENTRY_OP,
DYNAMIC_VIAL_GET_NUMBER_OF_ENTRIES), retries=20)
self.tap_dance_count = data[0]
self.tap_dance_entries = []
for x in range(self.tap_dance_count):
data = self.usb_send(self.dev, struct.pack("BBBB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_DYNAMIC_ENTRY_OP,
DYNAMIC_VIAL_TAP_DANCE_GET, x), retries=20)
if data[0] != 0:
raise RuntimeError("failed retrieving tapdance entry {} from the device".format(x))
self.tap_dance_entries.append(struct.unpack("<HHHHH", data[1:11]))
def set_key(self, layer, row, col, code):
if code < 0:
return
@ -477,6 +500,7 @@ class Keyboard:
data["macro"] = self.save_macro()
data["vial_protocol"] = self.vial_protocol
data["via_protocol"] = self.via_protocol
data["tap_dance"] = self.tap_dance_entries
return json.dumps(data).encode("utf-8")
@ -508,6 +532,10 @@ class Keyboard:
self.set_layout_options(data["layout_options"])
self.restore_macros(data.get("macro"))
for x, e in enumerate(data.get("tap_dance", [])):
if x < self.tap_dance_count:
self.tap_dance_set(x, e)
def restore_macros(self, macros):
if not isinstance(macros, list):
return
@ -670,6 +698,17 @@ class Keyboard:
def qmk_settings_reset(self):
self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_QMK_SETTINGS_RESET))
def tap_dance_get(self, idx):
return self.tap_dance_entries[idx]
def tap_dance_set(self, idx, entry):
if self.tap_dance_entries[idx] == entry:
return
self.tap_dance_entries[idx] = entry
serialized = struct.pack("<HHHHH", *self.tap_dance_entries[idx])
self.usb_send(self.dev, struct.pack("BBBB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_DYNAMIC_ENTRY_OP,
DYNAMIC_VIAL_TAP_DANCE_SET, idx) + serialized, retries=20)
class DummyKeyboard(Keyboard):

View File

@ -4,7 +4,8 @@ from PyQt5.QtGui import QPainter, QColor, QPainterPath, QTransform, QBrush, QPol
from PyQt5.QtWidgets import QWidget, QToolTip, QApplication
from PyQt5.QtCore import Qt, QSize, QRect, QPointF, pyqtSignal, QEvent, QRectF
from constants import KEY_SIZE_RATIO, KEY_SPACING_RATIO, KEYBOARD_WIDGET_PADDING, KEYBOARD_WIDGET_MASK_PADDING, KEYBOARD_WIDGET_MASK_HEIGHT, KEY_ROUNDNESS
from constants import KEY_SIZE_RATIO, KEY_SPACING_RATIO, KEYBOARD_WIDGET_PADDING, KEYBOARD_WIDGET_MASK_PADDING,\
KEYBOARD_WIDGET_MASK_HEIGHT, KEY_ROUNDNESS
class KeyWidget:
@ -166,6 +167,7 @@ class KeyboardWidget(QWidget):
self.enabled = True
self.scale = 1
self.padding = KEYBOARD_WIDGET_PADDING
self.setMouseTracking(True)
@ -215,7 +217,7 @@ class KeyboardWidget(QWidget):
# place common widgets, that is, ones which are always displayed and require no extra transforms
for widget in self.common_widgets:
widget.update_position(scale_factor, -top_x + KEYBOARD_WIDGET_PADDING, -top_y + KEYBOARD_WIDGET_PADDING)
widget.update_position(scale_factor, -top_x + self.padding, -top_y + self.padding)
self.widgets.append(widget)
# top-left position for specific layout
@ -236,7 +238,7 @@ class KeyboardWidget(QWidget):
if opt == self.layout_editor.get_choice(idx):
shift_x = layout_x[idx][opt] - layout_x[idx][0]
shift_y = layout_y[idx][opt] - layout_y[idx][0]
widget.update_position(scale_factor, -shift_x - top_x + KEYBOARD_WIDGET_PADDING, -shift_y - top_y + KEYBOARD_WIDGET_PADDING)
widget.update_position(scale_factor, -shift_x - top_x + self.padding, -shift_y - top_y + self.padding)
self.widgets.append(widget)
def update_layout(self):
@ -255,8 +257,8 @@ class KeyboardWidget(QWidget):
max_w = max(max_w, p.x() * self.scale)
max_h = max(max_h, p.y() * self.scale)
self.width = max_w + 2 * KEYBOARD_WIDGET_PADDING
self.height = max_h + 2 * KEYBOARD_WIDGET_PADDING
self.width = max_w + 2 * self.padding
self.height = max_h + 2 * self.padding
self.update()
self.updateGeometry()

View File

@ -524,6 +524,8 @@ KEYCODES_MEDIA = [
K(132, "KC_LSCR", "Locking\nScroll", "Locking Scroll Lock", alias=["KC_LOCKING_SCROLL"]),
]
KEYCODES_TAP_DANCE = []
KEYCODES_USER = []
KEYCODES_MACRO = []
@ -544,8 +546,8 @@ def recreate_keycodes():
KEYCODES.clear()
KEYCODES.extend(KEYCODES_SPECIAL + KEYCODES_BASIC + KEYCODES_SHIFTED + KEYCODES_ISO + KEYCODES_LAYERS +
KEYCODES_QUANTUM + KEYCODES_BACKLIGHT + KEYCODES_MEDIA + KEYCODES_MACRO + KEYCODES_USER +
KEYCODES_HIDDEN)
KEYCODES_QUANTUM + KEYCODES_BACKLIGHT + KEYCODES_MEDIA + KEYCODES_TAP_DANCE + KEYCODES_MACRO +
KEYCODES_USER + KEYCODES_HIDDEN)
def create_user_keycodes():
@ -608,6 +610,10 @@ def recreate_keyboard_keycodes(keyboard):
lbl = "M{}".format(x)
KEYCODES_MACRO.append(Keycode(0x5F12 + x, lbl, lbl))
for x in range(keyboard.tap_dance_count):
lbl = "TD({})".format(x)
KEYCODES_TAP_DANCE.append(Keycode(QK_TAP_DANCE | x, lbl, lbl))
# Check if custom keycodes are defined in keyboard, and if so add them to user keycodes
if keyboard.custom_keycodes is not None and len(keyboard.custom_keycodes) > 0:
create_custom_user_keycodes(keyboard.custom_keycodes)

View File

@ -1,8 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import json
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QMessageBox, QApplication
from PyQt5.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QMessageBox
from PyQt5.QtCore import Qt
from any_keycode_dialog import AnyKeycodeDialog
@ -12,7 +11,7 @@ from keycodes import recreate_keyboard_keycodes, Keycode
from keymaps import KEYMAPS
from square_button import SquareButton
from tabbed_keycodes import TabbedKeycodes
from util import tr
from util import tr, KeycodeDisplay
from vial_device import VialKeyboard
@ -48,8 +47,6 @@ class KeymapEditor(BasicEditor):
layout_editor.changed.connect(self.on_layout_changed)
self.keymap_override = KEYMAPS[0][1]
self.container.anykey.connect(self.on_any_keycode)
self.tabbed_keycodes = TabbedKeycodes()
@ -60,6 +57,7 @@ class KeymapEditor(BasicEditor):
self.addWidget(self.tabbed_keycodes)
self.device = None
KeycodeDisplay.notify_keymap_override(self)
def on_container_clicked(self):
""" Called when a mouse click event is bubbled up to the editor's container """
@ -115,6 +113,7 @@ class KeymapEditor(BasicEditor):
recreate_keyboard_keycodes(self.keyboard)
self.tabbed_keycodes.recreate_keycode_buttons()
TabbedKeycodes.tray.recreate_keycode_buttons()
self.refresh_layer_display()
self.container.setEnabled(self.valid())
@ -135,11 +134,6 @@ class KeymapEditor(BasicEditor):
self.keyboard.restore_layout(data)
self.refresh_layer_display()
def set_keymap_override(self, override):
self.keymap_override = override
self.refresh_layer_display()
self.tabbed_keycodes.set_keymap_override(override)
def on_any_keycode(self):
if self.container.active_key is None:
return
@ -148,17 +142,6 @@ class KeymapEditor(BasicEditor):
if dlg.exec_() and dlg.value >= 0:
self.on_keycode_changed(dlg.value)
def code_is_overriden(self, code):
""" Check whether a country-specific keymap overrides a code """
key = Keycode.find_outer_keycode(code)
return key is not None and key.qmk_id in self.keymap_override
def get_label(self, code):
""" Get label for a specific keycode """
if self.code_is_overriden(code):
return self.keymap_override[Keycode.find_outer_keycode(code).qmk_id]
return Keycode.label(code)
def code_for_widget(self, widget):
if widget.desc.row is not None:
return self.keyboard.layout[(self.current_layer, widget.desc.row, widget.desc.col)]
@ -177,20 +160,7 @@ class KeymapEditor(BasicEditor):
for widget in self.container.widgets:
code = self.code_for_widget(widget)
text = self.get_label(code)
tooltip = Keycode.tooltip(code)
mask = Keycode.is_mask(code)
mask_text = self.get_label(code & 0xFF)
if mask:
text = text.split("\n")[0]
widget.masked = mask
widget.setText(text)
widget.setMaskText(mask_text)
widget.setToolTip(tooltip)
if self.code_is_overriden(code):
widget.setColor(QApplication.palette().color(QPalette.Link))
else:
widget.setColor(None)
KeycodeDisplay.display_keycode(widget, code)
self.container.update()
self.container.updateGeometry()
@ -245,3 +215,6 @@ class KeymapEditor(BasicEditor):
self.refresh_layer_display()
self.keyboard.set_layout_options(self.layout_editor.pack())
def on_keymap_override(self):
self.refresh_layer_display()

View File

@ -18,8 +18,10 @@ from layout_editor import LayoutEditor
from macro_recorder import MacroRecorder
from qmk_settings import QmkSettings
from rgb_configurator import RGBConfigurator
from tabbed_keycodes import TabbedKeycodes
from tap_dance import TapDance
from unlocker import Unlocker
from util import tr, find_vial_devices, EXAMPLE_KEYBOARDS
from util import tr, find_vial_devices, EXAMPLE_KEYBOARDS, KeycodeDisplay
from vial_device import VialKeyboard
from matrix_test import MatrixTest
@ -58,12 +60,14 @@ class MainWindow(QMainWindow):
self.keymap_editor = KeymapEditor(self.layout_editor)
self.firmware_flasher = FirmwareFlasher(self)
self.macro_recorder = MacroRecorder()
self.tap_dance = TapDance()
self.qmk_settings = QmkSettings(self.appctx)
self.matrix_tester = MatrixTest(self.layout_editor)
self.rgb_configurator = RGBConfigurator()
self.editors = [(self.keymap_editor, "Keymap"), (self.layout_editor, "Layout"), (self.macro_recorder, "Macros"),
(self.rgb_configurator, "Lighting"), (self.qmk_settings, "QMK Settings"),
(self.rgb_configurator, "Lighting"), (self.tap_dance, "Tap Dance"),
(self.qmk_settings, "QMK Settings"),
(self.matrix_tester, "Matrix tester"), (self.firmware_flasher, "Firmware updater")]
Unlocker.global_layout_editor = self.layout_editor
@ -88,6 +92,10 @@ class MainWindow(QMainWindow):
layout.addWidget(self.tabs)
layout.addWidget(self.lbl_no_devices)
layout.setAlignment(self.lbl_no_devices, Qt.AlignHCenter)
self.tray_keycodes = TabbedKeycodes()
self.tray_keycodes.make_tray()
layout.addWidget(self.tray_keycodes)
self.tray_keycodes.hide()
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
@ -257,7 +265,7 @@ class MainWindow(QMainWindow):
self.current_device.keyboard.reload()
for e in [self.layout_editor, self.keymap_editor, self.firmware_flasher, self.macro_recorder,
self.qmk_settings, self.matrix_tester, self.rgb_configurator]:
self.tap_dance, self.qmk_settings, self.matrix_tester, self.rgb_configurator]:
e.rebuild(self.current_device)
def refresh_tabs(self):
@ -327,7 +335,7 @@ class MainWindow(QMainWindow):
def change_keyboard_layout(self, index):
self.settings.setValue("keymap", KEYMAPS[index][0])
self.keymap_editor.set_keymap_override(KEYMAPS[index][1])
KeycodeDisplay.set_keymap_override(KEYMAPS[index][1])
def get_theme(self):
return self.settings.value("theme", "Dark")
@ -340,6 +348,7 @@ class MainWindow(QMainWindow):
msg.exec_()
def on_tab_changed(self, index):
TabbedKeycodes.close_tray()
old_tab = self.current_tab
new_tab = None
if index >= 0:

View File

@ -7,10 +7,9 @@ from PyQt5.QtGui import QPalette
from constants import KEYCODE_BTN_RATIO
from flowlayout import FlowLayout
from keycodes import KEYCODES_BASIC, KEYCODES_ISO, KEYCODES_MACRO, KEYCODES_LAYERS, KEYCODES_QUANTUM, \
KEYCODES_BACKLIGHT, KEYCODES_MEDIA, KEYCODES_SPECIAL, KEYCODES_SHIFTED, KEYCODES_USER, Keycode
from keymaps import KEYMAPS
KEYCODES_BACKLIGHT, KEYCODES_MEDIA, KEYCODES_SPECIAL, KEYCODES_SHIFTED, KEYCODES_USER, Keycode, KEYCODES_TAP_DANCE
from square_button import SquareButton
from util import tr
from util import tr, KeycodeDisplay
class TabbedKeycodes(QTabWidget):
@ -21,7 +20,8 @@ class TabbedKeycodes(QTabWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.keymap_override = None
self.target = None
self.is_tray = False
self.tab_basic = QScrollArea()
self.tab_iso = QScrollArea()
@ -29,6 +29,7 @@ class TabbedKeycodes(QTabWidget):
self.tab_quantum = QScrollArea()
self.tab_backlight = QScrollArea()
self.tab_media = QScrollArea()
self.tab_tap_dance = QScrollArea()
self.tab_user = QScrollArea()
self.tab_macro = QScrollArea()
@ -41,12 +42,15 @@ class TabbedKeycodes(QTabWidget):
(self.tab_quantum, "Quantum", KEYCODES_QUANTUM),
(self.tab_backlight, "Backlight", KEYCODES_BACKLIGHT),
(self.tab_media, "App, Media and Mouse", KEYCODES_MEDIA),
(self.tab_tap_dance, "Tap Dance", KEYCODES_TAP_DANCE),
(self.tab_user, "User", KEYCODES_USER),
(self.tab_macro, "Macro", KEYCODES_MACRO),
]:
layout = FlowLayout()
if tab == self.tab_layers:
self.layout_layers = layout
elif tab == self.tab_tap_dance:
self.layout_tap_dance = layout
elif tab == self.tab_macro:
self.layout_macro = layout
elif tab == self.tab_user:
@ -71,16 +75,17 @@ class TabbedKeycodes(QTabWidget):
self.addTab(tab, tr("TabbedKeycodes", label))
self.layer_keycode_buttons = []
self.tap_dance_keycode_buttons = []
self.macro_keycode_buttons = []
self.user_keycode_buttons = []
self.set_keymap_override(KEYMAPS[0][1])
KeycodeDisplay.notify_keymap_override(self)
def create_buttons(self, layout, keycodes, wordWrap = False):
def create_buttons(self, layout, keycodes, word_wrap=False):
buttons = []
for keycode in keycodes:
btn = SquareButton()
btn.setWordWrap(wordWrap)
btn.setWordWrap(word_wrap)
btn.setRelSize(KEYCODE_BTN_RATIO)
btn.setToolTip(Keycode.tooltip(keycode.code))
btn.clicked.connect(lambda st, k=keycode: self.keycode_changed.emit(k.code))
@ -91,28 +96,63 @@ class TabbedKeycodes(QTabWidget):
return buttons
def recreate_keycode_buttons(self):
for btn in self.layer_keycode_buttons + self.macro_keycode_buttons + self.user_keycode_buttons:
for btn in self.layer_keycode_buttons + self.tap_dance_keycode_buttons + self.macro_keycode_buttons \
+ self.user_keycode_buttons:
self.widgets.remove(btn)
btn.hide()
btn.deleteLater()
self.layer_keycode_buttons = self.create_buttons(self.layout_layers, KEYCODES_LAYERS)
self.tap_dance_keycode_buttons = self.create_buttons(self.layout_tap_dance, KEYCODES_TAP_DANCE)
self.macro_keycode_buttons = self.create_buttons(self.layout_macro, KEYCODES_MACRO)
self.user_keycode_buttons = self.create_buttons(self.layout_user, KEYCODES_USER, wordWrap=True)
self.widgets += self.layer_keycode_buttons + self.macro_keycode_buttons + self.user_keycode_buttons
self.user_keycode_buttons = self.create_buttons(self.layout_user, KEYCODES_USER, word_wrap=True)
self.widgets += self.layer_keycode_buttons + self.tap_dance_keycode_buttons + \
self.macro_keycode_buttons + self.user_keycode_buttons
self.relabel_buttons()
def set_keymap_override(self, override):
self.keymap_override = override
def on_keymap_override(self):
self.relabel_buttons()
def relabel_buttons(self):
for widget in self.widgets:
qmk_id = widget.keycode.qmk_id
if qmk_id in self.keymap_override:
label = self.keymap_override[qmk_id]
if qmk_id in KeycodeDisplay.keymap_override:
label = KeycodeDisplay.keymap_override[qmk_id]
highlight_color = QApplication.palette().color(QPalette.Link).getRgb()
widget.setStyleSheet("QPushButton {color: rgb"+str(highlight_color)+";}")
else:
label = widget.keycode.label
widget.setStyleSheet("QPushButton {}")
widget.setText(label.replace("&", "&&"))
@classmethod
def set_tray(cls, tray):
cls.tray = tray
@classmethod
def open_tray(cls, target):
cls.tray.show()
if cls.tray.target is not None and cls.tray.target != target:
cls.tray.target.deselect()
cls.tray.target = target
@classmethod
def close_tray(cls):
if cls.tray.target is not None:
cls.tray.target.deselect()
cls.tray.target = None
cls.tray.hide()
def make_tray(self):
self.is_tray = True
TabbedKeycodes.set_tray(self)
self.keycode_changed.connect(self.on_tray_keycode_changed)
self.anykey.connect(self.on_tray_anykey)
def on_tray_keycode_changed(self, kc):
if self.target is not None:
self.target.on_keycode_changed(kc)
def on_tray_anykey(self):
if self.target is not None:
self.target.on_anykey()

View File

@ -0,0 +1,179 @@
# SPDX-License-Identifier: GPL-2.0-or-later
from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QTabWidget, QWidget, QSizePolicy, QGridLayout, QVBoxLayout, QLabel, QLineEdit, QHBoxLayout, \
QPushButton, QSpinBox
from key_widget import KeyWidget
from tabbed_keycodes import TabbedKeycodes
from util import tr
from vial_device import VialKeyboard
from basic_editor import BasicEditor
class TapDanceEntryUI(QObject):
key_changed = pyqtSignal()
timing_changed = pyqtSignal()
def __init__(self, idx):
super().__init__()
self.idx = idx
self.container = QGridLayout()
self.populate_container()
w = QWidget()
w.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
w.setLayout(self.container)
l = QVBoxLayout()
l.addStretch()
l.addSpacing(10)
l.addWidget(w)
l.setAlignment(w, QtCore.Qt.AlignHCenter)
l.addSpacing(10)
lbl = QLabel("Use <code>TD({})</code> to set up this action in the keymap.".format(self.idx))
l.addWidget(lbl)
l.setAlignment(lbl, QtCore.Qt.AlignHCenter)
l.addStretch()
self.w2 = QWidget()
self.w2.setLayout(l)
def populate_container(self):
self.container.addWidget(QLabel("On tap"), 0, 0)
self.kc_on_tap = KeyWidget()
self.kc_on_tap.changed.connect(self.on_key_changed)
self.container.addWidget(self.kc_on_tap, 0, 1)
self.container.addWidget(QLabel("On hold"), 1, 0)
self.kc_on_hold = KeyWidget()
self.kc_on_hold.changed.connect(self.on_key_changed)
self.container.addWidget(self.kc_on_hold, 1, 1)
self.container.addWidget(QLabel("On double tap"), 2, 0)
self.kc_on_double_tap = KeyWidget()
self.kc_on_double_tap.changed.connect(self.on_key_changed)
self.container.addWidget(self.kc_on_double_tap, 2, 1)
self.container.addWidget(QLabel("On tap + hold"), 3, 0)
self.kc_on_tap_hold = KeyWidget()
self.kc_on_tap_hold.changed.connect(self.on_key_changed)
self.container.addWidget(self.kc_on_tap_hold, 3, 1)
self.container.addWidget(QLabel("Tapping term (ms)"), 4, 0)
self.txt_tapping_term = QSpinBox()
self.txt_tapping_term.valueChanged.connect(self.on_timing_changed)
self.txt_tapping_term.setMinimum(0)
self.txt_tapping_term.setMaximum(10000)
self.container.addWidget(self.txt_tapping_term, 4, 1)
def widget(self):
return self.w2
def load(self, data):
objs = [self.kc_on_tap, self.kc_on_hold, self.kc_on_double_tap, self.kc_on_tap_hold, self.txt_tapping_term]
for o in objs:
o.blockSignals(True)
self.kc_on_tap.set_keycode(data[0])
self.kc_on_hold.set_keycode(data[1])
self.kc_on_double_tap.set_keycode(data[2])
self.kc_on_tap_hold.set_keycode(data[3])
self.txt_tapping_term.setValue(data[4])
for o in objs:
o.blockSignals(False)
def save(self):
return (
self.kc_on_tap.keycode,
self.kc_on_hold.keycode,
self.kc_on_double_tap.keycode,
self.kc_on_tap_hold.keycode,
self.txt_tapping_term.value()
)
def on_key_changed(self):
self.key_changed.emit()
def on_timing_changed(self):
self.timing_changed.emit()
class CustomTabWidget(QTabWidget):
def mouseReleaseEvent(self, ev):
TabbedKeycodes.close_tray()
class TapDance(BasicEditor):
def __init__(self):
super().__init__()
self.keyboard = None
self.tap_dance_entries = []
self.tap_dance_entries_available = []
self.tabs = CustomTabWidget()
for x in range(128):
entry = TapDanceEntryUI(x)
entry.key_changed.connect(self.on_key_changed)
entry.timing_changed.connect(self.on_timing_changed)
self.tap_dance_entries_available.append(entry)
self.addWidget(self.tabs)
buttons = QHBoxLayout()
buttons.addStretch()
self.btn_save = QPushButton(tr("TapDance", "Save"))
self.btn_save.clicked.connect(self.on_save)
btn_revert = QPushButton(tr("TapDance", "Revert"))
btn_revert.clicked.connect(self.on_revert)
buttons.addWidget(self.btn_save)
buttons.addWidget(btn_revert)
self.addLayout(buttons)
def rebuild_ui(self):
while self.tabs.count() > 0:
self.tabs.removeTab(0)
self.tap_dance_entries = self.tap_dance_entries_available[:self.keyboard.tap_dance_count]
for x, e in enumerate(self.tap_dance_entries):
self.tabs.addTab(e.widget(), str(x))
self.reload_ui()
def reload_ui(self):
for x, e in enumerate(self.tap_dance_entries):
e.load(self.keyboard.tap_dance_get(x))
self.update_modified_state()
def on_save(self):
for x, e in enumerate(self.tap_dance_entries):
self.keyboard.tap_dance_set(x, self.tap_dance_entries[x].save())
self.update_modified_state()
def on_revert(self):
self.keyboard.reload_dynamic()
self.reload_ui()
def rebuild(self, device):
super().rebuild(device)
if self.valid():
self.keyboard = device.keyboard
self.rebuild_ui()
def valid(self):
return isinstance(self.device, VialKeyboard) and \
(self.device.keyboard and self.device.keyboard.vial_protocol >= 4
and self.device.keyboard.tap_dance_count > 0)
def on_key_changed(self):
self.on_save()
def update_modified_state(self):
""" Update indication of which tabs are modified, and keep Save button enabled only if it's needed """
has_changes = False
for x, e in enumerate(self.tap_dance_entries):
if self.tap_dance_entries[x].save() != self.keyboard.tap_dance_get(x):
has_changes = True
self.tabs.setTabText(x, "{}*".format(x))
else:
self.tabs.setTabText(x, str(x))
self.btn_save.setEnabled(has_changes)
def on_timing_changed(self):
self.update_modified_state()

View File

@ -5,9 +5,12 @@ import time
from logging.handlers import RotatingFileHandler
from PyQt5.QtCore import QCoreApplication, QStandardPaths
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QApplication
from hidproxy import hid
from keycodes import Keycode
from keymaps import KEYMAPS
tr = QCoreApplication.translate
@ -147,3 +150,50 @@ def init_logger():
handler = RotatingFileHandler(path, maxBytes=5 * 1024 * 1024, backupCount=5)
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s"))
logging.getLogger().addHandler(handler)
class KeycodeDisplay:
keymap_override = KEYMAPS[0][1]
clients = []
@classmethod
def get_label(cls, code):
""" Get label for a specific keycode """
if cls.code_is_overriden(code):
return cls.keymap_override[Keycode.find_outer_keycode(code).qmk_id]
return Keycode.label(code)
@classmethod
def code_is_overriden(cls, code):
""" Check whether a country-specific keymap overrides a code """
key = Keycode.find_outer_keycode(code)
return key is not None and key.qmk_id in cls.keymap_override
@classmethod
def display_keycode(cls, widget, code):
text = cls.get_label(code)
tooltip = Keycode.tooltip(code)
mask = Keycode.is_mask(code)
mask_text = cls.get_label(code & 0xFF)
if mask:
text = text.split("\n")[0]
widget.masked = mask
widget.setText(text)
widget.setMaskText(mask_text)
widget.setToolTip(tooltip)
if cls.code_is_overriden(code):
widget.setColor(QApplication.palette().color(QPalette.Link))
else:
widget.setColor(None)
@classmethod
def set_keymap_override(cls, override):
cls.keymap_override = override
for client in cls.clients:
client.on_keymap_override()
@classmethod
def notify_keymap_override(cls, client):
cls.clients.append(client)
client.on_keymap_override()