implement rendering of currently selected keyboard layout
parent
6add0a8d70
commit
5af3798445
|
|
@ -118,6 +118,13 @@ class Keyboard:
|
|||
self.rowcol[(row, col)] = True
|
||||
self.keys.append(key)
|
||||
|
||||
# bottom right corner determines layout index and option in this layout
|
||||
key.layout_index = -1
|
||||
key.layout_option = -1
|
||||
if key.labels[8]:
|
||||
idx, opt = key.labels[8].split(",")
|
||||
key.layout_index, key.layout_option = int(idx), int(opt)
|
||||
|
||||
def reload_keymap(self):
|
||||
""" Load current key mapping from the keyboard """
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ class KeyboardContainer(QWidget):
|
|||
|
||||
number_layers_changed = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
def __init__(self, layout_editor):
|
||||
super().__init__()
|
||||
|
||||
self.layout_layers = QHBoxLayout()
|
||||
layer_label = QLabel(tr("KeyboardContainer", "Layer"))
|
||||
|
|
@ -26,7 +26,7 @@ class KeyboardContainer(QWidget):
|
|||
layout_labels_container.addStretch()
|
||||
|
||||
# contains the actual keyboard
|
||||
self.container = KeyboardWidget()
|
||||
self.container = KeyboardWidget(layout_editor)
|
||||
self.container.clicked.connect(self.on_key_clicked)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
|
@ -35,7 +35,6 @@ class KeyboardContainer(QWidget):
|
|||
layout.setAlignment(self.container, Qt.AlignHCenter)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.keys = []
|
||||
self.layer_labels = []
|
||||
self.keyboard = None
|
||||
self.current_layer = 0
|
||||
|
|
@ -59,11 +58,6 @@ class KeyboardContainer(QWidget):
|
|||
def rebuild(self, keyboard):
|
||||
self.keyboard = keyboard
|
||||
|
||||
# delete current layout
|
||||
for key in self.keys:
|
||||
key.deleteLater()
|
||||
self.keys = []
|
||||
|
||||
# get number of layers
|
||||
self.rebuild_layers()
|
||||
|
||||
|
|
@ -79,7 +73,7 @@ class KeyboardContainer(QWidget):
|
|||
label.setStyleSheet(LAYER_BTN_STYLE)
|
||||
self.layer_labels[self.current_layer].setStyleSheet(ACTIVE_LAYER_BTN_STYLE)
|
||||
|
||||
for widget in self.container.keys:
|
||||
for widget in self.container.widgets:
|
||||
if widget.desc.row is not None:
|
||||
code = self.keyboard.layout[(self.current_layer, widget.desc.row, widget.desc.col)]
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from collections import defaultdict
|
||||
|
||||
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, QRectF
|
||||
|
|
@ -7,7 +9,7 @@ from constants import KEY_WIDTH, KEY_SPACING, KEY_HEIGHT, KEYBOARD_WIDGET_PADDIN
|
|||
|
||||
class KeyWidget:
|
||||
|
||||
def __init__(self, desc):
|
||||
def __init__(self, desc, shift_x=0, shift_y=0):
|
||||
self.desc = desc
|
||||
self.text = ""
|
||||
self.mask_text = ""
|
||||
|
|
@ -17,8 +19,8 @@ class KeyWidget:
|
|||
self.rotation_y = (KEY_HEIGHT + KEY_SPACING) * desc.rotation_y
|
||||
self.rotation_angle = desc.rotation_angle
|
||||
|
||||
self.x = (KEY_WIDTH + KEY_SPACING) * desc.x
|
||||
self.y = (KEY_HEIGHT + KEY_SPACING) * desc.y
|
||||
self.x = shift_x + (KEY_WIDTH + KEY_SPACING) * desc.x
|
||||
self.y = shift_y + (KEY_HEIGHT + KEY_SPACING) * desc.y
|
||||
self.w = (KEY_WIDTH + KEY_SPACING) * desc.width - KEY_SPACING
|
||||
self.h = (KEY_HEIGHT + KEY_SPACING) * desc.height - KEY_SPACING
|
||||
|
||||
|
|
@ -113,28 +115,81 @@ class KeyboardWidget(QWidget):
|
|||
|
||||
clicked = pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, layout_editor):
|
||||
super().__init__()
|
||||
self.setMouseTracking(True)
|
||||
|
||||
self.keys = []
|
||||
self.layout_editor = layout_editor
|
||||
|
||||
# widgets common for all layouts
|
||||
self.common_widgets = []
|
||||
|
||||
# layout-specific widgets
|
||||
self.widgets_for_layout = defaultdict(lambda: defaultdict(list))
|
||||
|
||||
# widgets in current layout
|
||||
self.widgets = []
|
||||
|
||||
self.width = self.height = 0
|
||||
self.active_key = None
|
||||
self.active_mask = False
|
||||
|
||||
def set_keys(self, keys, encoders):
|
||||
self.keys = []
|
||||
for key in keys:
|
||||
self.keys.append(KeyWidget(key))
|
||||
for key in encoders:
|
||||
self.keys.append(EncoderWidget(key))
|
||||
self.calculate_size()
|
||||
self.update()
|
||||
self.common_widgets = []
|
||||
self.widgets_for_layout = defaultdict(lambda: defaultdict(list))
|
||||
|
||||
def calculate_size(self):
|
||||
self.add_keys([(x, KeyWidget) for x in keys] + [(x, EncoderWidget) for x in encoders])
|
||||
self.update_layout()
|
||||
|
||||
def add_keys(self, keys):
|
||||
top_x = top_y = 1e6
|
||||
|
||||
# find the global top-left position, all the keys will be shifted to the left/up by that position
|
||||
for key, cls in keys:
|
||||
obj = cls(key)
|
||||
top_x = min(top_x, obj.x)
|
||||
top_y = min(top_y, obj.y)
|
||||
|
||||
# obtain common widgets, that is, ones which are always displayed and require no extra transforms
|
||||
for key, cls in keys:
|
||||
if key.layout_index == -1:
|
||||
self.common_widgets.append(cls(key, -top_x, -top_y))
|
||||
|
||||
# top-left position for specific layout
|
||||
layout_x = defaultdict(lambda: defaultdict(lambda: 1e6))
|
||||
layout_y = defaultdict(lambda: defaultdict(lambda: 1e6))
|
||||
|
||||
# determine top-left position for every layout option
|
||||
for key, cls in keys:
|
||||
if key.layout_index != -1:
|
||||
obj = cls(key)
|
||||
idx, opt = key.layout_index, key.layout_option
|
||||
layout_x[idx][opt] = min(layout_x[idx][opt], obj.x)
|
||||
layout_y[idx][opt] = min(layout_y[idx][opt], obj.y)
|
||||
|
||||
# obtain widgets for all layout options now that we know how to shift them
|
||||
for key, cls in keys:
|
||||
if key.layout_index != -1:
|
||||
idx, opt = key.layout_index, key.layout_option
|
||||
shift_x = layout_x[idx][opt] - layout_x[idx][0]
|
||||
shift_y = layout_y[idx][opt] - layout_y[idx][0]
|
||||
obj = cls(key, -shift_x - top_x, -shift_y - top_y)
|
||||
self.widgets_for_layout[idx][opt].append(obj)
|
||||
|
||||
def update_layout(self):
|
||||
""" Updates self.widgets for the currently active layout """
|
||||
|
||||
# determine widgets for current layout
|
||||
self.widgets = []
|
||||
self.widgets += self.common_widgets
|
||||
for idx in self.widgets_for_layout.keys():
|
||||
option = self.layout_editor.get_choice(idx)
|
||||
print("index {} option {}".format(idx, option))
|
||||
self.widgets += self.widgets_for_layout[idx][option]
|
||||
|
||||
# determine maximum width and height of container
|
||||
max_w = max_h = 0
|
||||
|
||||
for key in self.keys:
|
||||
for key in self.widgets:
|
||||
p = key.polygon.boundingRect().bottomRight()
|
||||
max_w = max(max_w, p.x())
|
||||
max_h = max(max_h, p.y())
|
||||
|
|
@ -142,6 +197,8 @@ class KeyboardWidget(QWidget):
|
|||
self.width = max_w + 2 * KEYBOARD_WIDGET_PADDING
|
||||
self.height = max_h + 2 * KEYBOARD_WIDGET_PADDING
|
||||
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
qp = QPainter()
|
||||
qp.begin(self)
|
||||
|
|
@ -171,7 +228,7 @@ class KeyboardWidget(QWidget):
|
|||
mask_font = qp.font()
|
||||
mask_font.setPointSize(mask_font.pointSize() * 0.8)
|
||||
|
||||
for idx, key in enumerate(self.keys):
|
||||
for idx, key in enumerate(self.widgets):
|
||||
qp.save()
|
||||
qp.translate(key.rotation_x, key.rotation_y)
|
||||
qp.rotate(key.rotation_angle)
|
||||
|
|
@ -210,7 +267,7 @@ class KeyboardWidget(QWidget):
|
|||
def hit_test(self, pos):
|
||||
""" Returns key, hit_masked_part """
|
||||
|
||||
for key in self.keys:
|
||||
for key in self.widgets:
|
||||
if key.masked and key.mask_polygon.containsPoint(pos, Qt.OddEvenFill):
|
||||
return key, True
|
||||
if key.polygon.containsPoint(pos, Qt.OddEvenFill):
|
||||
|
|
@ -226,9 +283,8 @@ class KeyboardWidget(QWidget):
|
|||
|
||||
def select_next(self):
|
||||
""" Selects next key based on their order in the keymap """
|
||||
# TODO: this almost certainly needs changes after multilayout support lands
|
||||
|
||||
keys_looped = self.keys + [self.keys[0]]
|
||||
keys_looped = self.widgets + [self.widgets[0]]
|
||||
for x, key in enumerate(keys_looped):
|
||||
if key == self.active_key:
|
||||
self.active_key = keys_looped[x + 1]
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ from vial_device import VialKeyboard
|
|||
|
||||
class KeymapEditor(BasicEditor):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
def __init__(self, layout_editor):
|
||||
super().__init__()
|
||||
|
||||
self.keyboard_container = KeyboardContainer()
|
||||
self.keyboard_container = KeyboardContainer(layout_editor)
|
||||
self.keyboard_container.number_layers_changed.connect(self.on_number_layers_changed)
|
||||
|
||||
self.tabbed_keycodes = TabbedKeycodes()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from vial_device import VialKeyboard
|
|||
class BooleanChoice:
|
||||
|
||||
def __init__(self, container, label):
|
||||
self.choice = 0
|
||||
self.choice = False
|
||||
|
||||
self.widget_label = QLabel(label)
|
||||
self.widget_checkbox = QCheckBox()
|
||||
|
|
@ -22,13 +22,14 @@ class BooleanChoice:
|
|||
self.widget_checkbox.deleteLater()
|
||||
|
||||
def pack(self):
|
||||
return str(self.choice)
|
||||
return str(int(self.choice))
|
||||
|
||||
def unpack(self, value):
|
||||
self.change(int(value))
|
||||
|
||||
def change(self, value):
|
||||
self.widget_checkbox.setChecked(bool(value))
|
||||
self.choice = bool(value)
|
||||
self.widget_checkbox.setChecked(self.choice)
|
||||
|
||||
|
||||
class SelectChoice:
|
||||
|
|
@ -111,3 +112,9 @@ class LayoutEditor(BasicEditor):
|
|||
sz = len(choice.pack())
|
||||
choice.unpack(value[-sz:])
|
||||
value = value[:-sz]
|
||||
|
||||
def get_choice(self, index):
|
||||
return int(self.choices[index].pack(), 2)
|
||||
|
||||
def set_choice(self, index, value):
|
||||
self.choices[index].change(value)
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ class MainWindow(QMainWindow):
|
|||
layout_combobox.addWidget(self.combobox_devices)
|
||||
layout_combobox.addWidget(self.btn_refresh_devices)
|
||||
|
||||
self.keymap_editor = KeymapEditor()
|
||||
self.layout_editor = LayoutEditor()
|
||||
self.keymap_editor = KeymapEditor(self.layout_editor)
|
||||
self.firmware_flasher = FirmwareFlasher(self)
|
||||
|
||||
self.editors = [(self.keymap_editor, "Keymap"), (self.layout_editor, "Layout"),
|
||||
|
|
@ -116,8 +116,8 @@ class MainWindow(QMainWindow):
|
|||
if self.current_device is not None:
|
||||
self.current_device.open(self.sideload_json if self.current_device.sideload else None)
|
||||
|
||||
for editor, lbl in self.editors:
|
||||
editor.rebuild(self.current_device)
|
||||
for e in [self.layout_editor, self.keymap_editor, self.firmware_flasher]:
|
||||
e.rebuild(self.current_device)
|
||||
|
||||
self.refresh_tabs()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue