commit
1e1b86b283
|
|
@ -1,11 +1,9 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
KEY_WIDTH = 40
|
KEY_SIZE_RATIO = 2.667
|
||||||
KEY_HEIGHT = KEY_WIDTH
|
KEY_SPACING_RATIO = 0.267
|
||||||
KEY_SPACING = 4
|
|
||||||
|
|
||||||
KEYCODE_BTN_WIDTH = 50
|
KEYCODE_BTN_RATIO = 3.333
|
||||||
KEYCODE_BTN_HEIGHT = KEYCODE_BTN_WIDTH
|
|
||||||
|
|
||||||
WINDOW_WIDTH, WINDOW_HEIGHT = 1024, 768
|
WINDOW_WIDTH, WINDOW_HEIGHT = 1024, 768
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from keyboard_widget import KeyboardWidget, EncoderWidget
|
||||||
from keycodes import keycode_label, keycode_tooltip, keycode_is_mask, find_keycode
|
from keycodes import keycode_label, keycode_tooltip, keycode_is_mask, find_keycode
|
||||||
from constants import LAYER_BTN_STYLE, ACTIVE_LAYER_BTN_STYLE
|
from constants import LAYER_BTN_STYLE, ACTIVE_LAYER_BTN_STYLE
|
||||||
from keymaps import KEYMAPS
|
from keymaps import KEYMAPS
|
||||||
|
from square_button import SquareButton
|
||||||
from util import tr
|
from util import tr
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -54,8 +55,8 @@ class KeyboardContainer(QWidget):
|
||||||
|
|
||||||
# create new layer labels
|
# create new layer labels
|
||||||
for x in range(self.keyboard.layers):
|
for x in range(self.keyboard.layers):
|
||||||
btn = QPushButton(str(x))
|
btn = SquareButton(str(x))
|
||||||
btn.setFixedSize(25, 25)
|
btn.setRelSize(1.667)
|
||||||
btn.setCheckable(True)
|
btn.setCheckable(True)
|
||||||
btn.clicked.connect(lambda state, idx=x: self.switch_layer(idx))
|
btn.clicked.connect(lambda state, idx=x: self.switch_layer(idx))
|
||||||
self.layout_layers.addWidget(btn)
|
self.layout_layers.addWidget(btn)
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ from PyQt5.QtGui import QPainter, QColor, QPainterPath, QTransform, QBrush, QPol
|
||||||
from PyQt5.QtWidgets import QWidget, QToolTip, QApplication
|
from PyQt5.QtWidgets import QWidget, QToolTip, QApplication
|
||||||
from PyQt5.QtCore import Qt, QSize, QRect, QPointF, pyqtSignal, QEvent, QRectF
|
from PyQt5.QtCore import Qt, QSize, QRect, QPointF, pyqtSignal, QEvent, QRectF
|
||||||
|
|
||||||
from constants import KEY_WIDTH, KEY_SPACING, KEY_HEIGHT, KEYBOARD_WIDGET_PADDING, KEYBOARD_WIDGET_MASK_PADDING
|
from constants import KEY_SIZE_RATIO, KEY_SPACING_RATIO, KEYBOARD_WIDGET_PADDING, KEYBOARD_WIDGET_MASK_PADDING
|
||||||
|
|
||||||
|
|
||||||
class KeyWidget:
|
class KeyWidget:
|
||||||
|
|
||||||
def __init__(self, desc, shift_x=0, shift_y=0):
|
def __init__(self, desc, scale, shift_x=0, shift_y=0):
|
||||||
self.active = False
|
self.active = False
|
||||||
self.masked = False
|
self.masked = False
|
||||||
self.desc = desc
|
self.desc = desc
|
||||||
|
|
@ -17,26 +17,36 @@ class KeyWidget:
|
||||||
self.mask_text = ""
|
self.mask_text = ""
|
||||||
self.tooltip = ""
|
self.tooltip = ""
|
||||||
self.color = None
|
self.color = None
|
||||||
|
self.scale = 0
|
||||||
|
|
||||||
self.rotation_x = (KEY_WIDTH + KEY_SPACING) * desc.rotation_x
|
|
||||||
self.rotation_y = (KEY_HEIGHT + KEY_SPACING) * desc.rotation_y
|
|
||||||
self.rotation_angle = desc.rotation_angle
|
self.rotation_angle = desc.rotation_angle
|
||||||
|
|
||||||
self.shift_x = shift_x
|
|
||||||
self.shift_y = shift_y
|
|
||||||
self.x = (KEY_WIDTH + KEY_SPACING) * desc.x
|
|
||||||
self.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
|
|
||||||
|
|
||||||
self.rect = QRect(self.x, self.y, self.w, self.h)
|
|
||||||
|
|
||||||
self.has2 = desc.width2 != desc.width or desc.height2 != desc.height or desc.x2 != 0 or desc.y2 != 0
|
self.has2 = desc.width2 != desc.width or desc.height2 != desc.height or desc.x2 != 0 or desc.y2 != 0
|
||||||
|
|
||||||
self.x2 = self.x + (KEY_WIDTH + KEY_SPACING) * desc.x2
|
self.update_position(scale, shift_x, shift_y)
|
||||||
self.y2 = self.y + (KEY_WIDTH + KEY_SPACING) * desc.y2
|
|
||||||
self.w2 = (KEY_WIDTH + KEY_SPACING) * desc.width2 - KEY_SPACING
|
def update_position(self, scale, shift_x=0, shift_y=0):
|
||||||
self.h2 = (KEY_HEIGHT + KEY_SPACING) * desc.height2 - KEY_SPACING
|
if self.scale != scale or self.shift_x != shift_x or self.shift_y != shift_y:
|
||||||
|
self.scale = scale
|
||||||
|
size = self.scale * (KEY_SIZE_RATIO + KEY_SPACING_RATIO)
|
||||||
|
spacing = self.scale * KEY_SPACING_RATIO
|
||||||
|
|
||||||
|
self.rotation_x = size * self.desc.rotation_x
|
||||||
|
self.rotation_y = size * self.desc.rotation_y
|
||||||
|
|
||||||
|
self.shift_x = shift_x
|
||||||
|
self.shift_y = shift_y
|
||||||
|
self.x = size * self.desc.x
|
||||||
|
self.y = size * self.desc.y
|
||||||
|
self.w = size * self.desc.width - spacing
|
||||||
|
self.h = size * self.desc.height - spacing
|
||||||
|
|
||||||
|
self.rect = QRect(self.x, self.y, self.w, self.h)
|
||||||
|
|
||||||
|
self.x2 = self.x + size * self.desc.x2
|
||||||
|
self.y2 = self.y + size * self.desc.y2
|
||||||
|
self.w2 = size * self.desc.width2 - spacing
|
||||||
|
self.h2 = size * self.desc.height2 - spacing
|
||||||
|
|
||||||
self.bbox = self.calculate_bbox(QRectF(self.x, self.y, self.w, self.h))
|
self.bbox = self.calculate_bbox(QRectF(self.x, self.y, self.w, self.h))
|
||||||
self.polygon = QPolygonF(self.bbox + [self.bbox[0]])
|
self.polygon = QPolygonF(self.bbox + [self.bbox[0]])
|
||||||
|
|
@ -140,7 +150,7 @@ class KeyboardWidget(QWidget):
|
||||||
self.common_widgets = []
|
self.common_widgets = []
|
||||||
|
|
||||||
# layout-specific widgets
|
# layout-specific widgets
|
||||||
self.widgets_for_layout = defaultdict(lambda: defaultdict(list))
|
self.widgets_for_layout = []
|
||||||
|
|
||||||
# widgets in current layout
|
# widgets in current layout
|
||||||
self.widgets = []
|
self.widgets = []
|
||||||
|
|
@ -151,59 +161,64 @@ class KeyboardWidget(QWidget):
|
||||||
|
|
||||||
def set_keys(self, keys, encoders):
|
def set_keys(self, keys, encoders):
|
||||||
self.common_widgets = []
|
self.common_widgets = []
|
||||||
self.widgets_for_layout = defaultdict(lambda: defaultdict(list))
|
self.widgets_for_layout = []
|
||||||
|
|
||||||
self.add_keys([(x, KeyWidget) for x in keys] + [(x, EncoderWidget) for x in encoders])
|
self.add_keys([(x, KeyWidget) for x in keys] + [(x, EncoderWidget) for x in encoders])
|
||||||
self.update_layout()
|
self.update_layout()
|
||||||
|
|
||||||
def add_keys(self, keys):
|
def add_keys(self, keys):
|
||||||
top_x = top_y = 1e6
|
scale_factor = self.fontMetrics().height()
|
||||||
|
|
||||||
# find the global top-left position, all the keys will be shifted to the left/up by that position
|
|
||||||
for key, cls in keys:
|
for key, cls in keys:
|
||||||
if key.layout_index == -1:
|
if key.layout_index == -1:
|
||||||
obj = cls(key)
|
self.common_widgets.append(cls(key, scale_factor))
|
||||||
p = obj.polygon.boundingRect().topLeft()
|
else:
|
||||||
|
self.widgets_for_layout.append(cls(key, scale_factor))
|
||||||
|
|
||||||
|
def place_widgets(self):
|
||||||
|
top_x = top_y = 1e6
|
||||||
|
scale_factor = self.fontMetrics().height()
|
||||||
|
|
||||||
|
self.widgets = []
|
||||||
|
|
||||||
|
# find the global top-left position, all the keys will be shifted to the left/up by that position
|
||||||
|
for widget in self.common_widgets:
|
||||||
|
widget.update_position(scale_factor)
|
||||||
|
p = widget.polygon.boundingRect().topLeft()
|
||||||
top_x = min(top_x, p.x())
|
top_x = min(top_x, p.x())
|
||||||
top_y = min(top_y, p.y())
|
top_y = min(top_y, p.y())
|
||||||
|
|
||||||
# obtain common widgets, that is, ones which are always displayed and require no extra transforms
|
# place common widgets, that is, ones which are always displayed and require no extra transforms
|
||||||
for key, cls in keys:
|
for widget in self.common_widgets:
|
||||||
if key.layout_index == -1:
|
widget.update_position(scale_factor, -top_x + KEYBOARD_WIDGET_PADDING, -top_y + KEYBOARD_WIDGET_PADDING)
|
||||||
self.common_widgets.append(cls(key, -top_x + KEYBOARD_WIDGET_PADDING, -top_y + KEYBOARD_WIDGET_PADDING))
|
self.widgets.append(widget)
|
||||||
|
|
||||||
# top-left position for specific layout
|
# top-left position for specific layout
|
||||||
layout_x = defaultdict(lambda: defaultdict(lambda: 1e6))
|
layout_x = defaultdict(lambda: defaultdict(lambda: 1e6))
|
||||||
layout_y = defaultdict(lambda: defaultdict(lambda: 1e6))
|
layout_y = defaultdict(lambda: defaultdict(lambda: 1e6))
|
||||||
|
|
||||||
# determine top-left position for every layout option
|
# determine top-left position for every layout option
|
||||||
for key, cls in keys:
|
for widget in self.widgets_for_layout:
|
||||||
if key.layout_index != -1:
|
widget.update_position(scale_factor)
|
||||||
obj = cls(key)
|
idx, opt = widget.desc.layout_index, widget.desc.layout_option
|
||||||
idx, opt = key.layout_index, key.layout_option
|
p = widget.polygon.boundingRect().topLeft()
|
||||||
p = obj.polygon.boundingRect().topLeft()
|
|
||||||
layout_x[idx][opt] = min(layout_x[idx][opt], p.x())
|
layout_x[idx][opt] = min(layout_x[idx][opt], p.x())
|
||||||
layout_y[idx][opt] = min(layout_y[idx][opt], p.y())
|
layout_y[idx][opt] = min(layout_y[idx][opt], p.y())
|
||||||
|
|
||||||
# obtain widgets for all layout options now that we know how to shift them
|
# obtain widgets for all layout options now that we know how to shift them
|
||||||
for key, cls in keys:
|
for widget in self.widgets_for_layout:
|
||||||
if key.layout_index != -1:
|
idx, opt = widget.desc.layout_index, widget.desc.layout_option
|
||||||
idx, opt = key.layout_index, key.layout_option
|
if opt == self.layout_editor.get_choice(idx):
|
||||||
shift_x = layout_x[idx][opt] - layout_x[idx][0]
|
shift_x = layout_x[idx][opt] - layout_x[idx][0]
|
||||||
shift_y = layout_y[idx][opt] - layout_y[idx][0]
|
shift_y = layout_y[idx][opt] - layout_y[idx][0]
|
||||||
obj = cls(key, -shift_x - top_x + KEYBOARD_WIDGET_PADDING, -shift_y - top_y + KEYBOARD_WIDGET_PADDING)
|
widget.update_position(scale_factor, -shift_x - top_x + KEYBOARD_WIDGET_PADDING, -shift_y - top_y + KEYBOARD_WIDGET_PADDING)
|
||||||
self.widgets_for_layout[idx][opt].append(obj)
|
self.widgets.append(widget)
|
||||||
|
|
||||||
def update_layout(self):
|
def update_layout(self):
|
||||||
""" Updates self.widgets for the currently active layout """
|
""" Updates self.widgets for the currently active layout """
|
||||||
|
|
||||||
# determine widgets for current layout
|
# determine widgets for current layout
|
||||||
self.widgets = []
|
self.place_widgets()
|
||||||
self.widgets += self.common_widgets
|
|
||||||
for idx in self.widgets_for_layout.keys():
|
|
||||||
option = self.layout_editor.get_choice(idx)
|
|
||||||
self.widgets += self.widgets_for_layout[idx][option]
|
|
||||||
|
|
||||||
self.widgets = list(filter(lambda w: not w.desc.decal, self.widgets))
|
self.widgets = list(filter(lambda w: not w.desc.decal, self.widgets))
|
||||||
|
|
||||||
self.widgets.sort(key=lambda w: (w.y, w.x))
|
self.widgets.sort(key=lambda w: (w.y, w.x))
|
||||||
|
|
@ -219,6 +234,7 @@ class KeyboardWidget(QWidget):
|
||||||
self.height = max_h + 2 * KEYBOARD_WIDGET_PADDING
|
self.height = max_h + 2 * KEYBOARD_WIDGET_PADDING
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
self.updateGeometry()
|
||||||
|
|
||||||
def paintEvent(self, event):
|
def paintEvent(self, event):
|
||||||
qp = QPainter()
|
qp = QPainter()
|
||||||
|
|
@ -314,6 +330,9 @@ class KeyboardWidget(QWidget):
|
||||||
self.clicked.emit()
|
self.clicked.emit()
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def resizeEvent(self, ev):
|
||||||
|
self.update_layout()
|
||||||
|
|
||||||
def select_next(self):
|
def select_next(self):
|
||||||
""" Selects next key based on their order in the keymap """
|
""" Selects next key based on their order in the keymap """
|
||||||
|
|
||||||
|
|
@ -340,6 +359,8 @@ class KeyboardWidget(QWidget):
|
||||||
QToolTip.showText(ev.globalPos(), key.tooltip)
|
QToolTip.showText(ev.globalPos(), key.tooltip)
|
||||||
else:
|
else:
|
||||||
QToolTip.hideText()
|
QToolTip.hideText()
|
||||||
|
if ev.type() == QEvent.LayoutRequest:
|
||||||
|
self.update_layout()
|
||||||
return super().event(ev)
|
return super().event(ev)
|
||||||
|
|
||||||
def set_enabled(self, val):
|
def set_enabled(self, val):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QSize
|
||||||
|
from PyQt5.QtWidgets import QPushButton
|
||||||
|
|
||||||
|
class SquareButton(QPushButton):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.scale = 1.2
|
||||||
|
|
||||||
|
def setRelSize(self, ratio):
|
||||||
|
self.scale = ratio
|
||||||
|
self.updateGeometry()
|
||||||
|
|
||||||
|
def sizeHint(self):
|
||||||
|
size = int(round(self.fontMetrics().height() * self.scale))
|
||||||
|
return QSize(size, size)
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, pyqtSignal
|
from PyQt5.QtCore import Qt, QSize, pyqtSignal
|
||||||
from PyQt5.QtWidgets import QTabWidget, QWidget, QPushButton, QScrollArea, QApplication
|
from PyQt5.QtWidgets import QTabWidget, QWidget, QPushButton, QScrollArea, QApplication
|
||||||
from PyQt5.QtGui import QPalette
|
from PyQt5.QtGui import QPalette
|
||||||
|
|
||||||
from constants import KEYCODE_BTN_WIDTH, KEYCODE_BTN_HEIGHT
|
from constants import KEYCODE_BTN_RATIO
|
||||||
from flowlayout import FlowLayout
|
from flowlayout import FlowLayout
|
||||||
from keycodes import keycode_tooltip, KEYCODES_BASIC, KEYCODES_ISO, KEYCODES_MACRO, KEYCODES_LAYERS, KEYCODES_QUANTUM, \
|
from keycodes import keycode_tooltip, KEYCODES_BASIC, KEYCODES_ISO, KEYCODES_MACRO, KEYCODES_LAYERS, KEYCODES_QUANTUM, \
|
||||||
KEYCODES_BACKLIGHT, KEYCODES_MEDIA, KEYCODES_SPECIAL
|
KEYCODES_BACKLIGHT, KEYCODES_MEDIA, KEYCODES_SPECIAL
|
||||||
from keymaps import KEYMAPS
|
from keymaps import KEYMAPS
|
||||||
|
from square_button import SquareButton
|
||||||
from util import tr
|
from util import tr
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,8 +66,8 @@ class TabbedKeycodes(QTabWidget):
|
||||||
buttons = []
|
buttons = []
|
||||||
|
|
||||||
for keycode in keycodes:
|
for keycode in keycodes:
|
||||||
btn = QPushButton()
|
btn = SquareButton()
|
||||||
btn.setFixedSize(KEYCODE_BTN_WIDTH, KEYCODE_BTN_HEIGHT)
|
btn.setRelSize(KEYCODE_BTN_RATIO)
|
||||||
btn.setToolTip(keycode_tooltip(keycode.code))
|
btn.setToolTip(keycode_tooltip(keycode.code))
|
||||||
btn.clicked.connect(lambda st, k=keycode: self.keycode_changed.emit(k.code))
|
btn.clicked.connect(lambda st, k=keycode: self.keycode_changed.emit(k.code))
|
||||||
btn.keycode = keycode
|
btn.keycode = keycode
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue