initial support for masked keycodes
parent
3683ef98d1
commit
215a54fca1
|
|
@ -13,3 +13,4 @@ LAYER_BTN_STYLE = "border: 1px solid black; padding: 5px"
|
|||
ACTIVE_LAYER_BTN_STYLE = "border: 1px solid black; padding: 5px; background-color: black; color: white"
|
||||
|
||||
KEYBOARD_WIDGET_PADDING = 5
|
||||
KEYBOARD_WIDGET_MASK_PADDING = 3
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QVBoxLayout
|
|||
|
||||
from clickable_label import ClickableLabel
|
||||
from keyboard_widget import KeyboardWidget
|
||||
from keycodes import keycode_label, keycode_tooltip
|
||||
from keycodes import keycode_label, keycode_tooltip, keycode_is_mask
|
||||
from constants import LAYER_BTN_STYLE, ACTIVE_LAYER_BTN_STYLE
|
||||
from util import tr
|
||||
|
||||
|
|
@ -42,6 +42,7 @@ class KeyboardContainer(QWidget):
|
|||
self.rowcol = defaultdict(list)
|
||||
self.selected_row = -1
|
||||
self.selected_col = -1
|
||||
self.selected_mask = False
|
||||
self.keyboard = None
|
||||
self.current_layer = 0
|
||||
|
||||
|
|
@ -94,8 +95,14 @@ class KeyboardContainer(QWidget):
|
|||
code = self.keyboard.layout[(self.current_layer, row, col)]
|
||||
text = keycode_label(code)
|
||||
tooltip = keycode_tooltip(code)
|
||||
mask = keycode_is_mask(code)
|
||||
mask_text = keycode_label(code & 0xFF)
|
||||
if mask:
|
||||
text = text.split("\n")[0]
|
||||
for widget in widgets:
|
||||
widget.masked = mask
|
||||
widget.setText(text)
|
||||
widget.setMaskText(mask_text)
|
||||
widget.setToolTip(tooltip)
|
||||
self.container.update()
|
||||
|
||||
|
|
@ -109,13 +116,23 @@ class KeyboardContainer(QWidget):
|
|||
def set_key(self, keycode):
|
||||
""" Change currently selected key to provided keycode """
|
||||
|
||||
if self.selected_row >= 0 and self.selected_col >= 0:
|
||||
self.keyboard.set_key(self.current_layer, self.selected_row, self.selected_col, keycode)
|
||||
l, r, c = self.current_layer, self.selected_row, self.selected_col
|
||||
|
||||
if r >= 0 and c >= 0:
|
||||
# if masked, ensure that this is a byte-sized keycode
|
||||
if self.selected_mask:
|
||||
if keycode > 0xFF:
|
||||
return
|
||||
keycode = (self.keyboard.layout[(l, r, c)] & 0xFF00) | keycode
|
||||
|
||||
self.keyboard.set_key(l, r, c, keycode)
|
||||
self.refresh_layer_display()
|
||||
|
||||
def on_key_clicked(self, widget):
|
||||
def on_key_clicked(self, widget, is_mask):
|
||||
""" Change which key is currently selected """
|
||||
|
||||
self.selected_mask = is_mask
|
||||
|
||||
for (row, col), widgets in self.rowcol.items():
|
||||
if widget in widgets:
|
||||
self.selected_row = row
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from PyQt5.QtGui import QPainter, QColor, QPainterPath, QTransform, QBrush, QPolygonF
|
||||
from PyQt5.QtWidgets import QWidget, QToolTip
|
||||
from PyQt5.QtCore import Qt, QSize, QRect, QPointF, pyqtSignal, QEvent
|
||||
from PyQt5.QtCore import Qt, QSize, QRect, QPointF, pyqtSignal, QEvent, QRectF
|
||||
|
||||
from constants import KEY_WIDTH, KEY_SPACING, KEY_HEIGHT, KEYBOARD_WIDGET_PADDING
|
||||
from constants import KEY_WIDTH, KEY_SPACING, KEY_HEIGHT, KEYBOARD_WIDGET_PADDING, KEYBOARD_WIDGET_MASK_PADDING
|
||||
|
||||
|
||||
class KeyWidget:
|
||||
|
|
@ -10,6 +10,7 @@ class KeyWidget:
|
|||
def __init__(self, desc):
|
||||
self.desc = desc
|
||||
self.text = ""
|
||||
self.mask_text = ""
|
||||
self.tooltip = ""
|
||||
|
||||
self.rotation_x = (KEY_WIDTH + KEY_SPACING) * desc.rotation_x
|
||||
|
|
@ -30,15 +31,24 @@ class KeyWidget:
|
|||
self.w2 = (KEY_WIDTH + KEY_SPACING) * desc.width2 - KEY_SPACING
|
||||
self.h2 = (KEY_HEIGHT + KEY_SPACING) * desc.height2 - KEY_SPACING
|
||||
|
||||
self.bbox = self.calculate_bbox()
|
||||
self.bbox = self.calculate_bbox(QRectF(self.x, self.y, self.w, self.h))
|
||||
self.polygon = QPolygonF(self.bbox + [self.bbox[0]])
|
||||
self.draw_path = self.calculate_draw_path()
|
||||
|
||||
def calculate_bbox(self):
|
||||
x1 = self.x
|
||||
y1 = self.y
|
||||
x2 = self.x + self.w
|
||||
y2 = self.y + self.h
|
||||
# calculate areas where the inner keycode will be located
|
||||
# nonmask = outer (e.g. Rsft_T)
|
||||
# mask = inner (e.g. KC_A)
|
||||
self.nonmask_rect = QRectF(self.x, self.y, self.w, self.h / 2)
|
||||
self.mask_rect = QRectF(self.x + KEYBOARD_WIDGET_MASK_PADDING, self.y + self.h / 2,
|
||||
self.w - 2 * KEYBOARD_WIDGET_MASK_PADDING, self.h / 2 - KEYBOARD_WIDGET_MASK_PADDING)
|
||||
self.mask_bbox = self.calculate_bbox(self.mask_rect)
|
||||
self.mask_polygon = QPolygonF(self.mask_bbox + [self.mask_bbox[0]])
|
||||
|
||||
def calculate_bbox(self, rect):
|
||||
x1 = rect.topLeft().x()
|
||||
y1 = rect.topLeft().y()
|
||||
x2 = rect.bottomRight().x()
|
||||
y2 = rect.bottomRight().y()
|
||||
points = [(x1, y1), (x1, y2), (x2, y2), (x2, y1)]
|
||||
bbox = []
|
||||
for p in points:
|
||||
|
|
@ -66,13 +76,16 @@ class KeyWidget:
|
|||
def setText(self, text):
|
||||
self.text = text
|
||||
|
||||
def setMaskText(self, text):
|
||||
self.mask_text = text
|
||||
|
||||
def setToolTip(self, tooltip):
|
||||
self.tooltip = tooltip
|
||||
|
||||
|
||||
class KeyboardWidget(QWidget):
|
||||
|
||||
clicked = pyqtSignal(KeyWidget)
|
||||
clicked = pyqtSignal(KeyWidget, bool)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
|
@ -81,6 +94,7 @@ class KeyboardWidget(QWidget):
|
|||
self.keys = []
|
||||
self.width = self.height = 0
|
||||
self.active_key = None
|
||||
self.active_mask = False
|
||||
|
||||
def set_keys(self, keys):
|
||||
self.keys = []
|
||||
|
|
@ -126,21 +140,36 @@ class KeyboardWidget(QWidget):
|
|||
active_brush.setColor(QColor("black"))
|
||||
active_brush.setStyle(Qt.SolidPattern)
|
||||
|
||||
mask_font = qp.font()
|
||||
mask_font.setPointSize(mask_font.pointSize() * 0.8)
|
||||
|
||||
for idx, key in enumerate(self.keys):
|
||||
qp.save()
|
||||
qp.translate(key.rotation_x, key.rotation_y)
|
||||
qp.rotate(key.rotation_angle)
|
||||
qp.translate(-key.rotation_x, -key.rotation_y)
|
||||
|
||||
if self.active_key == key:
|
||||
if self.active_key == key and not self.active_mask:
|
||||
qp.setPen(active_pen)
|
||||
qp.setBrush(active_brush)
|
||||
|
||||
# draw the keycap
|
||||
qp.drawPath(key.draw_path)
|
||||
|
||||
# draw the legend
|
||||
qp.drawText(key.rect, Qt.AlignCenter, key.text)
|
||||
# if this is a mask key, draw the inner key
|
||||
if key.masked:
|
||||
qp.setFont(mask_font)
|
||||
qp.drawText(key.nonmask_rect, Qt.AlignCenter, key.text)
|
||||
|
||||
if self.active_key == key and self.active_mask:
|
||||
qp.setPen(active_pen)
|
||||
qp.setBrush(active_brush)
|
||||
|
||||
qp.drawRect(key.mask_rect)
|
||||
qp.drawText(key.mask_rect, Qt.AlignCenter, key.mask_text)
|
||||
else:
|
||||
# draw the legend
|
||||
qp.drawText(key.rect, Qt.AlignCenter, key.text)
|
||||
|
||||
qp.restore()
|
||||
|
||||
|
|
@ -150,20 +179,21 @@ class KeyboardWidget(QWidget):
|
|||
return QSize(self.width, self.height)
|
||||
|
||||
def hit_test(self, pos):
|
||||
""" Returns key, hit_masked_part """
|
||||
|
||||
for key in self.keys:
|
||||
if key.masked and key.mask_polygon.containsPoint(pos, Qt.OddEvenFill):
|
||||
return key, True
|
||||
if key.polygon.containsPoint(pos, Qt.OddEvenFill):
|
||||
return key
|
||||
return None
|
||||
return key, False
|
||||
|
||||
return None, False
|
||||
|
||||
def mousePressEvent(self, ev):
|
||||
prev_active_key = self.active_key
|
||||
|
||||
self.active_key = self.hit_test(ev.pos())
|
||||
self.active_key, self.active_mask = self.hit_test(ev.pos())
|
||||
if self.active_key is not None:
|
||||
self.clicked.emit(self.active_key)
|
||||
|
||||
if prev_active_key != self.active_key:
|
||||
self.update()
|
||||
self.clicked.emit(self.active_key, self.active_mask)
|
||||
self.update()
|
||||
|
||||
def deselect(self):
|
||||
if self.active_key is not None:
|
||||
|
|
@ -172,7 +202,7 @@ class KeyboardWidget(QWidget):
|
|||
|
||||
def event(self, ev):
|
||||
if ev.type() == QEvent.ToolTip:
|
||||
key = self.hit_test(ev.pos())
|
||||
key = self.hit_test(ev.pos())[0]
|
||||
if key is not None:
|
||||
QToolTip.showText(ev.globalPos(), key.tooltip)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -4,11 +4,18 @@
|
|||
|
||||
class Keycode:
|
||||
|
||||
def __init__(self, code, qmk_id, label, tooltip=None):
|
||||
masked_keycodes = set()
|
||||
|
||||
def __init__(self, code, qmk_id, label, tooltip=None, masked=False):
|
||||
self.code = code
|
||||
self.qmk_id = qmk_id
|
||||
self.label = label
|
||||
self.tooltip = tooltip
|
||||
# whether this keycode requires another sub-keycode
|
||||
self.masked = masked
|
||||
|
||||
if masked:
|
||||
self.masked_keycodes.add(code)
|
||||
|
||||
|
||||
K = Keycode
|
||||
|
|
@ -162,6 +169,8 @@ KEYCODES_ISO = [
|
|||
KEYCODES_LAYERS = []
|
||||
|
||||
QK_ONE_SHOT_MOD = 0x5500
|
||||
QK_MOD_TAP = 0x6000
|
||||
|
||||
MOD_LCTL = 0x01
|
||||
MOD_LSFT = 0x02
|
||||
MOD_LALT = 0x04
|
||||
|
|
@ -206,6 +215,8 @@ KEYCODES_QUANTUM = [
|
|||
K(QK_ONE_SHOT_MOD | MOD_HYPR, "OSM(MOD_HYPR)", "OSM\nHyper",
|
||||
"Enable Control, Shift, Alt, and GUI for one keypress"),
|
||||
|
||||
K(QK_MOD_TAP | (MOD_RSFT << 8), "RSFT_T(kc)", "RSft_T\n(kc)", "Right Shift when held, kc when tapped", masked=True),
|
||||
|
||||
K(0x5C16, "KC_GESC", "Esc\n~", "Esc normally, but ~ when Shift or GUI is pressed"),
|
||||
K(0x5CD7, "KC_LSPO", "LS\n(", "Left Shift when held, ( when tapped"),
|
||||
K(0x5CD8, "KC_RSPC", "RS\n)", "Right Shift when held, ) when tapped"),
|
||||
|
|
@ -232,6 +243,9 @@ K = None
|
|||
|
||||
|
||||
def find_keycode(code):
|
||||
if keycode_is_mask(code):
|
||||
code = code & 0xFF00
|
||||
|
||||
for keycode in KEYCODES:
|
||||
if keycode.code == code:
|
||||
return keycode
|
||||
|
|
@ -255,6 +269,10 @@ def keycode_tooltip(code):
|
|||
return tooltip
|
||||
|
||||
|
||||
def keycode_is_mask(code):
|
||||
return (code & 0xFF00) in Keycode.masked_keycodes
|
||||
|
||||
|
||||
def recreate_keycodes():
|
||||
""" Regenerates global KEYCODES array """
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue