diff --git a/open_osk.py b/open_osk.py new file mode 100644 index 0000000..542fdde --- /dev/null +++ b/open_osk.py @@ -0,0 +1,3 @@ +import src.lib.oskb.cli as oskbcli + +oskbcli.main() \ No newline at end of file diff --git a/open_oskb.sh b/open_oskb.sh new file mode 100755 index 0000000..acec113 --- /dev/null +++ b/open_oskb.sh @@ -0,0 +1,3 @@ +#!/bin/bash +source venv/bin/activat +python open_osk.py diff --git a/src/lib/oskb/__init__.py b/src/lib/oskb/__init__.py new file mode 100644 index 0000000..f03e533 --- /dev/null +++ b/src/lib/oskb/__init__.py @@ -0,0 +1,3 @@ +import pkg_resources + +from oskb.oskb import * diff --git a/src/lib/oskb/cli.py b/src/lib/oskb/cli.py new file mode 100644 index 0000000..03c0db6 --- /dev/null +++ b/src/lib/oskb/cli.py @@ -0,0 +1,376 @@ +import argparse, sys, os, psutil, subprocess, re, signal +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt, QTimer +import pkg_resources + +import oskb +from oskb import im + +linux = sys.platform.startswith("linux") + +if linux: + import getpass + from ewmh import EWMH, ewmh + + wm = EWMH() + moved_windows = [] + + +def command_line_arguments(): + ap = argparse.ArgumentParser() + ap.add_argument( + "keyboards", + help="""Which keyboard(s) to load. These are either files or names of built-in keyboards. If +multiple keyboards are loaded, the user can switch between them with a menu key. If no keyboards are chosen +a full keyboard is shown if display is wider than 600 pixels, otherwise a phone-style keyboard is used. If +oskb comes with a keyboard for the presently set keyboard layout, that keyboard is used. Otherwise oskb +will show a US keyboard layout and switch the system keyboard layout to that.""", + metavar="", + nargs="*", + ) + ap.add_argument("--version", "-v", help="Print version number and exit.", action="store_true") + ap.add_argument( + "--start", + metavar="", + help="""Normally the first keyboard specified is shown first. This allows you to specify one of the +loaded keyboards as the one to start with.""", + ) + ap.add_argument( + "--list", action="store_true", help="Lists all built-in keyboards that were shipped with oskb.", + ) + ap.add_argument( + "--dump", + help="Keyboards are JSON files. This will write the contents of a built-in keyboard to stdout.", + action="store_true", + ) + ap.add_argument( + "--nomap", + help="""Prevent oskb from changing the system keyboard to the keymap specified with the keyboard.""", + action="store_true", + ) + ap.add_argument( + "--toggle", + help="""Will turn the keyboard off if one is already active, otherwise starts the keyboard. This +allows one shortcut to be used to turn the keyboard on and off.""", + action="store_true", + ) + ap.add_argument("--off", help="Turns off a running keyboard.", action="store_true") + ap.add_argument( + "--nopushaway", + help="Do not attempt to push other windows out of the way when showing the keyboard.", + action="store_true", + ) + modmode = ap.add_mutually_exclusive_group() + modmode.add_argument( + "--flashmod", + help="""Only press the modifier keys (Alt, Shift, etc) down briefly during each keypress. This is the +default when --float is specified.""", + action="store_true", + ) + modmode.add_argument( + "--steadymod", + help="""Modifier keys are pressed down as shown in interface. This is the default unless --float is +specified.""", + action="store_true", + ) + ap.add_argument("--justshow", help="Show keyboard, do not send keys to OS.", action="store_true") + + loc = ap.add_argument_group(title="Controlling position on screen") + loc.add_argument("-x", help="Absolute position of left side of keyboard", metavar="", type=int) + loc.add_argument("-y", help="Absolute position of top of keyboard", metavar="", type=int) + loc.add_argument("--width", + help="Keyboard width in pixels. The default is to use the full width of the primary display.", + metavar="", + type=int, + ) + loc.add_argument( + "--height", + help="Keyboard height in pixels. The default is a third of the height of the primary display.", + metavar="", + type=int, + ) + hpos = loc.add_mutually_exclusive_group() + hpos.add_argument("--left", help="Keyboard docks to the left side of the screen.", action="store_true") + hpos.add_argument( + "--middle", + "--center", + help="Keyboard docks in the middle of the screen. This is the default.", + action="store_true", + ) + hpos.add_argument("--right", help="Keyboard docks to the right side of the screen.", action="store_true") + vpos = loc.add_mutually_exclusive_group() + vpos.add_argument( + "--top", help="Keyboard docks to the top of the screen. This is the default.", action="store_true" + ) + vpos.add_argument("--bottom", help="Keyboard docks to the bottom of the screen.", action="store_true") + loc.add_argument( + "--float", help="Floating keyboard window instead of fixed docked position.", action="store_true", + ) + + return ap + + +def main(): + global x, y, w, h + + # + # Parse command line arguments + # + + ap = command_line_arguments() + cmdline = ap.parse_args("") + + if cmdline.version: + print(pkg_resources.get_distribution("oskb").version) + sys.exit(0) + + if cmdline.list: + for k in pkg_resources.resource_listdir("oskb", "keyboards"): + if not k.startswith("_"): + print(k) + sys.exit(0) + + if cmdline.dump: + if len(cmdline.keyboards) != 1: + sys.stderr.write("Must specify exactly one built-in keyboard to dump.\n") + sys.exit(-1) + if not pkg_resources.resource_exists("oskb", "keyboards/" + cmdline.keyboards[0]): + sys.stderr.write("Built-in keyboard '" + cmdline.keyboards[0] + "' not found.\n") + sys.exit(-1) + print(pkg_resources.resource_string("oskb", "keyboards/" + cmdline.keyboards[0]).decode("utf-8")) + sys.exit(0) + + # + # Kill any existing keyboard instances. If we did end up killing existing keyboards + # only start up if '--toggle' wasn't specified. It allows the same command line to + # be used to turn the keyboard on and off. '--off' just kills keyboard processes. + # + + ikilled = False + mypid = os.getpid() + myparent = os.getppid() + myname = "oskb" + for proc in psutil.process_iter(attrs=(["pid"])): + try: + if not proc.pid == mypid and not proc.pid == myparent: + itsname = os.path.basename(proc.name()) + if re.match("^[Pp]ython\d*$", itsname) and len(proc.cmdline()) > 1: + itsname = os.path.basename(proc.cmdline()[1]) + if itsname == myname: + proc.send_signal(9) + ikilled = True + except: + pass + if (ikilled and cmdline.toggle) or cmdline.off: + sys.exit() + + # + # Start the Qt context + # + + app = QApplication([]) + + # + # Make sure Ctrl-C can interrupt oskb + # + + def sigint_handler(*args): + sys.stderr.write("\r") + QApplication.quit() + + signal.signal(signal.SIGINT, sigint_handler) + timer = QTimer() + timer.start(250) + timer.timeout.connect(lambda: None) + + # + # Get our keyboard widget instance + # + + keyboard = oskb.Keyboard() + if cmdline.float: + keyboard.setWindowTitle("On-Screen Keyboard") + keyboard.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus) + if not cmdline.steadymod: + keyboard.setFlashModifiers(True) + else: + if linux and not cmdline.nopushaway: + keyboard.sendScreenState(receiveScreenState) + if not cmdline.flashmod: + keyboard.setFlashModifiers(False) + # quickly make sure X doesn't make a window frame etc. + keyboard.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint) + # Qt.X11BypassWindowManagerHint means no WM border or title, no application focus, not in taskbar + + # also works but creates taskbar entry that will application focus to oskb if pressed + # ( Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.FramelessWindowHint) + + # + # Figure out where and how big we're going to be on the screen + # + + # First take the screen dimensions + desktop = app.desktop() + screen = desktop.screenGeometry() + screenleft = 0 + screentop = 0 + screenwidth = screen.width() + screenheight = screen.height() + # On Linux see if we can improve on that by asking windowmanager for workarea (minus taskbar etc). + if linux: + try: + screenleft, screentop, screenwidth, screenheight = list(wm.getWorkArea()[0:4]) + except: + pass + # set width and height from arguments, defaulting to screen width and quarter of screen height resp. + w = cmdline.width if cmdline.width else screenwidth + h = cmdline.height if cmdline.height else max(250, int(screenheight / 3)) + mw, mh = 70, 70 # Size of minimized keyboard button + # Vertical position + if cmdline.y: + y = cmdline.y + else: + if cmdline.top: + y = screentop + my = screentop + else: + y = screentop + screenheight - h + my = screentop + screenheight - mh + # Horizontal position + if cmdline.x: + x = cmdline.x + else: + if cmdline.left: + x = screenleft + mx = screenleft + elif cmdline.right: + x = screenleft + screenwidth - w + mx = screenleft + screenwidth - mw + else: + x = int(screenleft + (screenwidth / 2) - (w / 2)) + mx = int(screenleft + (screenwidth / 2) - (mw / 2)) + + # Set geometry accordingly + keyboard.setMinimizer(mx, my, mw, mh) + keyboard.setGeometry(x, y, w, h) + + # + # Figure out default keyboard if nothing selected + # + + # (Has to be done before plugging in virtual keyboard, bc that switches map to 'us') + load_keyboards = cmdline.keyboards + if load_keyboards == []: + kbname = 'paddy' if screenwidth > 600 else 'phoney' + tryfirst = kbname + "-" + querySystemKeymap("layout") + if pkg_resources.resource_exists("oskb", "keyboards/" + tryfirst): + load_keyboards = [tryfirst] + else: + load_keyboards = [kbname + "-us"] + + # + # Tell keyboard to send the keypresses to the default handler for OS + # + + if not cmdline.justshow: + plugged = False + try: + plugged = keyboard.sendKeys(im.default().receiveKeys) + except: + sys.stderr.write("Could not set up the virtual keyboard.\n") + + if not plugged: + if linux: + user = getpass.getuser() + sys.stderr.write( + "Try 'sudo setfacl -m m::rw -m u:" + user + ":rw /dev/uinput /dev/input/*'\n" + "See the oskb documentation for more information.\n" + ) + else: + sys.stderr.write( + "Your platform is not yet supported by oskd. Try --justshow if\n" + "you just want to see how pretty oskb is.\n" + ) + sys.exit(-1) + + # + # Tell oskb where to send keymap changes, so we can call setxkbmap to switch as well + # + + if not cmdline.nomap: + keyboard.sendMapChanges(receiveMapChanges) + + # + # Load the keyboard files + # + + for k in load_keyboards: + keyboard.readKeyboard(k) + + # Also works if no startup kbd is specified, because None will load first keyboard + keyboard.setKeyboard(cmdline.start) + + # + # Display the keyboard + # + + keyboard.show() + + sys.exit(app.exec_()) + + + + +def querySystemKeymap(key, default = None): + try: + output = subprocess.check_output(['setxkbmap', '-query']).decode("utf-8") + match = re.search(key + ":\s+(\w+)", output) + if match: + return match.group(1) + else: + return default + except: + return default + + +def receiveMapChanges(keymap): + try: + subprocess.run(["setxkbmap"] + keymap.split(" ")) + except: + pass + + +def receiveScreenState(maximize): + def get_geometry(window): + g = window.get_geometry() + return g.x, g.y, g.width, g.height + + def frame(window): + frame = window + while frame.query_tree().parent != wm.root: + frame = frame.query_tree().parent + return frame + + global moved_windows + if maximize: + moved_windows = [] + for window in wm.getClientList(): + wn = wm.getWmName(window).decode("utf-8") + wx, wy, ww, wh = get_geometry(window) + fx, fy, fw, fh = get_geometry(frame(window)) + bottom = fy + fh + if bottom > y and bottom < y + h and fy < y + h: + need = bottom - y + (fh - wh) + moveby = min(fy, need) + shrinkby = need - moveby + nx, nw = fx, fw + ny = fy - moveby + nh = bottom - fy - shrinkby + wm.setMoveResizeWindow(window, gravity=ewmh.X.SouthWestGravity, x=nx, y=ny, w=nw, h=nh) + moved_windows.append((window, (fx, fy, fw - (fw - ww), fh - (fh - wh)), (nx, ny, nw, nh))) + else: + for w in moved_windows: + window = w[0] + (nx, ny, nw, nh) = w[1] + wm.setMoveResizeWindow(window, gravity=ewmh.X.SouthWestGravity, x=nx, y=ny, w=nw, h=nh) + wm.display.flush() diff --git a/src/lib/oskb/default.css b/src/lib/oskb/default.css new file mode 100644 index 0000000..0e24367 --- /dev/null +++ b/src/lib/oskb/default.css @@ -0,0 +1,33 @@ +QWidget { + background-color: #cccccc; + margin:0px; +} + +QLabel { + font-size: 50%; + background: transparent; + margin: 2px 5px; + qproperty-alignment: 'AlignBottom | AlignLeft'; +} + +.key { + background-color: #eeeeee; + border: 1px solid black; + font-size: _OSKB_FONTSIZE_px; + margin: _OSKB_MARGIN_px; + border-radius: _OSKB_RADIUS_px; +} + +.spacer, .emptyrow { + background-color: #cccccc; + border: none; + color: #cccccc; +} + +.key:not(modifier):pressed, .key.pseudomodifier, .key.held { + background-color: #999999; +} + +.view .key.pseudomodifier, .key.locked { + background-color: #cc9999; +} \ No newline at end of file diff --git a/src/lib/oskb/im/__init__.py b/src/lib/oskb/im/__init__.py new file mode 100644 index 0000000..43fcecf --- /dev/null +++ b/src/lib/oskb/im/__init__.py @@ -0,0 +1,20 @@ +import sys, importlib, pkg_resources +from os.path import basename + +# All this does is "from * import *", if python only accepted that +# https://stackoverflow.com/questions/43059267/how-to-do-from-module-import-using-importlib +for module in pkg_resources.resource_listdir("oskb.im", ""): + if module.startswith("_"): + continue + module = basename(module)[:-3] + modhandle = importlib.import_module("oskb.im." + module) + names = [x for x in modhandle.__dict__ if not x.startswith("_")] + globals().update({k: getattr(modhandle, k) for k in names}) + + +# Return a default handler for a given platform + + +def default(): + if sys.platform.startswith("linux"): + return UInput() diff --git a/src/lib/oskb/im/uinput.py b/src/lib/oskb/im/uinput.py new file mode 100644 index 0000000..e5b4bb2 --- /dev/null +++ b/src/lib/oskb/im/uinput.py @@ -0,0 +1,13 @@ +import sys + +if sys.platform.startswith("linux"): + + import evdev + + class UInput: + def __init__(self): + self.uinput = evdev.UInput(name="oskb") + + def receiveKeys(self, keycode, keyevent): + self.uinput.write(evdev.ecodes.EV_KEY, keycode, keyevent) + self.uinput.syn() diff --git a/src/lib/oskb/keyboards/_new b/src/lib/oskb/keyboards/_new new file mode 100755 index 0000000..5d56cd1 --- /dev/null +++ b/src/lib/oskb/keyboards/_new @@ -0,0 +1,18 @@ +{ + "format": "oskb keyboard", + "formatversion": 1, + "description": "unnamed keyboard", + "views": { + "default" : { + "columns": [ + { + "rows": [ + { + "keys": [] + } + ] + } + ] + } + } +} diff --git a/src/lib/oskb/keyboards/paddy-de b/src/lib/oskb/keyboards/paddy-de new file mode 100644 index 0000000..2f9cda5 --- /dev/null +++ b/src/lib/oskb/keyboards/paddy-de @@ -0,0 +1,2103 @@ +{ + "format": "oskb keyboard", + "formatversion": 1, + "description": "DE - QWERTZ", + "keymap": "de", + "style": ".key {\n font-size: 65%;\n}\n\n.key.bigger {\n font-size: 80%;\n}\n\n.key.ctrlalt, .key.smaller {\n font-size: 45%;\n}\n\n.key.smallest {\n font-size: 35%;\n}\n\n.grey {\n background-color: #bbbbbb;\n}\n\n.lpsymbol {\n font-size: 50%;\n color: blue;\n qproperty-alignment: 'AlignTop | AlignRight';\n}", + "views": { + "default": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "type": "key", + "caption": "esc", + "single": { + "send": { + "keycode": "1", + "name": "esc", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "°\n^", + "single": { + "send": { + "keycode": "41", + "name": "`", + "printable": true + } + }, + "width": 0.8, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "!\n1", + "single": { + "send": { + "keycode": "2", + "name": "1", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "\"\n2", + "single": { + "send": { + "keycode": "3", + "name": "2", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "§\n3", + "single": { + "send": { + "keycode": "4", + "name": "3", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "$\n4", + "single": { + "send": { + "keycode": "5", + "name": "4", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "%\n5", + "single": { + "send": { + "keycode": "6", + "name": "5", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "&&\n6", + "single": { + "send": { + "keycode": "7", + "name": "6", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "/\n7", + "single": { + "send": { + "keycode": "8", + "name": "7", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "(\n8", + "single": { + "send": { + "keycode": "9", + "name": "8", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": ")\n9", + "single": { + "send": { + "keycode": "10", + "name": "9", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "=\n0", + "single": { + "send": { + "keycode": "11", + "name": "0", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "?\nß", + "single": { + "send": { + "keycode": "12", + "name": "-", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "`\n´", + "single": { + "send": { + "keycode": "13", + "name": "=", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "⌫", + "single": { + "send": { + "keycode": "14", + "name": "backspace", + "printable": false + } + }, + "width": 1.7, + "class": "grey" + } + ], + "type": "emptyrow", + "height": 1.0 + }, + { + "keys": [ + { + "type": "key", + "caption": "⇤\n⇥", + "single": { + "send": { + "keycode": "15", + "name": "tab", + "printable": false + } + }, + "width": 1.5, + "class": "grey" + }, + { + "caption": "Q", + "single": { + "send": { + "keycode": "16", + "name": "q", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "W", + "single": { + "send": { + "keycode": "17", + "name": "w", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "E", + "single": { + "send": { + "keycode": "18", + "name": "e", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "R", + "single": { + "send": { + "keycode": "19", + "name": "r", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "T", + "single": { + "send": { + "keycode": "20", + "name": "t", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "Z", + "single": { + "send": { + "keycode": "21", + "name": "y", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "caption": "U", + "single": { + "send": { + "keycode": "22", + "name": "u", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "I", + "single": { + "send": { + "keycode": "23", + "name": "i", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "O", + "single": { + "send": { + "keycode": "24", + "name": "o", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "P", + "single": { + "send": { + "keycode": "25", + "name": "p", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "Ü", + "single": { + "send": { + "keycode": "26", + "name": "[", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "*\n+", + "single": { + "send": { + "keycode": "27", + "name": "]", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "#\n'", + "single": { + "send": { + "keycode": "43", + "name": "\\", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + } + ] + }, + { + "keys": [ + { + "type": "key", + "caption": "⇪", + "single": { + "modifier": { + "keycode": "42", + "name": "shift", + "action": "lock", + "printable": true + } + }, + "width": 1.8, + "class": "grey" + }, + { + "caption": "A", + "single": { + "send": { + "keycode": "30", + "name": "a", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "S", + "single": { + "send": { + "keycode": "31", + "name": "s", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "D", + "single": { + "send": { + "keycode": "32", + "name": "d", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "F", + "single": { + "send": { + "keycode": "33", + "name": "f", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "G", + "single": { + "send": { + "keycode": "34", + "name": "g", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "H", + "single": { + "send": { + "keycode": "35", + "name": "h", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "J", + "single": { + "send": { + "keycode": "36", + "name": "j", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "K", + "single": { + "send": { + "keycode": "37", + "name": "k", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "L", + "single": { + "send": { + "keycode": "38", + "name": "l", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "Ö", + "single": { + "send": { + "keycode": "39", + "name": ";", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Ä", + "single": { + "send": { + "keycode": "40", + "name": "'", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "↵", + "single": { + "send": { + "keycode": "28", + "name": "enter", + "printable": false + } + }, + "width": 1.7, + "class": "grey" + } + ] + }, + { + "keys": [ + { + "caption": "⇧", + "single": { + "modifier": { + "keycode": "42", + "name": "shift", + "action": "toggle", + "printable": true + } + }, + "width": 2.2 + }, + { + "caption": "Z", + "single": { + "send": { + "keycode": "44", + "name": "z", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "X", + "single": { + "send": { + "keycode": "45", + "name": "x", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "C", + "single": { + "send": { + "keycode": "46", + "name": "c", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "V", + "single": { + "send": { + "keycode": "47", + "name": "v", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "B", + "single": { + "send": { + "keycode": "48", + "name": "b", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "N", + "single": { + "send": { + "keycode": "49", + "name": "n", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "M", + "single": { + "send": { + "keycode": "50", + "name": "m", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": ";\n,", + "single": { + "send": { + "keycode": "51", + "name": ",", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": ":\n.", + "single": { + "send": { + "keycode": "52", + "name": ".", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "_\n-", + "single": { + "send": { + "keycode": "53", + "name": "/", + "printable": true + } + }, + "width": 1.0, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "⇧", + "single": { + "modifier": { + "keycode": "54", + "name": "rightshift", + "action": "toggle", + "printable": true + } + }, + "width": 2.3, + "long": { + "modifier": { + "keycode": "54", + "name": "rightshift", + "action": "lock", + "printable": true + } + } + } + ] + }, + { + "keys": [ + { + "caption": "🌐", + "class": "grey", + "single": { + "keyboard": { + "name": "_chooser" + } + }, + "width": 0.9 + }, + { + "width": 0.9, + "caption": "⌨\n▼", + "class": "grey smaller", + "single": { + "keyboard": { + "name": "_minimized" + } + } + }, + { + "width": 0.9, + "caption": "Strg", + "single": { + "modifier": { + "keycode": "29", + "name": "ctrl", + "action": "toggle", + "printable": false + } + }, + "long": { + "modifier": { + "keycode": "29", + "name": "ctrl", + "action": "lock", + "printable": false + } + }, + "class": "ctrlalt smallest", + "double": {} + }, + { + "width": 0.9, + "caption": "Alt", + "single": { + "modifier": { + "keycode": "56", + "name": "alt", + "action": "toggle", + "printable": false + } + }, + "class": "ctrlalt smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "⌘❖", + "single": { + "modifier": { + "keycode": "125", + "action": "toggle", + "printable": false + } + }, + "width": 0.9, + "double": { + "send": { + "keycode": "125", + "name": "meta", + "printable": false + } + }, + "long": { + "modifier": { + "keycode": "125", + "name": "meta", + "action": "lock", + "printable": false + } + } + }, + { + "width": 8.6, + "single": { + "send": { + "keycode": "57", + "name": " ", + "printable": true + } + } + }, + { + "type": "key", + "caption": "⌘❖", + "single": { + "modifier": { + "keycode": "126", + "name": "rightmeta", + "action": "toggle", + "printable": false + } + }, + "width": 0.9, + "long": { + "modifier": { + "keycode": "126", + "name": "rightmeta", + "action": "lock", + "printable": false + } + } + }, + { + "type": "key", + "caption": "AltGr", + "single": { + "modifier": { + "keycode": "100", + "name": "rightalt", + "action": "toggle", + "printable": false + } + }, + "width": 0.9, + "class": "ctrlalt smallest", + "long": { + "modifier": { + "keycode": "100", + "name": "rightalt", + "action": "lock", + "printable": false + } + }, + "double": {} + }, + { + "type": "key", + "caption": "Strg", + "single": { + "modifier": { + "keycode": "97", + "name": "rightctrl", + "action": "toggle", + "printable": false + } + }, + "width": 0.9, + "class": "ctrlalt smallest", + "long": { + "modifier": { + "keycode": "97", + "name": "rightctrl", + "action": "lock", + "printable": false + } + }, + "double": {} + }, + { + "type": "key", + "caption": "⌨ 2", + "single": { + "view": { + "name": "rightside" + } + }, + "width": 0.9, + "class": "grey smaller" + } + ] + } + ] + }, + { + "rows": [ + { + "keys": [ + { + "type": "key", + "caption": "Druck\nS-Abf", + "single": { + "send": { + "keycode": "99", + "name": "sysrq", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Rollen\n⇩", + "single": { + "send": { + "keycode": "70", + "name": "scrolllock", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "width": 0.8, + "caption": "pause\nUntbr", + "class": "smallest", + "single": {}, + "double": {}, + "long": {} + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "Einfg", + "single": { + "send": { + "keycode": "110", + "name": "insert", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Pos 1", + "single": { + "send": { + "keycode": "102", + "name": "home", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Bild\n⬆", + "single": { + "send": { + "keycode": "104", + "name": "pageup", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "Entf", + "single": { + "send": { + "keycode": "111", + "name": "delete", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Ende", + "single": { + "send": { + "keycode": "107", + "name": "end", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Bild\n⬇", + "single": { + "send": { + "keycode": "109", + "name": "pagedown", + "printable": false + } + }, + "width": 0.8, + "class": "smallest", + "double": {}, + "long": {} + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "spacer", + "width": 0.8 + }, + { + "type": "key", + "caption": "▲", + "single": { + "send": { + "keycode": "103", + "name": "up", + "printable": false + } + }, + "width": 0.8 + }, + { + "type": "spacer", + "width": 0.8 + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "◀", + "single": { + "send": { + "keycode": "105", + "name": "left", + "printable": false + } + }, + "width": 0.8 + }, + { + "type": "key", + "caption": "▼", + "single": { + "send": { + "keycode": "108", + "name": "down", + "printable": false + } + }, + "width": 0.8 + }, + { + "type": "key", + "caption": "▶", + "single": { + "send": { + "keycode": "106", + "name": "right", + "printable": false + } + }, + "width": 0.8 + } + ], + "type": "emptyrow" + } + ] + } + ], + "widthInUnits": 10.0, + "heightInUnits": 4.0 + }, + "rightside": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "type": "key", + "width": 1.0, + "caption": "esc", + "class": "smaller", + "single": {}, + "double": {}, + "long": {} + }, + { + "type": "key", + "width": 1.0, + "caption": "🔅" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔆", + "style": "font-size: 100%" + }, + { + "type": "key", + "caption": "⏪", + "single": { + "send": { + "keycode": "168", + "name": "rewind", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "⏯", + "single": { + "send": { + "keycode": "164", + "name": "playpause", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "key", + "width": 1.0, + "caption": "⏩" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔇" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔉" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔊" + }, + { + "type": "key", + "width": 1.0, + "caption": "⏏" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "F1", + "single": { + "send": { + "keycode": "59", + "name": "f1", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F2", + "single": { + "send": { + "keycode": "60", + "name": "f2", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F3", + "single": { + "send": { + "keycode": "61", + "name": "f3", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F4", + "single": { + "send": { + "keycode": "62", + "name": "f4", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F5", + "single": { + "send": { + "keycode": "63", + "name": "f5", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F6", + "single": { + "send": { + "keycode": "64", + "name": "f6", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F7", + "single": { + "send": { + "keycode": "65", + "name": "f7", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F8", + "single": { + "send": { + "keycode": "66", + "name": "f8", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F9", + "single": { + "send": { + "keycode": "67", + "name": "f9", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F10", + "single": { + "send": { + "keycode": "68", + "name": "f10", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "F11", + "single": { + "send": { + "keycode": "87", + "name": "f11", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F12", + "single": { + "send": { + "keycode": "88", + "name": "f12", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F13", + "single": { + "send": { + "keycode": "183", + "name": "f13", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F14", + "single": { + "send": { + "keycode": "184", + "name": "f14", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "width": 1.0, + "caption": "F15", + "class": "smaller", + "single": { + "send": { + "keycode": "285", + "name": "f15", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F16", + "class": "smaller", + "single": { + "send": { + "keycode": "186", + "name": "f16", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F17", + "class": "smaller", + "single": { + "send": { + "keycode": "187", + "name": "f17", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F18", + "class": "smaller", + "single": { + "send": { + "keycode": "188", + "name": "f18", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F19", + "class": "smaller", + "single": { + "send": { + "keycode": "189", + "name": "f19", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F20", + "class": "smaller", + "single": { + "send": { + "keycode": "190", + "name": "f20", + "printable": false + } + } + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "width": 1.0, + "caption": "⌨\n▼", + "class": "grey smaller", + "single": { + "keyboard": { + "name": "_minimized" + } + } + }, + { + "caption": "⇧", + "single": { + "modifier": { + "keycode": "42", + "name": "shift", + "action": "toggle", + "printable": true + } + }, + "width": 2.0, + "double": {}, + "long": {} + }, + { + "type": "spacer", + "width": 5.0 + }, + { + "type": "key", + "caption": "⇧", + "single": { + "send": { + "keycode": "54", + "name": "rightshift", + "printable": false + }, + "modifier": { + "keycode": "54", + "name": "rightshift", + "action": "toggle", + "printable": true + } + }, + "width": 2.0, + "double": {}, + "long": {} + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "⌨ 1", + "single": { + "view": { + "name": "default" + } + }, + "width": 1.0, + "class": "grey smaller" + }, + { + "width": 1.0, + "caption": "Strg", + "single": { + "modifier": { + "keycode": "29", + "name": "ctrl", + "action": "toggle", + "printable": false + } + }, + "long": { + "modifier": { + "keycode": "29", + "name": "ctrl", + "action": "lock", + "printable": false + } + }, + "class": "ctrlalt smallest", + "double": {} + }, + { + "width": 1.0, + "caption": "Alt", + "single": { + "modifier": { + "keycode": "56", + "name": "alt", + "action": "toggle", + "printable": false + } + }, + "class": "ctrlalt smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "⌘ ❖", + "single": { + "modifier": { + "keycode": "125", + "name": "meta", + "action": "toggle", + "printable": false + } + }, + "width": 1.0, + "long": { + "modifier": { + "keycode": "125", + "name": "meta", + "action": "lock", + "printable": false + } + } + }, + { + "width": 3.0, + "single": { + "send": { + "keycode": "57", + "name": " ", + "printable": true + } + } + }, + { + "type": "key", + "caption": "⌘ ❖", + "single": { + "modifier": { + "keycode": "126", + "name": "rightmeta", + "action": "toggle", + "printable": false + } + }, + "width": 1.0, + "long": { + "modifier": { + "keycode": "126", + "name": "rightmeta", + "action": "lock", + "printable": false + } + } + }, + { + "type": "key", + "caption": "AltGr", + "single": { + "modifier": { + "keycode": "100", + "name": "rightalt", + "action": "toggle", + "printable": false + } + }, + "width": 1.0, + "class": "ctrlalt smallest", + "long": { + "modifier": { + "keycode": "100", + "name": "rightalt", + "action": "lock", + "printable": false + } + }, + "double": {} + }, + { + "type": "key", + "caption": "Strg", + "single": { + "modifier": { + "keycode": "97", + "name": "rightctrl", + "action": "toggle", + "printable": false + } + }, + "width": 1.0, + "class": "ctrlalt smallest", + "long": { + "modifier": { + "keycode": "97", + "name": "rightctrl", + "action": "lock", + "printable": false + } + }, + "double": {} + } + ], + "type": "emptyrow" + } + ] + }, + { + "rows": [ + { + "keys": [ + { + "type": "key", + "caption": "Druck\nS-Abf", + "single": { + "send": { + "keycode": "99", + "name": "sysrq", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Rollen\n⇩", + "single": { + "send": { + "keycode": "70", + "name": "scrolllock", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "width": 1.0, + "caption": "pause\nUntbr", + "class": "smallest", + "single": {}, + "double": {}, + "long": {} + } + ], + "type": "emptyrow", + "height": 0.7 + }, + { + "keys": [ + { + "type": "key", + "caption": "Einfg", + "single": { + "send": { + "keycode": "110", + "name": "insert", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Pos 1", + "single": { + "send": { + "keycode": "102", + "name": "home", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Bild\n⬆", + "single": { + "send": { + "keycode": "104", + "name": "pageup", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "Entf", + "single": { + "send": { + "keycode": "111", + "name": "delete", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Ende", + "single": { + "send": { + "keycode": "107", + "name": "end", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "Bild\n⬇", + "single": { + "send": { + "keycode": "109", + "name": "pagedown", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "spacer", + "width": 1.0 + }, + { + "type": "key", + "caption": "▲", + "single": { + "send": { + "keycode": "103", + "name": "up", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "spacer", + "width": 1.0 + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "◀", + "single": { + "send": { + "keycode": "105", + "name": "left", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "▼", + "single": { + "send": { + "keycode": "108", + "name": "down", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "▶", + "single": { + "send": { + "keycode": "106", + "name": "right", + "printable": false + } + }, + "width": 1.0 + } + ], + "type": "emptyrow" + } + ] + }, + { + "rows": [ + { + "keys": [ + { + "type": "key", + "caption": "Num\n⇩", + "single": { + "send": { + "keycode": "69", + "name": "numlock", + "printable": false + } + }, + "width": 1.0, + "class": "smallest", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "=", + "single": { + "send": { + "name": "=", + "keycode": "13" + } + } + }, + { + "type": "key", + "caption": "/", + "single": { + "send": { + "name": "/", + "keycode": "98" + } + } + }, + { + "type": "key", + "caption": "*", + "single": { + "send": { + "name": "*", + "keycode": "55" + } + } + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "7", + "single": { + "send": { + "name": "7", + "keycode": "71" + } + } + }, + { + "type": "key", + "caption": "8", + "single": { + "send": { + "name": "8", + "keycode": "72" + } + } + }, + { + "type": "key", + "caption": "9", + "single": { + "send": { + "name": "9", + "keycode": "73" + } + } + }, + { + "type": "key", + "caption": "-", + "single": { + "send": { + "name": "-", + "keycode": "74" + } + } + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "4", + "single": { + "send": { + "name": "4", + "keycode": "75" + } + } + }, + { + "type": "key", + "caption": "5", + "single": { + "send": { + "name": "5", + "keycode": "76" + } + } + }, + { + "type": "key", + "caption": "6", + "single": { + "send": { + "name": "6", + "keycode": "77" + } + } + }, + { + "type": "key", + "caption": "+", + "single": { + "send": { + "name": "+", + "keycode": "78" + } + } + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "1", + "single": { + "send": { + "name": "1", + "keycode": "79" + } + } + }, + { + "type": "key", + "caption": "2", + "single": { + "send": { + "name": "2", + "keycode": "80" + } + } + }, + { + "type": "key", + "caption": "3", + "single": { + "send": { + "name": "3", + "keycode": "81" + } + } + }, + { + "type": "spacer", + "width": 1.0 + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "0", + "single": { + "send": { + "keycode": "82", + "name": "0", + "printable": true + } + }, + "width": 2.000000000000001 + }, + { + "type": "key", + "caption": ".", + "single": { + "send": { + "name": ".", + "keycode": "83" + } + } + }, + { + "type": "key", + "caption": "↵", + "single": { + "send": { + "keycode": "96", + "name": "kpenter", + "printable": false + } + }, + "width": 1.0 + } + ], + "type": "emptyrow" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/src/lib/oskb/keyboards/paddy-us b/src/lib/oskb/keyboards/paddy-us new file mode 100644 index 0000000..5036931 --- /dev/null +++ b/src/lib/oskb/keyboards/paddy-us @@ -0,0 +1,2018 @@ +{ + "format": "oskb keyboard", + "formatversion": 1, + "description": "US - QWERTY", + "keymap": "us", + "style": ".key {\n font-size: 65%;\n}\n\n.key.bigger {\n font-size: 80%;\n}\n\n.key.ctrlalt, .key.smaller {\n font-size: 45%;\n}\n\n.key.smallest {\n font-size: 35%;\n}\n\n.grey {\n background-color: #bbbbbb;\n}\n\n.lpsymbol {\n font-size: 50%;\n color: blue;\n qproperty-alignment: 'AlignTop | AlignRight';\n}", + "views": { + "default": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "type": "key", + "caption": "esc", + "single": { + "send": { + "keycode": "1", + "name": "esc", + "printable": false + } + }, + "width": 0.8, + "class": "smaller" + }, + { + "type": "key", + "caption": "~\n`", + "single": { + "send": { + "keycode": "41", + "name": "`", + "printable": true + } + }, + "width": 0.8 + }, + { + "type": "key", + "caption": "!\n1", + "single": { + "send": { + "keycode": "2", + "name": "1", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "@\n2", + "single": { + "send": { + "keycode": "3", + "name": "2", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "#\n3", + "single": { + "send": { + "keycode": "4", + "name": "3", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "$\n4", + "single": { + "send": { + "keycode": "5", + "name": "4", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "%\n5", + "single": { + "send": { + "keycode": "6", + "name": "5", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "^\n6", + "single": { + "send": { + "keycode": "7", + "name": "6", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "&&\n7", + "single": { + "send": { + "keycode": "8", + "name": "7", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "*\n8", + "single": { + "send": { + "keycode": "9", + "name": "8", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "(\n9", + "single": { + "send": { + "keycode": "10", + "name": "9", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": ")\n0", + "single": { + "send": { + "keycode": "11", + "name": "0", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "_\n-", + "single": { + "send": { + "keycode": "12", + "name": "-", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "+\n=", + "single": { + "send": { + "keycode": "13", + "name": "=", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "⌫", + "single": { + "send": { + "keycode": "14", + "name": "backspace", + "printable": false + } + }, + "width": 1.7, + "class": "grey" + } + ], + "type": "emptyrow", + "height": 1.0 + }, + { + "keys": [ + { + "type": "key", + "caption": "⇤\n⇥", + "single": { + "send": { + "keycode": "15", + "name": "tab", + "printable": false + } + }, + "width": 1.5, + "class": "grey" + }, + { + "caption": "Q", + "single": { + "send": { + "keycode": "16", + "name": "q", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "W", + "single": { + "send": { + "keycode": "17", + "name": "w", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "E", + "single": { + "send": { + "keycode": "18", + "name": "e", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "R", + "single": { + "send": { + "keycode": "19", + "name": "r", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "T", + "single": { + "send": { + "keycode": "20", + "name": "t", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "Y", + "single": { + "send": { + "keycode": "21", + "name": "y", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "U", + "single": { + "send": { + "keycode": "22", + "name": "u", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "I", + "single": { + "send": { + "keycode": "23", + "name": "i", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "O", + "single": { + "send": { + "keycode": "24", + "name": "o", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "P", + "single": { + "send": { + "keycode": "25", + "name": "p", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "{\n[", + "single": { + "send": { + "keycode": "26", + "name": "[", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "}\n]", + "single": { + "send": { + "keycode": "27", + "name": "]", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "|\n\\", + "single": { + "send": { + "keycode": "43", + "name": "\\", + "printable": true + } + }, + "width": 1.0 + } + ] + }, + { + "keys": [ + { + "type": "key", + "caption": "⇪", + "single": { + "modifier": { + "keycode": "42", + "name": "shift", + "action": "lock", + "printable": true + } + }, + "width": 1.8, + "class": "grey" + }, + { + "caption": "A", + "single": { + "send": { + "keycode": "30", + "name": "a", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "S", + "single": { + "send": { + "keycode": "31", + "name": "s", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "D", + "single": { + "send": { + "keycode": "32", + "name": "d", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "F", + "single": { + "send": { + "keycode": "33", + "name": "f", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "G", + "single": { + "send": { + "keycode": "34", + "name": "g", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "H", + "single": { + "send": { + "keycode": "35", + "name": "h", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "J", + "single": { + "send": { + "keycode": "36", + "name": "j", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "K", + "single": { + "send": { + "keycode": "37", + "name": "k", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "L", + "single": { + "send": { + "keycode": "38", + "name": "l", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": ":\n;", + "single": { + "send": { + "keycode": "39", + "name": ";", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "\"\n'", + "single": { + "send": { + "keycode": "40", + "name": "'", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "↵", + "single": { + "send": { + "keycode": "28", + "name": "enter", + "printable": false + } + }, + "width": 1.7, + "class": "grey" + } + ] + }, + { + "keys": [ + { + "caption": "⇧", + "single": { + "modifier": { + "keycode": "42", + "name": "shift", + "action": "toggle", + "printable": true + } + }, + "width": 2.2 + }, + { + "caption": "Z", + "single": { + "send": { + "keycode": "44", + "name": "z", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "X", + "single": { + "send": { + "keycode": "45", + "name": "x", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "C", + "single": { + "send": { + "keycode": "46", + "name": "c", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "V", + "single": { + "send": { + "keycode": "47", + "name": "v", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "B", + "single": { + "send": { + "keycode": "48", + "name": "b", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "N", + "single": { + "send": { + "keycode": "49", + "name": "n", + "printable": true + } + }, + "width": 1.0 + }, + { + "caption": "M", + "single": { + "send": { + "keycode": "50", + "name": "m", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "<\n,", + "single": { + "send": { + "keycode": "51", + "name": ",", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": ">\n.", + "single": { + "send": { + "keycode": "52", + "name": ".", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "?\n/", + "single": { + "send": { + "keycode": "53", + "name": "/", + "printable": true + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "⇧", + "single": { + "modifier": { + "keycode": "54", + "name": "rightshift", + "action": "toggle", + "printable": true + } + }, + "width": 2.3, + "long": { + "modifier": { + "keycode": "54", + "name": "rightshift", + "action": "lock", + "printable": true + } + } + } + ] + }, + { + "keys": [ + { + "caption": "🌐", + "class": "grey", + "single": { + "keyboard": { + "name": "_chooser" + } + }, + "width": 0.9 + }, + { + "width": 0.9, + "caption": "⌨\n▼", + "class": "grey smaller", + "single": { + "keyboard": { + "name": "_minimized" + } + }, + "double": {}, + "long": {} + }, + { + "width": 0.9, + "caption": "ctrl", + "single": { + "modifier": { + "keycode": "29", + "name": "ctrl", + "action": "toggle", + "printable": false + } + }, + "long": { + "modifier": { + "keycode": "29", + "name": "ctrl", + "action": "lock", + "printable": false + } + }, + "class": "ctrlalt", + "double": {} + }, + { + "width": 0.9, + "caption": "alt", + "single": { + "modifier": { + "keycode": "56", + "name": "alt", + "action": "toggle", + "printable": false + } + }, + "class": "ctrlalt", + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "⌘❖", + "single": { + "modifier": { + "keycode": "125", + "action": "toggle", + "printable": false + } + }, + "width": 0.9, + "double": { + "send": { + "keycode": "125", + "name": "meta", + "printable": false + } + }, + "long": { + "modifier": { + "keycode": "125", + "name": "meta", + "action": "lock", + "printable": false + } + } + }, + { + "width": 8.6, + "single": { + "send": { + "keycode": "57", + "name": " ", + "printable": true + } + }, + "double": {}, + "long": {} + }, + { + "type": "key", + "caption": "⌘❖", + "single": { + "modifier": { + "keycode": "126", + "name": "rightmeta", + "action": "toggle", + "printable": false + } + }, + "width": 0.9, + "long": { + "modifier": { + "keycode": "126", + "name": "rightmeta", + "action": "lock", + "printable": false + } + }, + "double": {} + }, + { + "type": "key", + "caption": "alt", + "single": { + "modifier": { + "keycode": "100", + "name": "rightalt", + "action": "toggle", + "printable": false + } + }, + "width": 0.9, + "class": "ctrlalt", + "long": { + "modifier": { + "keycode": "100", + "name": "rightalt", + "action": "lock", + "printable": false + } + }, + "double": {} + }, + { + "type": "key", + "caption": "ctrl", + "single": { + "modifier": { + "keycode": "97", + "name": "rightctrl", + "action": "toggle", + "printable": false + } + }, + "width": 0.9, + "class": "ctrlalt", + "long": { + "modifier": { + "keycode": "97", + "name": "rightctrl", + "action": "lock", + "printable": false + } + }, + "double": {} + }, + { + "type": "key", + "caption": "⌨ 2", + "single": { + "view": { + "name": "rightside" + } + }, + "width": 0.9, + "class": "grey smaller", + "double": {}, + "long": {} + } + ] + } + ] + }, + { + "rows": [ + { + "keys": [ + { + "type": "key", + "caption": "sysrq\nprtscr", + "single": { + "send": { + "keycode": "99", + "name": "sysrq", + "printable": false + } + }, + "width": 0.8, + "class": "smallest" + }, + { + "type": "key", + "caption": "scroll\nlock", + "single": { + "send": { + "keycode": "70", + "name": "scrolllock", + "printable": false + } + }, + "width": 0.8, + "class": "smallest" + }, + { + "type": "key", + "width": 0.8, + "caption": "break", + "class": "smallest" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "ins", + "single": { + "send": { + "keycode": "110", + "name": "insert", + "printable": false + } + }, + "width": 0.8, + "class": "smaller" + }, + { + "type": "key", + "caption": "home", + "single": { + "send": { + "keycode": "102", + "name": "home", + "printable": false + } + }, + "width": 0.8, + "class": "smaller" + }, + { + "type": "key", + "caption": "pgup", + "single": { + "send": { + "keycode": "104", + "name": "pageup", + "printable": false + } + }, + "width": 0.8, + "class": "smaller" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "del", + "single": { + "send": { + "keycode": "111", + "name": "delete", + "printable": false + } + }, + "width": 0.8, + "class": "smaller" + }, + { + "type": "key", + "caption": "end", + "single": { + "send": { + "keycode": "107", + "name": "end", + "printable": false + } + }, + "width": 0.8, + "class": "smaller" + }, + { + "type": "key", + "caption": "pgdn", + "single": { + "send": { + "keycode": "109", + "name": "pagedown", + "printable": false + } + }, + "width": 0.8, + "class": "smaller" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "spacer", + "width": 0.8 + }, + { + "type": "key", + "caption": "▲", + "single": { + "send": { + "keycode": "103", + "name": "up", + "printable": false + } + }, + "width": 0.8 + }, + { + "type": "spacer", + "width": 0.8 + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "◀", + "single": { + "send": { + "keycode": "105", + "name": "left", + "printable": false + } + }, + "width": 0.8 + }, + { + "type": "key", + "caption": "▼", + "single": { + "send": { + "keycode": "108", + "name": "down", + "printable": false + } + }, + "width": 0.8 + }, + { + "type": "key", + "caption": "▶", + "single": { + "send": { + "keycode": "106", + "name": "right", + "printable": false + } + }, + "width": 0.8 + } + ], + "type": "emptyrow" + } + ] + } + ], + "widthInUnits": 10.0, + "heightInUnits": 4.0 + }, + "rightside": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "type": "key", + "width": 1.0, + "caption": "esc", + "class": "smaller" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔅" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔆", + "style": "font-size: 100%" + }, + { + "type": "key", + "caption": "⏪", + "single": { + "send": { + "keycode": "168", + "name": "rewind", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "⏯", + "single": { + "send": { + "keycode": "164", + "name": "playpause", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "key", + "width": 1.0, + "caption": "⏩" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔇" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔉" + }, + { + "type": "key", + "width": 1.0, + "caption": "🔊" + }, + { + "type": "key", + "width": 1.0, + "caption": "⏏" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "F1", + "single": { + "send": { + "keycode": "59", + "name": "f1", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F2", + "single": { + "send": { + "keycode": "60", + "name": "f2", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F3", + "single": { + "send": { + "keycode": "61", + "name": "f3", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F4", + "single": { + "send": { + "keycode": "62", + "name": "f4", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F5", + "single": { + "send": { + "keycode": "63", + "name": "f5", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F6", + "single": { + "send": { + "keycode": "64", + "name": "f6", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F7", + "single": { + "send": { + "keycode": "65", + "name": "f7", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F8", + "single": { + "send": { + "keycode": "66", + "name": "f8", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F9", + "single": { + "send": { + "keycode": "67", + "name": "f9", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F10", + "single": { + "send": { + "keycode": "68", + "name": "f10", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "F11", + "single": { + "send": { + "keycode": "87", + "name": "f11", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F12", + "single": { + "send": { + "keycode": "88", + "name": "f12", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F13", + "single": { + "send": { + "keycode": "183", + "name": "f13", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "F14", + "single": { + "send": { + "keycode": "184", + "name": "f14", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "width": 1.0, + "caption": "F15", + "class": "smaller", + "single": { + "send": { + "keycode": "285", + "name": "f15", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F16", + "class": "smaller", + "single": { + "send": { + "keycode": "186", + "name": "f16", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F17", + "class": "smaller", + "single": { + "send": { + "keycode": "187", + "name": "f17", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F18", + "class": "smaller", + "single": { + "send": { + "keycode": "188", + "name": "f18", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F19", + "class": "smaller", + "single": { + "send": { + "keycode": "189", + "name": "f19", + "printable": false + } + } + }, + { + "type": "key", + "width": 1.0, + "caption": "F20", + "class": "smaller", + "single": { + "send": { + "keycode": "190", + "name": "f20", + "printable": false + } + } + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "width": 1.0, + "caption": "⌨\n▼", + "class": "grey smaller", + "single": { + "keyboard": { + "name": "_minimized" + } + }, + "double": {}, + "long": {} + }, + { + "caption": "shift", + "single": { + "modifier": { + "keycode": "42", + "name": "shift", + "action": "toggle", + "printable": true + } + }, + "width": 2.0, + "class": "smaller" + }, + { + "type": "spacer", + "width": 5.0 + }, + { + "type": "key", + "caption": "shift", + "single": { + "send": { + "keycode": "54", + "name": "rightshift", + "printable": false + }, + "modifier": { + "keycode": "54", + "name": "rightshift", + "action": "toggle", + "printable": true + } + }, + "width": 2.0, + "class": "smaller" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "⌨ 1", + "single": { + "view": { + "name": "default" + } + }, + "width": 1.0, + "class": "grey smaller", + "double": {}, + "long": {} + }, + { + "width": 1.0, + "caption": "ctrl", + "single": { + "modifier": { + "keycode": "29", + "name": "ctrl", + "action": "toggle", + "printable": false + } + }, + "long": { + "modifier": { + "keycode": "29", + "name": "ctrl", + "action": "lock", + "printable": false + } + }, + "class": "ctrlalt" + }, + { + "width": 1.0, + "caption": "alt", + "single": { + "modifier": { + "keycode": "56", + "name": "alt", + "action": "toggle", + "printable": false + } + }, + "class": "ctrlalt" + }, + { + "type": "key", + "caption": "⌘ ❖", + "single": { + "modifier": { + "keycode": "125", + "name": "meta", + "action": "toggle", + "printable": false + } + }, + "width": 1.0, + "long": { + "modifier": { + "keycode": "125", + "name": "meta", + "action": "lock", + "printable": false + } + } + }, + { + "width": 3.0, + "single": { + "send": { + "keycode": "57", + "name": " ", + "printable": true + } + } + }, + { + "type": "key", + "caption": "⌘ ❖", + "single": { + "modifier": { + "keycode": "126", + "name": "rightmeta", + "action": "toggle", + "printable": false + } + }, + "width": 1.0, + "long": { + "modifier": { + "keycode": "126", + "name": "rightmeta", + "action": "lock", + "printable": false + } + } + }, + { + "type": "key", + "caption": "alt", + "single": { + "modifier": { + "keycode": "100", + "name": "rightalt", + "action": "toggle", + "printable": false + } + }, + "width": 1.0, + "class": "ctrlalt", + "long": { + "modifier": { + "keycode": "100", + "name": "rightalt", + "action": "lock", + "printable": false + } + } + }, + { + "type": "key", + "caption": "ctrl", + "single": { + "modifier": { + "keycode": "97", + "name": "rightctrl", + "action": "toggle", + "printable": false + } + }, + "width": 1.0, + "class": "ctrlalt", + "long": { + "modifier": { + "keycode": "97", + "name": "rightctrl", + "action": "lock", + "printable": false + } + } + } + ], + "type": "emptyrow" + } + ] + }, + { + "rows": [ + { + "keys": [ + { + "type": "key", + "caption": "sysrq\nprtscr", + "single": { + "send": { + "keycode": "99", + "name": "sysrq", + "printable": false + } + }, + "width": 1.0, + "class": "smallest" + }, + { + "type": "key", + "caption": "scroll\nlock", + "single": { + "send": { + "keycode": "70", + "name": "scrolllock", + "printable": false + } + }, + "width": 1.0, + "class": "smallest" + }, + { + "type": "key", + "width": 1.0, + "caption": "break", + "class": "smallest" + } + ], + "type": "emptyrow", + "height": 0.7 + }, + { + "keys": [ + { + "type": "key", + "caption": "ins", + "single": { + "send": { + "keycode": "110", + "name": "insert", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "home", + "single": { + "send": { + "keycode": "102", + "name": "home", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "pgup", + "single": { + "send": { + "keycode": "104", + "name": "pageup", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "del", + "single": { + "send": { + "keycode": "111", + "name": "delete", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "end", + "single": { + "send": { + "keycode": "107", + "name": "end", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + }, + { + "type": "key", + "caption": "pgdn", + "single": { + "send": { + "keycode": "109", + "name": "pagedown", + "printable": false + } + }, + "width": 1.0, + "class": "smaller" + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "spacer", + "width": 1.0 + }, + { + "type": "key", + "caption": "▲", + "single": { + "send": { + "keycode": "103", + "name": "up", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "spacer", + "width": 1.0 + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "◀", + "single": { + "send": { + "keycode": "105", + "name": "left", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "▼", + "single": { + "send": { + "keycode": "108", + "name": "down", + "printable": false + } + }, + "width": 1.0 + }, + { + "type": "key", + "caption": "▶", + "single": { + "send": { + "keycode": "106", + "name": "right", + "printable": false + } + }, + "width": 1.0 + } + ], + "type": "emptyrow" + } + ] + }, + { + "rows": [ + { + "keys": [ + { + "type": "key", + "caption": "num\nlock", + "single": { + "send": { + "keycode": "69", + "name": "numlock", + "printable": false + } + }, + "width": 1.0, + "class": "smallest" + }, + { + "type": "key", + "caption": "=", + "single": { + "send": { + "name": "=", + "keycode": "13" + } + } + }, + { + "type": "key", + "caption": "/", + "single": { + "send": { + "name": "/", + "keycode": "98" + } + } + }, + { + "type": "key", + "caption": "*", + "single": { + "send": { + "name": "*", + "keycode": "55" + } + } + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "7", + "single": { + "send": { + "name": "7", + "keycode": "71" + } + } + }, + { + "type": "key", + "caption": "8", + "single": { + "send": { + "name": "8", + "keycode": "72" + } + } + }, + { + "type": "key", + "caption": "9", + "single": { + "send": { + "name": "9", + "keycode": "73" + } + } + }, + { + "type": "key", + "caption": "-", + "single": { + "send": { + "name": "-", + "keycode": "74" + } + } + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "4", + "single": { + "send": { + "name": "4", + "keycode": "75" + } + } + }, + { + "type": "key", + "caption": "5", + "single": { + "send": { + "name": "5", + "keycode": "76" + } + } + }, + { + "type": "key", + "caption": "6", + "single": { + "send": { + "name": "6", + "keycode": "77" + } + } + }, + { + "type": "key", + "caption": "+", + "single": { + "send": { + "name": "+", + "keycode": "78" + } + } + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "1", + "single": { + "send": { + "name": "1", + "keycode": "79" + } + } + }, + { + "type": "key", + "caption": "2", + "single": { + "send": { + "name": "2", + "keycode": "80" + } + } + }, + { + "type": "key", + "caption": "3", + "single": { + "send": { + "name": "3", + "keycode": "81" + } + } + }, + { + "type": "spacer", + "width": 1.0 + } + ], + "type": "emptyrow" + }, + { + "keys": [ + { + "type": "key", + "caption": "0", + "single": { + "send": { + "keycode": "82", + "name": "0", + "printable": true + } + }, + "width": 2.000000000000001 + }, + { + "type": "key", + "caption": ".", + "single": { + "send": { + "name": ".", + "keycode": "83" + } + } + }, + { + "type": "key", + "caption": "↵", + "single": { + "send": { + "keycode": "96", + "name": "kpenter", + "printable": false + } + }, + "width": 1.0 + } + ], + "type": "emptyrow" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/src/lib/oskb/keyboards/phoney-de b/src/lib/oskb/keyboards/phoney-de new file mode 100644 index 0000000..5057e59 --- /dev/null +++ b/src/lib/oskb/keyboards/phoney-de @@ -0,0 +1,2001 @@ +{ + "format": "oskb keyboard", + "formatversion": 1, + "description": "DE - QWERTZ", + "keymap": "de", + "style": ".grey {\n background-color: #aaaaaa;\n}\n\n.key.row4, .key.view_arrows {\n font-size: 65%;\n}\n\n.key.row1.view_arrows {\n font-size: 60%;\n}\n\n.lpsymbol {\n font-size: 50%;\n color: blue;\n qproperty-alignment: 'AlignTop | AlignRight';\n}", + "views": { + "default": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "caption": "q", + "single": { + "send": { + "name": "q", + "keycode": "16" + } + } + }, + { + "caption": "w", + "single": { + "send": { + "name": "w", + "keycode": "17" + } + } + }, + { + "caption": "e", + "single": { + "send": { + "name": "e", + "keycode": "18" + } + } + }, + { + "caption": "r", + "single": { + "send": { + "name": "r", + "keycode": "19" + } + } + }, + { + "caption": "t", + "single": { + "send": { + "name": "t", + "keycode": "20" + } + } + }, + { + "caption": "z", + "single": { + "send": { + "name": "z", + "keycode": "21" + } + } + }, + { + "caption": "u", + "single": { + "send": { + "name": "u", + "keycode": "22" + } + } + }, + { + "caption": "i", + "single": { + "send": { + "name": "i", + "keycode": "23" + } + } + }, + { + "caption": "o", + "single": { + "send": { + "name": "o", + "keycode": "24" + } + } + }, + { + "caption": "p", + "single": { + "send": { + "name": "p", + "keycode": "25" + } + } + }, + { + "caption": "ü", + "single": { + "send": { + "name": "ü", + "keycode": "26" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "a", + "single": { + "send": { + "name": "a", + "keycode": "30" + } + } + }, + { + "caption": "s", + "single": { + "send": { + "name": "s", + "keycode": "31" + } + } + }, + { + "caption": "d", + "single": { + "send": { + "name": "d", + "keycode": "32" + } + } + }, + { + "caption": "f", + "single": { + "send": { + "name": "f", + "keycode": "33" + } + } + }, + { + "caption": "g", + "single": { + "send": { + "name": "g", + "keycode": "34" + } + } + }, + { + "caption": "h", + "single": { + "send": { + "name": "h", + "keycode": "35" + } + } + }, + { + "caption": "j", + "single": { + "send": { + "name": "j", + "keycode": "36" + } + } + }, + { + "caption": "k", + "single": { + "send": { + "name": "k", + "keycode": "37" + } + } + }, + { + "caption": "l", + "single": { + "send": { + "name": "l", + "keycode": "38" + } + } + }, + { + "caption": "ö", + "single": { + "send": { + "name": "ö", + "keycode": "39" + } + } + }, + { + "caption": "ä", + "single": { + "send": { + "name": "ä", + "keycode": "40" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "⇧", + "single": { + "view": { + "name": "shifted", + "until": ".", + "thenview": "default" + } + }, + "long": { + "view": { + "name": "shifted" + } + }, + "width": 1.3 + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "y", + "single": { + "send": { + "name": "y", + "keycode": "44" + } + } + }, + { + "caption": "x", + "single": { + "send": { + "name": "x", + "keycode": "45" + } + } + }, + { + "caption": "c", + "single": { + "send": { + "name": "c", + "keycode": "46" + } + } + }, + { + "caption": "v", + "single": { + "send": { + "name": "v", + "keycode": "47" + } + } + }, + { + "caption": "b", + "single": { + "send": { + "name": "b", + "keycode": "48" + } + } + }, + { + "caption": "n", + "single": { + "send": { + "name": "n", + "keycode": "49" + } + } + }, + { + "caption": "m", + "single": { + "send": { + "name": "m", + "keycode": "50" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "ß", + "single": { + "send": { + "name": "ß", + "keycode": "12" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⌫", + "style": ".key { font-size: 65%; }", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "123", + "class": "grey", + "single": { + "view": { + "name": "numbers" + } + }, + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "Leerzeichen", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + }, + "double": { + "send": { + "name": "1", + "keycode": "52;57" + }, + "view": { + "name": "shifted", + "until": ".", + "thenview": "default" + } + } + }, + { + "width": 2, + "caption": "↵", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ] + }, + "shifted": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "caption": "Q", + "single": { + "send": { + "name": "Q", + "keycode": "42+16" + } + } + }, + { + "caption": "W", + "single": { + "send": { + "name": "W", + "keycode": "42+17" + } + } + }, + { + "caption": "E", + "single": { + "send": { + "name": "E", + "keycode": "42+18" + } + } + }, + { + "caption": "R", + "single": { + "send": { + "name": "R", + "keycode": "42+19" + } + } + }, + { + "caption": "T", + "single": { + "send": { + "name": "T", + "keycode": "42+20" + } + } + }, + { + "caption": "Z", + "single": { + "send": { + "name": "Z", + "keycode": "42+21" + } + } + }, + { + "caption": "U", + "single": { + "send": { + "name": "U", + "keycode": "42+22" + } + } + }, + { + "caption": "I", + "single": { + "send": { + "name": "I", + "keycode": "42+23" + } + } + }, + { + "caption": "O", + "single": { + "send": { + "name": "O", + "keycode": "42+24" + } + } + }, + { + "caption": "P", + "single": { + "send": { + "name": "P", + "keycode": "42+25" + } + } + }, + { + "caption": "Ü", + "single": { + "send": { + "name": "Ü", + "keycode": "42+26" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "A", + "single": { + "send": { + "name": "A", + "keycode": "42+30" + } + } + }, + { + "caption": "S", + "single": { + "send": { + "name": "S", + "keycode": "42+31" + } + } + }, + { + "caption": "D", + "single": { + "send": { + "name": "D", + "keycode": "42+32" + } + } + }, + { + "caption": "F", + "single": { + "send": { + "name": "F", + "keycode": "42+33" + } + } + }, + { + "caption": "G", + "single": { + "send": { + "name": "G", + "keycode": "42+34" + } + } + }, + { + "caption": "H", + "single": { + "send": { + "name": "H", + "keycode": "42+35" + } + } + }, + { + "caption": "J", + "single": { + "send": { + "name": "J", + "keycode": "42+36" + } + } + }, + { + "caption": "K", + "single": { + "send": { + "name": "K", + "keycode": "42+37" + } + } + }, + { + "caption": "L", + "single": { + "send": { + "name": "L", + "keycode": "42+38" + } + } + }, + { + "caption": "Ö", + "single": { + "send": { + "name": "Ö", + "keycode": "42+39" + } + } + }, + { + "caption": "Ä", + "single": { + "send": { + "name": "Ä", + "keycode": "42+40" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "⇧", + "single": { + "view": { + "name": "default" + } + }, + "long": { + "view": { + "name": "shifted" + } + }, + "width": 1.3, + "class": "pseudomodifier" + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "Y", + "single": { + "send": { + "name": "Y", + "keycode": "42+44" + } + } + }, + { + "caption": "X", + "single": { + "send": { + "name": "X", + "keycode": "42+45" + } + } + }, + { + "caption": "C", + "single": { + "send": { + "name": "C", + "keycode": "42+46" + } + } + }, + { + "caption": "V", + "single": { + "send": { + "name": "V", + "keycode": "42+47" + } + } + }, + { + "caption": "B", + "single": { + "send": { + "name": "B", + "keycode": "42+48" + } + } + }, + { + "caption": "N", + "single": { + "send": { + "name": "N", + "keycode": "42+49" + } + } + }, + { + "caption": "M", + "single": { + "send": { + "name": "M", + "keycode": "42+50" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "ẞ", + "single": { + "send": { + "name": "ẞ", + "keycode": "42+100+31" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⌫", + "style": ".key { font-size: 65%; }", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "123", + "class": "grey", + "single": { + "view": { + "name": "numbers" + } + }, + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "Leerzeichen", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + } + }, + { + "width": 2, + "caption": "↵", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ] + }, + "numbers": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "caption": "1", + "single": { + "send": { + "name": "1", + "keycode": "2" + } + } + }, + { + "caption": "2", + "single": { + "send": { + "name": "2", + "keycode": "3" + } + } + }, + { + "caption": "3", + "single": { + "send": { + "name": "3", + "keycode": "4" + } + } + }, + { + "caption": "4", + "single": { + "send": { + "name": "4", + "keycode": "5" + } + } + }, + { + "caption": "5", + "single": { + "send": { + "name": "5", + "keycode": "6" + } + } + }, + { + "caption": "6", + "single": { + "send": { + "name": "6", + "keycode": "7" + } + } + }, + { + "caption": "7", + "single": { + "send": { + "name": "7", + "keycode": "8" + } + } + }, + { + "caption": "8", + "single": { + "send": { + "name": "8", + "keycode": "9" + } + } + }, + { + "caption": "9", + "single": { + "send": { + "name": "9", + "keycode": "10" + } + } + }, + { + "caption": "0", + "single": { + "send": { + "name": "0", + "keycode": "11" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "-", + "single": { + "send": { + "name": "-", + "keycode": "53" + } + } + }, + { + "caption": "/", + "single": { + "send": { + "name": "/", + "keycode": "42+8" + } + } + }, + { + "caption": ":", + "single": { + "send": { + "name": ":", + "keycode": "42+52" + } + } + }, + { + "caption": ";", + "single": { + "send": { + "name": ";", + "keycode": "42+51" + } + } + }, + { + "caption": "(", + "single": { + "send": { + "name": "(", + "keycode": "42+9" + } + } + }, + { + "caption": ")", + "single": { + "send": { + "name": ")", + "keycode": "42+01" + } + } + }, + { + "caption": "€", + "single": { + "send": { + "name": "€", + "keycode": "100+18" + } + } + }, + { + "caption": "&&", + "single": { + "send": { + "name": "&", + "keycode": "42+7" + } + } + }, + { + "caption": "@", + "single": { + "send": { + "name": "@", + "keycode": "100+16" + } + } + }, + { + "caption": "\"", + "single": { + "send": { + "name": "\"", + "keycode": "42+3" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "#+=", + "style": ".key { font-size: 65%; }", + "class": "grey", + "single": { + "view": { + "name": "symbols" + } + }, + "width": 1.3 + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": ".", + "width": 1.4, + "single": { + "send": { + "name": ".", + "keycode": "52" + } + } + }, + { + "caption": ",", + "width": 1.4, + "single": { + "send": { + "name": ",", + "keycode": "51" + } + } + }, + { + "caption": "?", + "width": 1.4, + "single": { + "send": { + "name": "?", + "keycode": "42+12" + } + } + }, + { + "caption": "!", + "width": 1.4, + "single": { + "send": { + "name": "!", + "keycode": "42+2" + } + } + }, + { + "caption": "'", + "width": 1.4, + "single": { + "send": { + "name": "'", + "keycode": "42+43" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⌫", + "style": ".key { font-size: 65%; }", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "ABC", + "class": "grey", + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "single": { + "view": { + "name": "default" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "Leerzeichen", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + } + }, + { + "width": 2, + "caption": "↵", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ] + }, + "symbols": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "caption": "[", + "single": { + "send": { + "name": "[", + "keycode": "100+9" + } + } + }, + { + "caption": "]", + "single": { + "send": { + "name": "]", + "keycode": "100+10" + } + } + }, + { + "caption": "{", + "single": { + "send": { + "name": "{", + "keycode": "100+8" + } + } + }, + { + "caption": "}", + "single": { + "send": { + "name": "}", + "keycode": "100+11" + } + } + }, + { + "caption": "#", + "single": { + "send": { + "name": "#", + "keycode": "43" + } + } + }, + { + "caption": "%", + "single": { + "send": { + "name": "%", + "keycode": "42+6" + } + } + }, + { + "caption": "^", + "single": { + "send": { + "name": "^", + "keycode": "41;41" + } + } + }, + { + "caption": "*", + "single": { + "send": { + "name": "*", + "keycode": "42+27" + } + } + }, + { + "caption": "+", + "single": { + "send": { + "name": "+", + "keycode": "27" + } + } + }, + { + "caption": "=", + "single": { + "send": { + "name": "=", + "keycode": "42+11" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "_", + "single": { + "send": { + "name": "_", + "keycode": "42+53" + } + } + }, + { + "caption": "\\", + "single": { + "send": { + "name": "\\", + "keycode": "100+12" + } + } + }, + { + "caption": "|", + "single": { + "send": { + "name": "|", + "keycode": "100+86" + } + } + }, + { + "caption": "~", + "single": { + "send": { + "name": "~", + "keycode": "100+27" + } + } + }, + { + "caption": "<", + "single": { + "send": { + "name": "<", + "keycode": "86" + } + } + }, + { + "caption": ">", + "single": { + "send": { + "name": ">", + "keycode": "42+86" + } + } + }, + { + "caption": "$", + "single": { + "send": { + "name": "$", + "keycode": "42+5" + } + } + }, + { + "type": "spacer", + "width": 3 + } + ] + }, + { + "keys": [ + { + "caption": "→↑↓", + "style": ".key { font-size: 60%; }", + "class": "grey", + "single": { + "view": { + "name": "arrows" + } + }, + "width": 1.3 + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": ".", + "width": 1.4, + "single": { + "send": { + "name": ".", + "keycode": "52" + } + } + }, + { + "caption": ",", + "width": 1.4, + "single": { + "send": { + "name": ",", + "keycode": "51" + } + } + }, + { + "caption": "?", + "width": 1.4, + "single": { + "send": { + "name": "?", + "keycode": "42+12" + } + } + }, + { + "caption": "!", + "width": 1.4, + "single": { + "send": { + "name": "!", + "keycode": "42+2" + } + } + }, + { + "caption": "'", + "width": 1.4, + "single": { + "send": { + "name": "'", + "keycode": "42+43" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⌫", + "style": ".key { font-size: 65%; }", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "ABC", + "class": "grey", + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "single": { + "view": { + "name": "default" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "Leerzeichen", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + } + }, + { + "width": 2, + "caption": "↵", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ] + }, + "arrows": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "width": 1.2, + "caption": "Esc", + "single": { + "send": { + "name": "escape", + "keycode": "1", + "printable": false + } + } + }, + { + "caption": "F1", + "single": { + "send": { + "name": "f1", + "keycode": "59", + "printable": false + } + } + }, + { + "caption": "F2", + "single": { + "send": { + "name": "f2", + "keycode": "60", + "printable": false + } + } + }, + { + "caption": "F3", + "single": { + "send": { + "name": "f3", + "keycode": "61", + "printable": false + } + } + }, + { + "caption": "F4", + "single": { + "send": { + "name": "f4", + "keycode": "62", + "printable": false + } + } + }, + { + "caption": "F5", + "single": { + "send": { + "name": "f5", + "keycode": "63", + "printable": false + } + } + }, + { + "caption": "F6", + "single": { + "send": { + "name": "f6", + "keycode": "64", + "printable": false + } + } + }, + { + "caption": "F7", + "single": { + "send": { + "name": "f7", + "keycode": "65", + "printable": false + } + } + }, + { + "caption": "F8", + "single": { + "send": { + "name": "f8", + "keycode": "66", + "printable": false + } + } + }, + { + "caption": "F9", + "single": { + "send": { + "name": "f9", + "keycode": "67", + "printable": false + } + } + }, + { + "width": 1.2, + "caption": "F10", + "single": { + "send": { + "name": "f10", + "keycode": "68", + "printable": false + } + } + } + ] + }, + { + "MeasureStdKeyWidthHere": "1", + "keys": [ + { + "caption": "Tab", + "single": { + "send": { + "name": "tab", + "keycode": "15", + "printable": false + } + }, + "width": 2 + }, + { + "type": "spacer", + "width": 1.1 + }, + { + "caption": "Ins", + "single": { + "send": { + "name": "insert", + "keycode": "110", + "printable": false + } + } + }, + { + "caption": "⇱", + "single": { + "send": { + "name": "home", + "keycode": "102", + "printable": false + } + } + }, + { + "caption": "⇞", + "single": { + "send": { + "name": "pgup", + "keycode": "104", + "printable": false + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⭠", + "single": { + "send": { + "name": "left", + "keycode": "105", + "printable": false + } + } + }, + { + "caption": "⭡", + "single": { + "send": { + "name": "up", + "keycode": "103", + "printable": false + } + } + }, + { + "caption": "⭢", + "single": { + "send": { + "name": "right", + "keycode": "106", + "printable": false + } + } + }, + { + "type": "spacer", + "width": 0.7 + } + ] + }, + { + "keys": [ + { + "class": "grey", + "caption": "123", + "single": { + "view": { + "name": "numbers" + } + }, + "width": 1.3 + }, + { + "caption": "⇧", + "style": ".key { font-size: 100%; }", + "single": { + "modifier": { + "name": "shift", + "keycode": "42", + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "shift", + "keycode": "42", + "action": "lock" + } + }, + "width": 1.5 + }, + { + "type": "spacer", + "width": 0.3 + }, + { + "caption": "Del", + "single": { + "send": { + "name": "delete", + "keycode": "111", + "printable": false + } + } + }, + { + "caption": "⇲", + "single": { + "send": { + "name": "end", + "keycode": "107", + "printable": false + } + } + }, + { + "caption": "⇟", + "single": { + "send": { + "name": "pgdn", + "keycode": "109", + "printable": false + } + } + }, + { + "type": "spacer", + "width": 1.2 + }, + { + "caption": "⭣", + "single": { + "send": { + "name": "down", + "keycode": "108", + "printable": false + } + } + }, + { + "type": "spacer", + "width": 0.4 + }, + { + "caption": "⌫", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "ABC", + "class": "grey", + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "single": { + "view": { + "name": "default" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "Leerzeichen", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + } + }, + { + "width": 2, + "caption": "↵", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/src/lib/oskb/keyboards/phoney-us b/src/lib/oskb/keyboards/phoney-us new file mode 100644 index 0000000..42648c9 --- /dev/null +++ b/src/lib/oskb/keyboards/phoney-us @@ -0,0 +1,1947 @@ +{ + "format": "oskb keyboard", + "formatversion": 1, + "description": "US - QWERTY", + "keymap": "us", + "style": ".grey {\n background-color: #aaaaaa;\n}\n\n.key.row4, .key.view_arrows {\n font-size: 65%;\n}\n\n.key.row1.view_arrows {\n font-size: 60%;\n}\n\n.lpsymbol {\n font-size: 50%;\n color: blue;\n qproperty-alignment: 'AlignTop | AlignRight';\n}", + "views": { + "default": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "caption": "q", + "single": { + "send": { + "name": "q", + "keycode": "16" + } + } + }, + { + "caption": "w", + "single": { + "send": { + "name": "w", + "keycode": "17" + } + } + }, + { + "caption": "e", + "single": { + "send": { + "name": "e", + "keycode": "18" + } + } + }, + { + "caption": "r", + "single": { + "send": { + "name": "r", + "keycode": "19" + } + } + }, + { + "caption": "t", + "single": { + "send": { + "name": "t", + "keycode": "20" + } + } + }, + { + "caption": "y", + "single": { + "send": { + "name": "y", + "keycode": "21" + } + } + }, + { + "caption": "u", + "single": { + "send": { + "name": "u", + "keycode": "22" + } + } + }, + { + "caption": "i", + "single": { + "send": { + "name": "i", + "keycode": "23" + } + } + }, + { + "caption": "o", + "single": { + "send": { + "name": "o", + "keycode": "24" + } + } + }, + { + "caption": "p", + "single": { + "send": { + "name": "p", + "keycode": "25" + } + } + } + ] + }, + { + "keys": [ + { + "type": "spacer", + "width": 0.5 + }, + { + "caption": "a", + "single": { + "send": { + "name": "a", + "keycode": "30" + } + } + }, + { + "caption": "s", + "single": { + "send": { + "name": "s", + "keycode": "31" + } + } + }, + { + "caption": "d", + "single": { + "send": { + "name": "d", + "keycode": "32" + } + } + }, + { + "caption": "f", + "single": { + "send": { + "name": "f", + "keycode": "33" + } + } + }, + { + "caption": "g", + "single": { + "send": { + "name": "g", + "keycode": "34" + } + } + }, + { + "caption": "h", + "single": { + "send": { + "name": "h", + "keycode": "35" + } + } + }, + { + "caption": "j", + "single": { + "send": { + "name": "j", + "keycode": "36" + } + } + }, + { + "caption": "k", + "single": { + "send": { + "name": "k", + "keycode": "37" + } + } + }, + { + "caption": "l", + "single": { + "send": { + "name": "l", + "keycode": "38" + } + } + }, + { + "type": "spacer", + "width": 0.5 + } + ] + }, + { + "keys": [ + { + "caption": "⇧", + "single": { + "view": { + "name": "shifted", + "until": ".", + "thenview": "default" + } + }, + "long": { + "view": { + "name": "shifted" + } + }, + "width": 1.3 + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "z", + "single": { + "send": { + "name": "z", + "keycode": "44" + } + } + }, + { + "caption": "x", + "single": { + "send": { + "name": "x", + "keycode": "45" + } + } + }, + { + "caption": "c", + "single": { + "send": { + "name": "c", + "keycode": "46" + } + } + }, + { + "caption": "v", + "single": { + "send": { + "name": "v", + "keycode": "47" + } + } + }, + { + "caption": "b", + "single": { + "send": { + "name": "b", + "keycode": "48" + } + } + }, + { + "caption": "n", + "single": { + "send": { + "name": "n", + "keycode": "49" + } + } + }, + { + "caption": "m", + "single": { + "send": { + "name": "m", + "keycode": "50" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⌫", + "style": ".key { font-size: 65%; }", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "123", + "class": "grey", + "single": { + "view": { + "name": "numbers", + "until": "( |return)", + "thenview": "default" + } + }, + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "space", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + }, + "double": { + "send": { + "name": "1", + "keycode": "52;57" + }, + "view": { + "name": "shifted", + "until": ".", + "thenview": "default" + } + } + }, + { + "width": 2, + "caption": "return", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ], + "widthInUnits": 10.0, + "heightInUnits": 4.0 + }, + "shifted": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "caption": "Q", + "single": { + "send": { + "name": "Q", + "keycode": "42+16" + } + } + }, + { + "caption": "W", + "single": { + "send": { + "name": "W", + "keycode": "42+17" + } + } + }, + { + "caption": "E", + "single": { + "send": { + "name": "E", + "keycode": "42+18" + } + } + }, + { + "caption": "R", + "single": { + "send": { + "name": "R", + "keycode": "42+19" + } + } + }, + { + "caption": "T", + "single": { + "send": { + "name": "T", + "keycode": "42+20" + } + } + }, + { + "caption": "Y", + "single": { + "send": { + "name": "Y", + "keycode": "42+21" + } + } + }, + { + "caption": "U", + "single": { + "send": { + "name": "U", + "keycode": "42+22" + } + } + }, + { + "caption": "I", + "single": { + "send": { + "name": "I", + "keycode": "42+23" + } + } + }, + { + "caption": "O", + "single": { + "send": { + "name": "O", + "keycode": "42+24" + } + } + }, + { + "caption": "P", + "single": { + "send": { + "name": "P", + "keycode": "42+25" + } + } + } + ] + }, + { + "keys": [ + { + "type": "spacer", + "width": 0.5 + }, + { + "caption": "A", + "single": { + "send": { + "name": "A", + "keycode": "42+30" + } + } + }, + { + "caption": "S", + "single": { + "send": { + "name": "S", + "keycode": "42+31" + } + } + }, + { + "caption": "D", + "single": { + "send": { + "name": "D", + "keycode": "42+32" + } + } + }, + { + "caption": "F", + "single": { + "send": { + "name": "F", + "keycode": "42+33" + } + } + }, + { + "caption": "G", + "single": { + "send": { + "name": "G", + "keycode": "42+34" + } + } + }, + { + "caption": "H", + "single": { + "send": { + "name": "H", + "keycode": "42+35" + } + } + }, + { + "caption": "J", + "single": { + "send": { + "name": "J", + "keycode": "42+36" + } + } + }, + { + "caption": "K", + "single": { + "send": { + "name": "K", + "keycode": "42+37" + } + } + }, + { + "caption": "L", + "single": { + "send": { + "name": "L", + "keycode": "42+38" + } + } + }, + { + "type": "spacer", + "width": 0.5 + } + ] + }, + { + "keys": [ + { + "caption": "⇧", + "single": { + "view": { + "name": "default" + } + }, + "long": { + "view": { + "name": "shifted" + } + }, + "width": 1.3, + "class": "pseudomodifier" + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "Z", + "single": { + "send": { + "name": "Z", + "keycode": "42+44" + } + } + }, + { + "caption": "X", + "single": { + "send": { + "name": "X", + "keycode": "42+45" + } + } + }, + { + "caption": "C", + "single": { + "send": { + "name": "C", + "keycode": "42+46" + } + } + }, + { + "caption": "V", + "single": { + "send": { + "name": "V", + "keycode": "42+47" + } + } + }, + { + "caption": "B", + "single": { + "send": { + "name": "B", + "keycode": "42+48" + } + } + }, + { + "caption": "N", + "single": { + "send": { + "name": "N", + "keycode": "42+49" + } + } + }, + { + "caption": "M", + "single": { + "send": { + "name": "M", + "keycode": "42+50" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⌫", + "style": ".key { font-size: 65%; }", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "123", + "class": "grey", + "single": { + "view": { + "name": "numbers" + } + }, + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "space", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + } + }, + { + "width": 2, + "caption": "return", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ], + "widthInUnits": 10.0, + "heightInUnits": 4.0 + }, + "numbers": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "caption": "1", + "single": { + "send": { + "name": "1", + "keycode": "2" + } + } + }, + { + "caption": "2", + "single": { + "send": { + "name": "2", + "keycode": "3" + } + } + }, + { + "caption": "3", + "single": { + "send": { + "name": "3", + "keycode": "4" + } + } + }, + { + "caption": "4", + "single": { + "send": { + "name": "4", + "keycode": "5" + } + } + }, + { + "caption": "5", + "single": { + "send": { + "name": "5", + "keycode": "6" + } + } + }, + { + "caption": "6", + "single": { + "send": { + "name": "6", + "keycode": "7" + } + } + }, + { + "caption": "7", + "single": { + "send": { + "name": "7", + "keycode": "8" + } + } + }, + { + "caption": "8", + "single": { + "send": { + "name": "8", + "keycode": "9" + } + } + }, + { + "caption": "9", + "single": { + "send": { + "name": "9", + "keycode": "10" + } + } + }, + { + "caption": "0", + "single": { + "send": { + "name": "0", + "keycode": "11" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "-", + "single": { + "send": { + "name": "-", + "keycode": "12" + } + } + }, + { + "caption": "/", + "single": { + "send": { + "name": "/", + "keycode": "53" + } + } + }, + { + "caption": ":", + "single": { + "send": { + "name": ":", + "keycode": "42+39" + } + } + }, + { + "caption": ";", + "single": { + "send": { + "name": ";", + "keycode": "39" + } + } + }, + { + "caption": "(", + "single": { + "send": { + "name": "(", + "keycode": "42+10" + } + } + }, + { + "caption": ")", + "single": { + "send": { + "name": ")", + "keycode": "42+11" + } + } + }, + { + "caption": "$", + "single": { + "send": { + "name": "$", + "keycode": "42+5" + } + } + }, + { + "caption": "&&", + "single": { + "send": { + "name": "&", + "keycode": "42+8" + } + } + }, + { + "caption": "@", + "single": { + "send": { + "name": "@", + "keycode": "42+3" + } + } + }, + { + "caption": "\"", + "single": { + "send": { + "name": "\"", + "keycode": "42+40" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "#+=", + "style": ".key { font-size: 65%; }", + "class": "grey", + "single": { + "view": { + "name": "symbols" + } + }, + "width": 1.3 + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": ".", + "width": 1.4, + "single": { + "send": { + "name": ".", + "keycode": "52" + } + } + }, + { + "caption": ",", + "width": 1.4, + "single": { + "send": { + "name": ",", + "keycode": "51" + } + } + }, + { + "caption": "?", + "width": 1.4, + "single": { + "send": { + "name": "?", + "keycode": "42+53" + } + } + }, + { + "caption": "!", + "width": 1.4, + "single": { + "send": { + "name": "!", + "keycode": "42+2" + } + } + }, + { + "caption": "'", + "width": 1.4, + "single": { + "send": { + "name": "'", + "keycode": "40" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⌫", + "style": ".key { font-size: 65%; }", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "ABC", + "class": "grey", + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "single": { + "view": { + "name": "default" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "space", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + } + }, + { + "width": 2, + "caption": "return", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ], + "widthInUnits": 10.0, + "heightInUnits": 4.0 + }, + "symbols": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "caption": "[", + "single": { + "send": { + "name": "[", + "keycode": "26" + } + } + }, + { + "caption": "]", + "single": { + "send": { + "name": "]", + "keycode": "27" + } + } + }, + { + "caption": "{", + "single": { + "send": { + "name": "{", + "keycode": "42+26" + } + } + }, + { + "caption": "}", + "single": { + "send": { + "name": "}", + "keycode": "42+27" + } + } + }, + { + "caption": "#", + "single": { + "send": { + "name": "#", + "keycode": "42+4" + } + } + }, + { + "caption": "%", + "single": { + "send": { + "name": "%", + "keycode": "42+6" + } + } + }, + { + "caption": "^", + "single": { + "send": { + "name": "^", + "keycode": "42+7" + } + } + }, + { + "caption": "*", + "single": { + "send": { + "name": "*", + "keycode": "42+9" + } + } + }, + { + "caption": "+", + "single": { + "send": { + "name": "+", + "keycode": "42+13" + } + } + }, + { + "caption": "=", + "single": { + "send": { + "name": "=", + "keycode": "13" + } + } + } + ] + }, + { + "keys": [ + { + "caption": "_", + "single": { + "send": { + "name": "_", + "keycode": "42+12" + } + } + }, + { + "caption": "\\", + "single": { + "send": { + "name": "\\", + "keycode": "43" + } + } + }, + { + "caption": "|", + "single": { + "send": { + "name": "|", + "keycode": "42+43" + } + } + }, + { + "caption": "~", + "single": { + "send": { + "name": "~", + "keycode": "42+41" + } + } + }, + { + "caption": "<", + "single": { + "send": { + "name": "<", + "keycode": "42+51" + } + } + }, + { + "caption": ">", + "single": { + "send": { + "name": ">", + "keycode": "42+52" + } + } + }, + { + "caption": "`", + "single": { + "send": { + "name": "`", + "keycode": "41" + } + } + }, + { + "type": "spacer", + "width": 3 + } + ] + }, + { + "keys": [ + { + "caption": "→↑↓", + "style": ".key { font-size: 60%; }", + "class": "grey", + "single": { + "view": { + "name": "arrows" + } + }, + "width": 1.3 + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": ".", + "width": 1.4, + "single": { + "send": { + "name": ".", + "keycode": "52" + } + } + }, + { + "caption": ",", + "width": 1.4, + "single": { + "send": { + "name": ",", + "keycode": "51" + } + } + }, + { + "caption": "?", + "width": 1.4, + "single": { + "send": { + "name": "?", + "keycode": "42+53" + } + } + }, + { + "caption": "!", + "width": 1.4, + "single": { + "send": { + "name": "!", + "keycode": "42+2" + } + } + }, + { + "caption": "'", + "width": 1.4, + "single": { + "send": { + "name": "'", + "keycode": "40" + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⌫", + "style": ".key { font-size: 65%; }", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "ABC", + "class": "grey", + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "single": { + "view": { + "name": "default" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "space", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + } + }, + { + "width": 2, + "caption": "return", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ], + "widthInUnits": 10.0, + "heightInUnits": 4.0 + }, + "arrows": { + "columns": [ + { + "rows": [ + { + "keys": [ + { + "width": 1.2, + "caption": "Esc", + "single": { + "send": { + "name": "escape", + "keycode": "1", + "printable": false + } + } + }, + { + "caption": "F1", + "single": { + "send": { + "name": "f1", + "keycode": "59", + "printable": false + } + } + }, + { + "caption": "F2", + "single": { + "send": { + "name": "f2", + "keycode": "60", + "printable": false + } + } + }, + { + "caption": "F3", + "single": { + "send": { + "name": "f3", + "keycode": "61", + "printable": false + } + } + }, + { + "caption": "F4", + "single": { + "send": { + "name": "f4", + "keycode": "62", + "printable": false + } + } + }, + { + "caption": "F5", + "single": { + "send": { + "name": "f5", + "keycode": "63", + "printable": false + } + } + }, + { + "caption": "F6", + "single": { + "send": { + "name": "f6", + "keycode": "64", + "printable": false + } + } + }, + { + "caption": "F7", + "single": { + "send": { + "name": "f7", + "keycode": "65", + "printable": false + } + } + }, + { + "caption": "F8", + "single": { + "send": { + "name": "f8", + "keycode": "66", + "printable": false + } + } + }, + { + "caption": "F9", + "single": { + "send": { + "name": "f9", + "keycode": "67", + "printable": false + } + } + }, + { + "width": 1.2, + "caption": "F10", + "single": { + "send": { + "name": "f10", + "keycode": "68", + "printable": false + } + } + } + ] + }, + { + "MeasureStdKeyWidthHere": "1", + "keys": [ + { + "caption": "Tab", + "single": { + "send": { + "name": "tab", + "keycode": "15", + "printable": false + } + }, + "width": 2 + }, + { + "type": "spacer", + "width": 1.1 + }, + { + "caption": "Ins", + "single": { + "send": { + "name": "insert", + "keycode": "110", + "printable": false + } + } + }, + { + "caption": "⇱", + "single": { + "send": { + "name": "home", + "keycode": "102", + "printable": false + } + } + }, + { + "caption": "⇞", + "single": { + "send": { + "name": "pgup", + "keycode": "104", + "printable": false + } + } + }, + { + "type": "spacer", + "width": 0.2 + }, + { + "caption": "⭠", + "single": { + "send": { + "name": "left", + "keycode": "105", + "printable": false + } + } + }, + { + "caption": "⭡", + "single": { + "send": { + "name": "up", + "keycode": "103", + "printable": false + } + } + }, + { + "caption": "⭢", + "single": { + "send": { + "name": "right", + "keycode": "106", + "printable": false + } + } + }, + { + "type": "spacer", + "width": 0.7 + } + ] + }, + { + "keys": [ + { + "class": "grey", + "caption": "123", + "single": { + "view": { + "name": "numbers" + } + }, + "width": 1.3 + }, + { + "caption": "⇧", + "style": ".key { font-size: 100%; }", + "single": { + "modifier": { + "name": "shift", + "keycode": "42", + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "shift", + "keycode": "42", + "action": "lock" + } + }, + "width": 1.5 + }, + { + "type": "spacer", + "width": 0.3 + }, + { + "caption": "Del", + "single": { + "send": { + "name": "delete", + "keycode": "111", + "printable": false + } + } + }, + { + "caption": "⇲", + "single": { + "send": { + "name": "end", + "keycode": "107", + "printable": false + } + } + }, + { + "caption": "⇟", + "single": { + "send": { + "name": "pgdn", + "keycode": "109", + "printable": false + } + } + }, + { + "type": "spacer", + "width": 1.2 + }, + { + "caption": "⭣", + "single": { + "send": { + "name": "down", + "keycode": "108", + "printable": false + } + } + }, + { + "type": "spacer", + "width": 0.4 + }, + { + "caption": "⌫", + "class": "grey", + "width": 1.3, + "single": { + "send": { + "name": "backspace", + "keycode": "14", + "printable": false + } + } + } + ] + }, + { + "keys": [ + { + "caption": "ABC", + "class": "grey", + "long": { + "keyboard": { + "name": "_chooser" + } + }, + "single": { + "view": { + "name": "default" + } + }, + "width": 1.3, + "extracaptions": { + "lpsymbol": "🌐" + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "c\nt\nl", + "single": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "ctrl", + "keycode": "29", + "printable": false, + "action": "lock" + } + } + }, + { + "width": 0.8, + "style": ".key { font-size: 40%; }", + "caption": "a\nl\nt", + "single": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "toggle" + } + }, + "long": { + "modifier": { + "name": "alt", + "keycode": "56", + "printable": false, + "action": "lock" + } + } + }, + { + "caption": "space", + "width": 5.1, + "single": { + "send": { + "name": " ", + "keycode": "57" + } + } + }, + { + "width": 2, + "caption": "return", + "class": "grey", + "single": { + "send": { + "name": "return", + "keycode": "28", + "printable": false + } + }, + "long": { + "keyboard": { + "name": "_minimized" + } + }, + "extracaptions": { + "lpsymbol": "⌨" + } + } + ] + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/src/lib/oskb/oskb.py b/src/lib/oskb/oskb.py new file mode 100644 index 0000000..60796f8 --- /dev/null +++ b/src/lib/oskb/oskb.py @@ -0,0 +1,661 @@ +import os, sys, re, json, subprocess +from functools import partial +import pkg_resources + +from PyQt5.QtCore import QTimer, QRect, QSysInfo, QEvent, QSize, Qt +from PyQt5.QtWidgets import ( + QWidget, + QPushButton, + QMainWindow, + QGridLayout, + QHBoxLayout, + QSizePolicy, + QLayout, + QStackedLayout, + QLabel, +) + + +RELEASED = 0 +PRESSED = 1 + +COLUMN_MARGIN = 0.1 + +# key detection timings in milliseconds +LONGPRESS_TIMEOUT = 350 +DOUBLECLICK_TIMEOUT = 200 + +# The keyboard file format has its own version numbering +KEYBOARDFILE_VERSION = 1 + + +class Keyboard(QWidget): + def __init__(self): + super().__init__() + self._modifiers = {} + self._flashmodifiers = True + # This is all for the key-detection state-machine + self._longpresswait = False + self._longtimer = QTimer() + self._stopsinglepress = False + self._doublebutton = None + self._doubletimer = QTimer() + self._doubletimer.setSingleShot(True) + self._doubletimer.timeout.connect(self._doubleTimeout) + + self._viewindex = None + self._kbdname = None + + self._viewuntil = None + self._thenview = None + + self._kbds = {} + + # Create the special 'chooser' keyboard that shows all the loaded keyboards + self._kbds["_chooser"] = { + "views": {"default": {"columns": [{"rows": []}]}}, + } + + # Create the special 'minimized' keyboard that shows one small button + self._kbds["_minimized"] = { + "style": "QWidget {background: transparent;}", + "views": { + "default": { + "columns": [ + {"rows": [{"keys": [{"caption": "⌨", "single": {"keyboard": {"name": "back"}}}]}]} + ], + } + }, + } + + self._view = None + self._viewname = "default" + self._kbd = None + self._sendkeys = None + self._sendmapchanges = None + self._sendscreenstate = None + self._buttonhandler = self._oskbButtonHandler + self._minimizerlocation = QRect(0, 0, 70, 70) + + self._kbdstack = QStackedLayout(self) + + self._stylesheet = pkg_resources.resource_string("oskb", "default.css").decode("utf-8") + + # + # Reimplemented Qt methods + # + + # Make sure show events also calculate proper sizes and first initialise if that hasn't happened yet. + def showEvent(self, event): + self.updateKeyboard() + QWidget.showEvent(self, event) + + # Recalculate the fontsize and mrgaing and change the stylesheets when resizing + def resizeEvent(self, event): + QWidget.resizeEvent(self, event) + if self._view and self.isVisible(): + self.updateKeyboard() + + # We just store the stylesheet, and then only do the super().setStyleSheet() when we've + # recalculated values in updateKeyboard() + def setStyleSheet(self, stylesheet): + self._stylesheet = stylesheet + + # + # Our own public + # + + # specify callback that receives keymap information when user switches keyboards using _chooser + def sendMapChanges(self, function): + if callable(function): + self._sendmapchanges = function + return True + return False + + def sendScreenState(self, function): + if callable(function): + self._sendscreenstate = function + return True + return False + + def sendKeys(self, function): + if callable(function): + self._sendkeys = function + return True + return False + + def setButtonHandler(self, handler=None): + if not handler: + handler = self._oskbButtonHandler + self._buttonhandler = handler + + def setMinimizer(self, mx, my, mw, mh): + self._minimizerlocation = QRect(mx, my, mw, mh) + + def setFlashModifiers(self, mode): + self._flashmodifiers = mode + + def readKeyboard(self, kbdfile): + kbd = None + if os.access(kbdfile, os.R_OK): + with open(kbdfile, "r", encoding="utf-8") as f: + kbd = json.load(f) + elif kbdfile == os.path.basename(kbdfile) and pkg_resources.resource_exists( + "oskb", "keyboards/" + kbdfile + ): + kbd = json.loads(pkg_resources.resource_string("oskb", "keyboards/" + kbdfile)) + if not kbd: + raise FileNotFoundError("Could not find " + kbdfile) + if kbd.get("format") != "oskb keyboard": + raise RuntimeError("Not an oskb keyboard file") + if kbd.get("formatversion") > KEYBOARDFILE_VERSION: + raise RuntimeError("oskb keyboard file for newer oskb version. You must upgrade.") + kbdname = os.path.basename(kbdfile) + self._kbds[kbdname] = kbd + self._updateChooser() + self.initKeyboards() + return os.path.basename(kbdfile) + + def getView(self): + return self._viewname + + def getViews(self): + return self._kbd["views"].keys() + + def _updateChooser(self): + if not self._kbds.get("_chooser"): + return + therows = self._kbds["_chooser"]["views"]["default"]["columns"][0]["rows"] + therows.clear() + for kbdname, kbd in self._kbds.items(): + if kbdname.startswith("_"): + continue + therows.append( + {"keys": [{"caption": kbd.get("description"), "single": {"keyboard": {"name": kbdname}},}]} + ) + + def setKeyboard(self, kbdname=None): + if self._sendscreenstate: + self._sendscreenstate(kbdname != "_minimized") + newgeometry = None + if kbdname == "_minimized": + newgeometry = self._minimizerlocation + else: + if kbdname == "back": + kbdname = self._previouskeyboard + if self._previousgeometry != self.geometry(): + newgeometry = self._previousgeometry + for n, k in self._kbds.items(): + if kbdname and kbdname != n: + continue + if not kbdname and n.startswith("_"): + continue + self._kbdname = n + self._kbd = k + # print("setKeybaord picked ", n) + self._releaseModifiers() + if self._sendmapchanges and k.get("keymap"): + self._sendmapchanges(k.get("keymap")) + if newgeometry: + self.hide() + if kbdname != "_minimized": + self._previouskeyboard = n + self._previousgeometry = self.geometry() + self._kbdstack.setCurrentIndex(k.get("_stackindex", 0)) + if self._kbd["views"].get(self._viewname): + self.setView(self._viewname, newgeometry) + else: + self.setView("default", newgeometry) + return True + return False + + def setView(self, viewname, newgeometry=None): + # print ("setView", viewname) + if self._kbd["views"].get(viewname): + self._view = self._kbd["views"][viewname] + self._viewname = viewname + self._kbd["_QWidget"].layout().setCurrentIndex(self._view["_stackindex"]) + if newgeometry: + self.setGeometry(newgeometry) + self.show() + else: + self.updateKeyboard() + return True + return False + + def getRawKbds(self): + return self._kbds + + # + # initKeyboards sets up a QStackedLayout holding QWidgets for each keyboard, which in turn have a + # QStackedlayout that holds a QWidget for each view within that keyboard. That has a QGridLayout with + # QHboxLayouts in it that hold the individual key QPushButton widgets. It also sets the captions and + # button actions for each key and figures out how many standard key widths and row vis there are in + # all the views, which is used by updateKeyboard() to dynamically figure out how big the fonts, margins + # and rounded corners need to be. + # + + def initKeyboards(self): + + # Helper to return placeholder "empty row" widget + def _makeEmptyRow(row): + er = QPushButton(self) + er.pressed.connect(partial(self._buttonhandler, er, PRESSED)) + er.released.connect(partial(self._buttonhandler, er, RELEASED)) + er.setMinimumSize(1, 1) + er.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + er.setProperty("class", "emptyrow") + row["_QWidget"] = er + row["type"] = "emptyrow" + er.data = row + return er + + # Helper to return a QLayout to go in place of the QPushButton that contains it plus + # any extra labels stacked on top + def _makeCaptionLayout(k): + extracaptions = k.data.get("extracaptions", None) + if not extracaptions: + return False + # ecl = extra captions layout + ecl = QStackedLayout() + ecl.setStackingMode(QStackedLayout.StackAll) + ecl.addWidget(k) + for cssclass, txt in extracaptions.items(): + ql = QLabel(txt) + ql.setProperty("class", cssclass) + ql.setAttribute(Qt.WA_TransparentForMouseEvents) + ecl.addWidget(ql) + return ecl + + def _maxRowsInView(view): + maxrows = 0 + for column in view.get("columns", []): + maxrows = max(len(column.get("rows")), maxrows) + return maxrows + + # This stores the width and height in standard key widths for each view. + def _storeWidthsAndHeights(view): + total_height = 0 + # Heights are only stored in first column + column = view["columns"][0] + for ri, row in enumerate(column.get("rows", [])): + total_height += row.get("height", 1) + total_width = 0 + for ci, column in enumerate(view.get("columns", [])): + largest_width = 0 + for ri, row in reversed(list(enumerate(column.get("rows", [])))): + if len(row.get("keys", [])): + totalweight = 0 + for keydata in row.get("keys", []): + w = keydata.get("width", 1) + totalweight += w + # Not counting frst row if there are widths already (reversed order) + if totalweight > largest_width and (ri != 0 or totalweight == 0): + largest_width = totalweight + column["_widthInUnits"] = largest_width + total_width += largest_width + view["_widthInUnits"] = max(total_width, 1) + view["_heightInUnits"] = max(total_height, 1) + + # Start of initKeyboards() itself + + if self._kbdstack.itemAt(0): + self._clearLayout(self._kbdstack) + ki = 0 + for kbdname, kbd in self._kbds.items(): + viewstack = QStackedLayout() + vi = 0 + for viewname, view in kbd.get("views", {}).items(): + _storeWidthsAndHeights(view) + grid = QGridLayout() + grid.setSpacing(0) + grid.setContentsMargins(0, 0, 0, 0) + for ci, column in enumerate(view.get("columns", [])): + for ri in range(_maxRowsInView(view)): + if ri < len(column["rows"]): + row = column["rows"][ri] + else: + row = {"keys": []} + column["rows"].append(row) + keys = row.get("keys", []) + kl = QHBoxLayout() + kl.setContentsMargins(0, 0, 0, 0) + kl.setSpacing(0) + for keydata in keys: + stretch = keydata.get("width", 1) * 10 + type = keydata.get("type", "key") + k = QPushButton(self) + k.setMinimumSize(1, 1) + keydata["_QWidget"] = k + keydata["_selected"] = False + k.data = keydata + k.pressed.connect(partial(self._buttonhandler, k, PRESSED)) + k.released.connect(partial(self._buttonhandler, k, RELEASED)) + k.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + k.setMinimumSize(1, 1) + if type == "key": + k.setText(keydata.get("caption", "")) + # Multiple captions? Create a QStackedWidget overlays them all + ecl = _makeCaptionLayout(k) + if ecl: + kl.addLayout(ecl, stretch) + else: + kl.addWidget(k, int(stretch)) + else: + kl.addWidget(k, int(stretch)) + if not len(keys): + er = _makeEmptyRow(row) + kl.addWidget(er) + else: + row["_QWidget"] = None + grid.addLayout(kl, ri, ci * 2) + if ci == 0: + grid.setRowStretch(ri, int(row.get("height", 1) * 10)) + grid.setColumnStretch(ci * 2, int(column.get("_widthInUnits", 1) * 10)) + if ci > 0: + spacercolumn = QHBoxLayout() + spacercolumn.addWidget(QWidget(None)) + grid.setColumnStretch((ci * 2) - 1, int(COLUMN_MARGIN * 10)) + grid.addLayout(spacercolumn, 0, (ci * 2) - 1) + # Create with self as parent, then reparent to prevent startup flicker + view["_QWidget"] = QWidget(self) + view["_QWidget"].setLayout(grid) + viewstack.addWidget(view["_QWidget"]) + view["_stackindex"] = vi + vi += 1 + kbd["_QWidget"] = QWidget(self) + kbd["_stackindex"] = ki + ki += 1 + kbd["_QWidget"].setLayout(viewstack) + self._kbdstack.addWidget(kbd["_QWidget"]) + self.setKeyboard(self._kbdname) + # Qt keeps coming up with minimum sizes that are way too wide + # Some sane number will have to go in at some point, I guess + self.setMaximumSize(16777215, 16777215) + self.setMinimumSize(1, 1) + + def updateKeyboard(self): + + # Helper function to dynamically recalculate some sizes in stylesheets + def fixStyle(stylesheet, fontsize, margin, radius): + if stylesheet == "": + return "" + # Replace the main calculated values + stylesheet = stylesheet.replace("_OSKB_FONTSIZE_", str(fontsize)) + stylesheet = stylesheet.replace("_OSKB_MARGIN_", str(margin)) + stylesheet = stylesheet.replace("_OSKB_RADIUS_", str(radius)) + # And then all the percentages based thereon (Qt5 doesn't do percentages in fontsizes) + r = re.compile(r"font-size\s*:\s*(\d+)\%") + i = r.finditer(stylesheet) + for m in i: + stylesheet = stylesheet.replace( + m.group(0), "font-size: " + str(int((fontsize / 100) * int(m.group(1)))) + "px", + ) + return stylesheet + + if not self._view: + return False + # Calculate the font and margin sizes + kw = self.width() / self._view["_widthInUnits"] + kh = self.height() / self._view["_heightInUnits"] + fontsize = min(max(int(min(kw / 1.5, kh / 2)), 5), 50) + margin = int(fontsize / 15) + radius = margin * 3 + # Dynamically change the default and keyboard stylesheets + all_sheets = self._stylesheet + "\n\n" + self._kbd.get("style", "") + super().setStyleSheet(fixStyle(all_sheets, fontsize, margin, radius)) + # Then adjust the stylesheets and class properties of all keys + for ci, column in enumerate(self._view.get("columns", [])): + for ri, row in enumerate(column.get("rows", [])): + rowwidget = row.get("_QWidget") + if rowwidget: + if row.get("_selected", False): + rowwidget.setProperty("class", "emptyrow selected") + else: + rowwidget.setProperty("class", "emptyrow") + # It needs .setStyleSheet(""), not .repaint() to show the changes + rowwidget.setStyleSheet("") + else: + for keydata in row.get("keys", []): + k = keydata.get("_QWidget") + type = keydata.get("type", "key") + classes = [type] + classes.append(keydata.get("class", "")) + if keydata.get("single") and keydata["single"].get("modifier"): + modname = keydata["single"]["modifier"].get("name", "") + moddata = self._modifiers.get(modname, {}) + modstate = moddata.get("state") + if modstate == 1: + classes.append("held") + elif modstate == 2: + classes.append("locked") + else: + classes.append("modifier") + if keydata.get("_selected", False): + classes.append("selected") + classes.append("view_" + self._viewname) + classes.append("row" + str(ri + 1)) + classes.append("col" + str(ci + 1)) + k.setProperty("class", " ".join(classes).strip()) + keystyle = keydata.get("style", "") + k.setStyleSheet(fixStyle(keystyle, fontsize, margin, radius)) + + + # + # The part here is the low-level button handling. It takes care of calling _doAction() with PRESSED and + # RELEASED with pointers to either the "single", "double" or "long" sub-dictionaries for that button, + # handling all the nitty-gritty. Bit involved.., Maybe only touch when wide awake and concentrated. + # + + def _oskbButtonHandler(self, button, direction): + sng = button.data.get("single") + dbl = button.data.get("double") + lng = button.data.get("long") + if direction == PRESSED: + if self._doublebutton and self._doublebutton != button: + # Another key was pressed within the doubleclick timeout, so we must + # first process the previous key that was held back + self._doAction(self._doublebutton.data.get("single"), PRESSED) + self._doAction(self._doublebutton.data.get("single"), RELEASED) + self._doublebutton = None + self._doubletimer.stop() + self._stopsinglepress = False + if lng or dbl: + if lng: + self._longtimer = QTimer() + self._longtimer.setSingleShot(True) + self._longtimer.timeout.connect(partial(self._longPress, lng)) + self._longtimer.start(LONGPRESS_TIMEOUT) + if dbl: + self._stopsinglepress = True + if self._doubletimer.isActive(): + self._doubletimer.stop() + self._doAction(dbl, PRESSED) + self._doAction(dbl, RELEASED) + self._doublebutton = None + else: + self._doublebutton = button + self._doubletimer.start(DOUBLECLICK_TIMEOUT) + else: + self._doAction(sng, PRESSED) + else: + if not self._stopsinglepress: + if self._longtimer.isActive(): + self._longtimer.stop() + self._doAction(sng, PRESSED) + self._doAction(sng, RELEASED) + else: + self._doAction(sng, RELEASED) + self._stopsinglepress = False + self._longtimer.stop() + + def _longPress(self, lng): + self._stopsinglepress = True + self._doAction(lng, PRESSED) + self._doAction(lng, RELEASED) + + def _doubleTimeout(self): + if not self._stopsinglepress: + actiondict = self._doublebutton.data.get("single") + self._doAction(actiondict, PRESSED) + self._doAction(actiondict, RELEASED) + self._doublebutton = None + + # + # Higher level button handling: parses the actions from the action dictionary + # + + def _doAction(self, actiondict, direction): + if not actiondict: + return + for cmd, argdict in actiondict.items(): + if not argdict: + continue + + if cmd == "send": + keycode = argdict.get("keycode", "") + keycodeplus = keycode + keyname = argdict.get("name", "") + printable = argdict.get("printable", True) + + for modname, mod in self._modifiers.items(): + if mod.get("state") > 0: + keyname = modname + " " + keyname + modkeycode = mod.get("keycode") + keycodeplus = modkeycode + "+" + keycode + if not mod.get("printable"): + printable = False + if direction == PRESSED and self._flashmodifiers: + self._injectKeys(modkeycode, PRESSED) + self._injectKeys(keycode, direction) + if direction == RELEASED: + self._releaseModifiers() + if self._viewuntil and re.fullmatch(self._viewuntil, keyname): + self.setView(self._thenview) + self.viewuntil, self._thenview = None, None + + if cmd == "view" and direction == RELEASED: + viewname = argdict.get("name", "default") + self._viewuntil = argdict.get("until") + self._thenview = argdict.get("thenview") + self.setView(viewname) + addclass = "oneview" if self._viewuntil else "view" + self.setProperty("class", self._view.get("class", "") + addclass) + self.updateKeyboard() + + if cmd == "modifier" and direction == RELEASED: + keycode = argdict.get("keycode", "") + modifier = argdict.get("name", "") + printable = argdict.get("printable", True) + modaction = argdict.get("action", "toggle") + m = self._modifiers.get(modifier) + if modaction == "toggle": + if not m or m["state"] == 0: + self._modifiers[modifier] = { + "state": 1, + "keycode": keycode, + "printable": printable, + } + if not self._flashmodifiers: + self._injectKeys(keycode, PRESSED) + else: + self._modifiers[modifier] = { + "state": 0, + "keycode": keycode, + "printable": printable, + } + if not self._flashmodifiers: + self._injectKeys(keycode, RELEASED) + if modaction == "lock": + if not m: + self._modifiers[modifier] = {} + s = self._modifiers[modifier].get("state", 0) + self._modifiers[modifier] = { + "state": 0 if s == 2 else 2, + "keycode": keycode, + "printable": printable, + } + if not self._flashmodifiers: + self._injectKeys(keycode, PRESSED if s == 0 else RELEASED) + self.updateKeyboard() + + if cmd == "keyboard" and direction == RELEASED: + kbdname = argdict.get("name", "") + self.setKeyboard(kbdname) + + # This is where the strings with keycodes to be pressed or released get turned into actual keypress + # events. There's two levels here: "42+2;57" (in the US layout) means we're first pressing and then + # releasing shift 2 (an exclamation point) and then a space. + + def _injectKeys(self, keystr, direction): + keylist = keystr.split(";") + + # If PRESSED, press and release all the ;-separated keycodes, releasing all but the last + if direction == PRESSED: + for keycodes in keylist: + keycodelist = keycodes.split("+") + for keycode in keycodelist: + self._sendKey(int(keycode), PRESSED) + if keycodes != keylist[-1]: + self._sendKey(int(keycode), RELEASED) + + # If RELEASED, only need to release the last (set of) keys + if direction == RELEASED: + keycodelist = keylist[-1].split("+") + for keycode in reversed(keycodelist): + self._sendKey(int(keycode), RELEASED) + + def _sendKey(self, keycode, keyevent): + if self._sendkeys: + self._sendkeys(keycode, keyevent) + + def _releaseModifiers(self): + if self._view: + donestuff = False + for modinfo in self._modifiers.values(): + if modinfo["state"] == 1: + donestuff = True + if not self._flashmodifiers: + self._injectKeys(modinfo["keycode"], RELEASED) + modinfo["state"] = 0 + if self._flashmodifiers: + self._injectKeys(modinfo["keycode"], RELEASED) + if donestuff: + self.updateKeyboard() + + # Helper + + def _clearLayout(self, layout): + if layout != None: + while layout.count(): + child = layout.takeAt(0) + if child.widget() is not None: + child.widget().deleteLater() + elif child.layout() is not None: + self._clearLayout(child.layout()) + + +# oskbCopy() copies an oskb data structure (a dict with sub-dicts and sub-lists). If you specify two +# variables it will move from one to the other without breaking the reference. If you specify just one, +# it will return a new copy. + +def oskbCopy(f, t=None): + if t == None: + t = {} + t.clear() + if type(f) == dict: + for fk, fv in f.items(): + if fv != {} and fv != "" and not fk.startswith("_"): + if type(fv) == list or type(fv) == dict: + t[fk] = oskbCopy(fv) + else: + t[fk] = fv + elif type(f) == list: + t = [] + for fi, fv in enumerate(f): + t.append(oskbCopy(fv)) + return t + + +if __name__ == "__main__": + main() diff --git a/src/lib/oskb/oskbedit.css b/src/lib/oskb/oskbedit.css new file mode 100644 index 0000000..c96a4d0 --- /dev/null +++ b/src/lib/oskb/oskbedit.css @@ -0,0 +1,48 @@ +QWidget { + background-color: #cccccc; + margin:0px; +} + +QLabel { + font-size: 50%; + background: transparent; + margin: 2px 5px; + qproperty-alignment: 'AlignBottom | AlignLeft'; +} + +.key { + background-color: #eeeeee; + border: 1px solid black; + font-size: _OSKB_FONTSIZE_px; + margin: _OSKB_MARGIN_px; + border-radius: _OSKB_RADIUS_px; +} + +.spacer { + background-color: #cccccc; + border: 2px dotted white; + margin: _OSKB_MARGIN_px 0px; + border-radius: _OSKB_RADIUS_px; +} + +.emptyrow { + background-color: #cccccc; + border: 2px dotted #aaaaaa; + margin: _OSKB_MARGIN_px 0px; + border-radius: _OSKB_RADIUS_px; +} + +.key:pressed, .key.pseudomodifier, .key.held { + background-color: #999999; +} + +.view .key.pseudomodifier, .key.locked { + background-color: #cc9999; +} + + +/* Below are used by oskbedit */ + +.key.selected, .spacer.selected, .emptyrow.selected { + background-color: #77bbff !important; +} \ No newline at end of file diff --git a/src/lib/oskb/oskbedit.py b/src/lib/oskb/oskbedit.py new file mode 100644 index 0000000..eb73b62 --- /dev/null +++ b/src/lib/oskb/oskbedit.py @@ -0,0 +1,1025 @@ +import sys, os, json, re, pkg_resources, argparse +from functools import partial +from PyQt5.QtCore import ( + QTimer, + QRect, + QSysInfo, + QEvent, + QSize, + Qt, + QMetaObject, +) +from PyQt5.QtGui import ( + QIcon, + QFont, + QGuiApplication, +) +from PyQt5.QtWidgets import ( + QAction, + QActionGroup, + QApplication, + QCheckBox, + QComboBox, + QDesktopWidget, + QDialog, + QDialogButtonBox, + QDoubleSpinBox, + QFileDialog, + QFrame, + QGridLayout, + QHBoxLayout, + QInputDialog, + QLabel, + QLayout, + QLineEdit, + QMainWindow, + QMenuBar, + QMessageBox, + QPushButton, + QSizePolicy, + QStackedLayout, + QStyle, + QStyleFactory, + QTableWidget, + QTableWidgetItem, + QTabWidget, + QTextEdit, + QVBoxLayout, + QWidget, +) +import oskb +from oskb.ui_keywizard import Ui_KeyWizard +from oskb.ui_editkey import Ui_EditKey +from oskb.ui_keyactions import Ui_KeyActions +from oskb.ui_kbdproperties import Ui_KbdProperties +from oskb.ui_valueedit import Ui_ValueEdit + +if sys.platform.startswith("linux"): + import getpass, evdev + +DOUBLECLICK_TIMEOUT = 350 +MAX_UNDO = 10 + +def command_line_arguments(): + ap = argparse.ArgumentParser() + ap.add_argument("--input", "-i", help="input device for key wizard", metavar="") + ap.add_argument("--inputlist", help="list input devices and exit", action="store_true") + ap.add_argument("keyboard", help="a keyboard file", metavar="", nargs="?") + return ap + +def main(): + # A few things are global because the alternative is passing them around + global g_cmdline, g_kbdinput, g_oskbwidget + # Parse the command line arguments + ap = command_line_arguments() + g_cmdline = ap.parse_args() + # Handle --inputlist and create g_kbdinput InputDevice if --input is specified + g_kbdinput = None + if g_cmdline.inputlist or g_cmdline.input: + if sys.platform.startswith("linux"): + worked = True + if g_cmdline.inputlist: + devices = [evdev.InputDevice(path) for path in evdev.list_devices()] + if devices != []: + for device in devices: + print(device.path + ":", device.name, device.phys) + sys.exit(0) + else: + worked = False + elif g_cmdline.input: + try: + g_kbdinput = evdev.InputDevice(g_cmdline.input) + except: + worked = False + + if not worked: + user = getpass.getuser() + sys.stderr.write( + "Do you have permission to read the input devices?\n\n" + "Try 'sudo setfacl -m m::rw -m u:" + user + ":rw /dev/uinput /dev/input/*'\n" + "See the oskb documentation for more information.\n" + ) + sys.exit(-1) + else: + sys.stderr.write("You cannot use the key wizard on this OS, that is Linux-only") + sys.exit(-1) + # Start the Qt magic + app = QApplication([]) + window = OskbEdit() + sys.exit(app.exec_()) + + +class OskbEdit(QWidget): + def __init__(self): + super().__init__() + # Some variables need initialising + self._doubletimer = QTimer() + self._doubletimer.setSingleShot(True) + self._mode = "edit" + self._changed = False + self._lastclicked = None + self._copypaste = [] + # Size window at half width and third of height of screen, positioned in the middle + av_height = QDesktopWidget().availableGeometry(self).size().height() + av_width = QDesktopWidget().availableGeometry(self).size().width() + self.setGeometry( + QStyle.alignedRect( + Qt.LeftToRight, + Qt.AlignCenter, + QSize(int(av_width / 2), int(av_height / 3)), + QDesktopWidget().availableGeometry(self), + ) + ) + # Get a keyboard widget instance and set it up a tiny bit + global g_oskbwidget + g_oskbwidget = oskb.Keyboard() + g_oskbwidget.setButtonHandler(self._buttonHandler) + g_oskbwidget.setStyleSheet(pkg_resources.resource_string("oskb", "oskbedit.css").decode("utf-8")) + # Set up elements on the screen, must be done before _loadFile() + layout = QVBoxLayout(self) + frame = QWidget() + self._kbdlayout = QHBoxLayout() + frame.setLayout(self._kbdlayout) + self._kbdlayout.addWidget(g_oskbwidget) + layout.addWidget(frame) + self.setWindowTitle("oskbedit") + layout.addWidget(frame) + self._menu = QMenuBar() + self._menu.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.layout().setMenuBar(self._menu) + self.show() + # Load the file from the command line, or the blank keyboard if none specified + if g_cmdline.keyboard: + if not self._loadFile(g_cmdline.keyboard): + sys.exit(-1) + else: + self._loadFile("_new") + + def _fixMenu(self): + self._menu.clear() + self._menu_file() + self._menu_edit() + self._menu_insert() + self._menu_view() + + # + # "File" menu + # + + def _menu_file(self): + filemenu = self._menu.addMenu("&File") + newitem = filemenu.addAction("&New") + newitem.setShortcut("Ctrl+N") + newitem.triggered.connect(partial(self._loadFile, "_new")) + loaditem = filemenu.addAction("&Open file") + loaditem.triggered.connect(self._file_open) + loaditem.setShortcut("Ctrl+O") + builtinmenu = filemenu.addMenu("open &Builtin") + for k in pkg_resources.resource_listdir("oskb", "keyboards"): + if not k.startswith("_"): + builtinitem = QAction(k, self) + builtinitem.triggered.connect(partial(self._loadFile, k)) + builtinmenu.addAction(builtinitem) + saveitem = filemenu.addAction("&Save") + if not self._savefilename or not self._changed: + saveitem.setEnabled(False) + else: + saveitem.triggered.connect(partial(self._saveFile, self._savefilename)) + saveitem.setShortcut("Ctrl+S") + saveasitem = filemenu.addAction("Save &As") + if self._changed: + saveasitem.triggered.connect(self._file_save_as) + if not self._savefilename: + saveasitem.setShortcut("Ctrl+S") + else: + saveasitem.setEnabled(False) + filemenu.addSeparator() + exitButton = QAction("&Quit", self) + exitButton.setShortcut("Ctrl+Q") + exitButton.setStatusTip("Exit application") + exitButton.triggered.connect(self.close) + filemenu.addAction(exitButton) + + def _file_open(self): + if self._changed and not self._areyousure(): + return + self._changed = False + dialog = QFileDialog() + dialog.setFileMode(QFileDialog.ExistingFile) + dialog.setViewMode(QFileDialog.Detail) + if dialog.exec_(): + self._loadFile(*dialog.selectedFiles()) + + def _loadFile(self, f): + if self._changed and not self._areyousure(): + return False + if os.path.isfile(f): + self._savefilename = f + else: + self._savefilename = None + try: + self._kbdname = g_oskbwidget.readKeyboard(f) + except: + QMessageBox.warning( + self, + "read failed", + "File read failed: " + str(sys.exc_info()[1]), + QMessageBox.Ok, + QMessageBox.Ok, + ) + return False + g_oskbwidget.setKeyboard(self._kbdname) + self._changed = False + self._kbds = g_oskbwidget.getRawKbds() + try: + del self._kbds["_minimized"] + del self._kbds["_chooser"] + except: + pass + self._kbd = self._kbds[self._kbdname] + self._viewname = g_oskbwidget.getView() + self._view = self._kbd["views"][self._viewname] + g_oskbwidget.updateKeyboard() + g_oskbwidget.show() + self._undo = [] + self._redo = [] + self._previouskbd = oskb.oskbCopy(self._kbd) + self._stir() + return True + + def _areyousure(self): + if ( + QMessageBox.warning( + self, + "Almost losing changes", + "You have made changes, are you sure you want to do this? Hit 'No' and save if not.", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No, + ) + == QMessageBox.Yes + ): + return True + else: + return False + + def _file_save_as(self): + dialog = QFileDialog() + dialog.setFileMode(QFileDialog.AnyFile) + dialog.setAcceptMode(QFileDialog.AcceptSave) + dialog.setViewMode(QFileDialog.Detail) + if dialog.exec_(): + filenames = dialog.selectedFiles() + self._saveFile(*filenames) + + def _saveFile(self, f): + savecopy = oskb.oskbCopy(self._kbd) + with open(f, "w") as outfile: + json.dump(savecopy, outfile, ensure_ascii=False, indent=4) + self._changed = False + + def closeEvent(self, event): + if self._changed and not self._areyousure(): + event.ignore() + return + event.accept() + + # + # "Edit" menu + # + + def _menu_edit(self): + selrows, selkeys = self._surveySelected() + sel = selrows + selkeys + editmenu = self._menu.addMenu("&Edit") + if len(self._undo): + undoitem = QAction("&Undo " + self._undo[0][0], self) + undoitem.triggered.connect(self._edit_undo) + else: + undoitem = QAction("&Undo", self) + undoitem.setEnabled(False) + undoitem.setShortcut("Ctrl+Z") + editmenu.addAction(undoitem) + if len(self._redo): + redoitem = QAction("&Redo " + self._redo[0][0], self) + redoitem.triggered.connect(self._edit_redo) + else: + redoitem = QAction("Redo", self) + redoitem.setEnabled(False) + redoitem.setShortcut("Shift+Ctrl+Z") + editmenu.addAction(redoitem) + editmenu.addSeparator() + cutitem = QAction("Cut", self) + cutitem.setShortcut("Ctrl+X") + cutitem.triggered.connect(self._edit_cut) + cutitem.setEnabled(selkeys > 0 and selrows == 0) + editmenu.addAction(cutitem) + copyitem = QAction("Copy", self) + copyitem.setShortcut("Ctrl+C") + copyitem.triggered.connect(self._edit_copy) + copyitem.setEnabled(selkeys > 0 and selrows == 0) + editmenu.addAction(copyitem) + pasteafteritem = QAction("&Paste", self) + pasteafteritem.setShortcut("Ctrl+V") + if not self._copypaste or sel == 0 or (selrows > 0 and selkeys > 0) or selrows > 1: + pasteafteritem.setEnabled(False) + else: + if selkeys > 0: + pasteafteritem.triggered.connect(partial(self._edit_paste, self._lastSelKey(), 1)) + editmenu.addAction(pasteafteritem) + + pastebeforeitem = QAction("Paste &Before Selected", self) + pastebeforeitem.triggered.connect(partial(self._edit_paste, self._firstSelKey())) + editmenu.addAction(pastebeforeitem) + elif selrows == 1: + pasteafteritem.triggered.connect(partial(self._edit_paste, self._firstSelRow())) + editmenu.addAction(pasteafteritem) + deleteitem = QAction("&Delete", self) + deleteitem.setShortcut("Del") + deleteitem.triggered.connect(self._edit_delete) + deleteitem.setEnabled(sel > 0) + editmenu.addAction(deleteitem) + deleterowitem = QAction("Delete &Row", self) + deleterowitem.triggered.connect(partial(self._edit_delete_row, self._firstSel())) + deleterowitem.setEnabled(sel == 1) + editmenu.addAction(deleterowitem) + deletecolumnitem = QAction("Delete &Column", self) + deletecolumnitem.triggered.connect(partial(self._edit_delete_column, self._firstSel())) + deletecolumnitem.setEnabled(sel == 1) + editmenu.addAction(deletecolumnitem) + editmenu.addSeparator() + editkeyitem = QAction("Edit &Key/Spacer", self) + if sel != 1 or selrows > 0: + editkeyitem.setEnabled(False) + else: + w = self._firstSelWidget() + if w.data.get("type", "key") == "key": + editkeyitem.triggered.connect(partial(self._edit_key, w)) + elif w.data.get("type", "key") == "spacer": + editkeyitem.triggered.connect(partial(self._edit_spacer, w)) + editmenu.addAction(editkeyitem) + editrowitem = QAction("Edit &Row", self) + if sel != 1: + editrowitem.setEnabled(False) + else: + if selkeys == 1: + _, ri, _ = self._firstSelKey() + else: + _, ri, _ = self._firstSelRow() + # heights are only stored in first column + editrowitem.triggered.connect(partial(self._edit_row, ri)) + editmenu.addAction(editrowitem) + propitem = QAction("&Keyboard Properties", self) + propitem.triggered.connect(self._edit_properties) + editmenu.addAction(propitem) + + def _edit_undo(self): + actionname, actionview, kbd = self._undo.pop(0) + self._redo.insert(0, (actionname, self._viewname, oskb.oskbCopy(self._kbd))) + oskb.oskbCopy(kbd, self._kbd) + self._stir() + + def _edit_redo(self): + actionname, actionview, kbd = self._redo.pop(0) + self._undo.insert(0, (actionname, self._viewname, oskb.oskbCopy(self._kbd))) + oskb.oskbCopy(kbd, self._kbd) + self._stir() + + def _edit_delete(self): + self._copyCut(True) + self._stir("Delete") + + def _edit_delete_row(self, tuple): + if len(self._view["columns"][0]["rows"]) == 1: + QMessageBox.warning(self, "Delete", "You cannot delete the last row", QMessageBox.Ok) + return + _, ri, _ = tuple + for ci, column in enumerate(self._view.get("columns", [])): + column["rows"].pop(ri) + self._stir("Delete Row") + + def _edit_delete_column(self, tuple): + if len(self._view["columns"]) == 1: + QMessageBox.warning(self, "Delete", "You cannot delete the last column", QMessageBox.Ok) + return + ci, _, _ = tuple + self._view["columns"].pop(ci) + self._stir("Delete Column") + + def _edit_copy(self): + self._copypaste = self._copyCut(False) + + def _edit_cut(self): + self._copypaste = self._copyCut(True) + self._stir("Cut") + + def _edit_paste(self, tuple, after=0): + ci, ri, ki = tuple + for ins in self._copypaste: + self._view["columns"][ci]["rows"][ri]["keys"].insert(ki + after, oskb.oskbCopy(ins)) + self._stir("Paste") + + def _copyCut(self, cut=False): + buffer = [] + for ci, ri, ki, keydata in self._reverseIterateKeys(): + if keydata.get("_selected", False): + buffer.append(oskb.oskbCopy(keydata)) + if cut: + del self._view["columns"][ci]["rows"][ri]["keys"][ki] + return buffer + + def _edit_properties(self): + if KbdProperties(self._kbd).exec(): + self._stir("Edit Properties") + + def _edit_spacer(self, widget): + if ValueEdit(widget.data, "width", 0.5).exec(): + self._stir("Edit Spacer") + + def _edit_row(self, ri): + dict = self._view["columns"][0]["rows"][ri] + if ValueEdit(dict, "height").exec(): + self._stir("Edit Row") + + def _edit_key(self, widget): + if EditKey(widget).exec(): + self._stir("Edit Key") + + # + # "Insert" menu + # + + def _menu_insert(self): + selrows, selkeys = self._surveySelected() + sel = selrows + selkeys + keymenu = self._menu.addMenu("&Insert") + insertkmenu = keymenu.addMenu("&Key") + insertsmenu = keymenu.addMenu("&Spacer") + if sel == 0 or (selrows > 0 and selkeys > 0) or selrows > 1: + insertkmenu.setEnabled(False) + insertsmenu.setEnabled(False) + elif selkeys > 0: + addkbefore = QAction("&Before Selected", self) + addkbefore.triggered.connect(partial(self._insert_key, self._firstSelKey())) + insertkmenu.addAction(addkbefore) + addkafter = QAction("&After Selected", self) + addkafter.triggered.connect(partial(self._insert_key, self._lastSelKey(), 1)) + addkafter.setShortcut("Ctrl+K") + insertkmenu.addAction(addkafter) + addsbefore = QAction("&Before Selected", self) + addsbefore.triggered.connect(partial(self._insert_spacer, self._firstSelKey())) + insertsmenu.addAction(addsbefore) + addsafter = QAction("&After Selected", self) + addsafter.triggered.connect(partial(self._insert_spacer, self._lastSelKey(), 1)) + insertsmenu.addAction(addsafter) + elif selrows == 1: + addkon = QAction("&On Selected Row", self) + addkon.triggered.connect(partial(self._insert_key, self._firstSelRow())) + addkon.setShortcut("Ctrl+K") + insertkmenu.addAction(addkon) + addson = QAction("&On Selected Row", self) + addson.triggered.connect(partial(self._insert_spacer, self._firstSelRow())) + insertsmenu.addAction(addson) + insertrmenu = keymenu.addMenu("&Row") + insertcmenu = keymenu.addMenu("&Column") + if sel != 1: + insertrmenu.setEnabled(False) + insertcmenu.setEnabled(False) + else: + curpos = self._firstSel() + addrbefore = QAction("&Before Selected", self) + addrbefore.triggered.connect(partial(self._insert_row, curpos)) + insertrmenu.addAction(addrbefore) + addrafter = QAction("&After Selected", self) + addrafter.triggered.connect(partial(self._insert_row, curpos, 1)) + insertrmenu.addAction(addrafter) + addcbefore = QAction("&Before Selected", self) + addcbefore.triggered.connect(partial(self._insert_column, curpos)) + insertcmenu.addAction(addcbefore) + addcafter = QAction("&After Selected", self) + addcafter.triggered.connect(partial(self._insert_column, curpos, 1)) + insertcmenu.addAction(addcafter) + + def _insert_key(self, tuple, after=0): + ci, ri, ki = tuple + k = {"type": "key"} + rowkeys = self._view["columns"][ci]["rows"][ri]["keys"] + rowkeys.insert(ki + after, k) + wiz = None + if g_kbdinput: + wiz = KeyWizard() + if wiz.exec(): + k["caption"] = wiz.caption + k["single"] = {"send": {}} + k["single"]["send"]["name"] = wiz.keyname + k["single"]["send"]["keycode"] = wiz.keycode + if not wiz.printable: + k["single"]["send"]["printable"] = False + else: + wiz = None + if not wiz: + g_oskbwidget.initKeyboards() + self._edit_key(rowkeys[ki + after]["_QWidget"]) + self._stir("Insert Key") + self._selectState(False) + self._selectState(True, rowkeys[ki + after]["_QWidget"]) + g_oskbwidget.updateKeyboard() + self._fixMenu() + + def _insert_spacer(self, tuple, after=0): + ci, ri, ki = tuple + rowkeys = self._view["columns"][ci]["rows"][ri]["keys"] + rowkeys.insert(ki + after, {"type": "spacer", "width": 0.5}) + self._stir("Insert Spacer") + self._selectState(False) + self._selectState(True, rowkeys[ki + after]["_QWidget"]) + g_oskbwidget.updateKeyboard() + self._fixMenu() + + def _insert_row(self, tuple, after=0): + _, ri, _ = tuple + for ci, column in enumerate(self._view.get("columns", [])): + column["rows"].insert(ri + after, {"keys": []}) + self._stir("Insert Row") + + def _insert_column(self, tuple, after=0): + ci, _, _ = tuple + self._view["columns"].insert(ci + after, {"rows": [{"keys": []}]}) + self._stir("Insert Column") + + # + # "View" menu + # + + def _menu_view(self): + viewmenu = self._menu.addMenu("&View") + mag = QActionGroup(self) + editmodeitem = QAction("&Edit mode", self) + editmodeitem.triggered.connect(self._view_editmode) + editmodeitem.setCheckable(True) + editmodeitem.setChecked(self._mode == "edit") + editmodeitem.setShortcut("Ctrl+E") + viewmenu.addAction(editmodeitem) + mag.addAction(editmodeitem) + testmodeitem = QAction("&Test mode", self) + testmodeitem.triggered.connect(self._view_testmode) + testmodeitem.setCheckable(True) + testmodeitem.setChecked(self._mode == "test") + testmodeitem.setShortcut("Ctrl+T") + viewmenu.addAction(testmodeitem) + mag.addAction(testmodeitem) + if self._mode == "edit": + viewmenu.addSeparator() + vag = QActionGroup(self) + for vn, v in self._kbd["views"].items(): + va = QAction(vn, vag) + va.setCheckable(True) + if v == self._view: + va.setChecked(True) + va.triggered.connect(partial(self._view_switch, vn)) + viewmenu.addAction(va) + viewmenu.addSeparator() + addviewitem = QAction("&Add New View", self) + addviewitem.triggered.connect(self._view_add) + viewmenu.addAction(addviewitem) + delviewitem = QAction("&Delete Current View", self) + delviewitem.triggered.connect(self._view_delete) + viewmenu.addAction(delviewitem) + + def _view_editmode(self): + self._mode = "edit" + self._view_switch(g_oskbwidget.getView()) + g_oskbwidget.setButtonHandler(self._buttonHandler) + self._stir() + + def _view_testmode(self): + self._mode = "test" + self._selectState(False) + g_oskbwidget.setButtonHandler() + self._stir() + + def _view_switch(self, viewname): + g_oskbwidget.setView(viewname) + self._view = self._kbd["views"][viewname] + self._viewname = viewname + self._fixMenu() + + def _view_delete(self): + delview = g_oskbwidget.getView() + if delview == "default": + QMessageBox.warning(self, "New View", "Cannot delete 'default' view", QMessageBox.Ok) + return + self._view_switch("default") + del self._kbd["views"][delview] + self._stir("Delete View") + + def _view_add(self): + while True: + viewname, result = QInputDialog.getText(self, "New View", "Name:") + if not result: + break + if not re.fullmatch("[a-z_]+", viewname): + QMessageBox.warning( + self, + "New View", + "View name can only contain lower case letters and underscores", + QMessageBox.Ok, + ) + continue + if self._kbd["views"].get(viewname): + QMessageBox.warning(self, "New View", "View " + viewname + " already exists", QMessageBox.Ok) + continue + self._kbd["views"][viewname] = {"columns": [{"rows": [{"keys": []}]}]} + self._stir("Add View") + self._view_switch(viewname) + self._fixMenu() + break + + # + # Button Handler + # + + def _buttonHandler(self, widget, direction): + if direction == oskb.PRESSED: + # Read only once, is not state now but of last click event + mod = QGuiApplication.keyboardModifiers() + if mod & Qt.ControlModifier: + self._selectState(not widget.data.get("_selected", False), widget) + elif mod & Qt.ShiftModifier: + if self._lastclicked: + if widget.data.get("type") == "emptyrow": + return + selecting = False + for ci, ri, ki, keydata in self._iterateKeys(): + thisone = keydata["_QWidget"] + if thisone == self._lastclicked: + selecting = not selecting + if thisone == widget: + selecting = not selecting + if selecting or thisone == widget: + self._selectState(True, thisone) + else: + if self._doubletimer.isActive(): + self._doubleClick(widget) + else: + self._doubletimer.start(DOUBLECLICK_TIMEOUT) + self._selectState(False) + self._selectState(True, widget) + if widget.data.get("type") == "emptyrow": + self._lastclicked = None + else: + self._lastclicked = widget + g_oskbwidget.updateKeyboard() + self._fixMenu() + + def _doubleClick(self, widget): + if widget.data.get("type") == "spacer": + self._edit_spacer(widget) + elif widget.data.get("type", "key") == "key": + self._edit_key(widget) + + # + # Various functions + # + + # Redoes the keyboard. When called with an actionname string, it will store an undo state. + def _stir(self, actionname=None): + if actionname: + self._changed = True + self._undo.insert(0, (actionname, self._viewname[:], oskb.oskbCopy(self._previouskbd))) + self._previouskbd = oskb.oskbCopy(self._kbd) + while len(self._undo) > MAX_UNDO: + self._undo.pop(len(self._undo) - 1) + self._redo = [] + g_oskbwidget.initKeyboards() + # g_oskbwidget.updateKeyboard() + self._view_switch(g_oskbwidget.getView()) + self._fixMenu() + + def _listViews(self): + viewlist = [] + for view in self._kbd["views"]: + viewlist.append(view["name"]) + return viewlist + + # Calling without widget selects or deselects everything + def _selectState(self, newstate, widget=None): + for _, _, row in self._iterateRows(): + if row.get("_QWidget"): + if not widget or row.get("_QWidget") == widget: + row["_selected"] = newstate + for _, _, _, keydata in self._iterateKeys(): + if not widget or widget == keydata.get("_QWidget"): + keydata["_selected"] = newstate + + def _surveySelected(self): + selrows, selkeys = 0, 0 + for _, _, row in self._iterateRows(): + if row.get("_QWidget") and row.get("_selected"): + selrows += 1 + for _, _, _, keydata in self._iterateKeys(): + if keydata.get("_selected"): + selkeys += 1 + return selrows, selkeys + + def _firstSelWidget(self): + for ci, ri, ki, keydata in self._iterateKeys(): + if keydata.get("_selected"): + return keydata.get("_QWidget") + + def _firstSelKey(self): + for ci, ri, ki, keydata in self._iterateKeys(): + if keydata.get("_selected"): + return (ci, ri, ki) + + def _lastSelKey(self): + for ci, ri, ki, keydata in self._reverseIterateKeys(): + if keydata.get("_selected"): + return (ci, ri, ki) + + def _firstSelRow(self): + for ci, ri, row in self._iterateRows(): + if row.get("_QWidget") and row.get("_selected"): + return (ci, ri, 0) + + def _firstSel(self): + p = self._firstSelKey() + if p: + return p + return self._firstSelRow() + + def _iterateKeys(self): + for ci, column in enumerate(self._view.get("columns", [])): + for ri, row in enumerate(column.get("rows", [])): + for ki, keydata in enumerate(row.get("keys", [])): + yield ci, ri, ki, keydata + + def _reverseIterateKeys(self): + for ci, column in reversed(list(enumerate(self._view.get("columns", [])))): + for ri, row in reversed(list(enumerate(column.get("rows", [])))): + for ki, keydata in reversed(list(enumerate(row.get("keys", [])))): + yield ci, ri, ki, keydata + + def _iterateRows(self): + for ci, column in enumerate(self._view.get("columns", [])): + for ri, row in enumerate(column.get("rows", [])): + yield ci, ri, row + + +# +# Other windows and widgets. +# + + +class KeyWizard(QDialog): + def __init__(self): + super().__init__() + self.ui = Ui_KeyWizard() + self.ui.setupUi(self) + self.ui.lineEdit.setFocus() + self.show() + QTimer.singleShot(20, self.get_key) + + def get_key(self): + # Wait for all keys to be released, and then a key to be pressed + while g_kbdinput.active_keys() != []: + pass + while g_kbdinput.active_keys() == []: + pass + pressed = [] + while True: + active = g_kbdinput.active_keys() + if active == []: + break + for k in active: + if not k in pressed: + pressed.append(k) + result = "" + for k in pressed: + result += "+" + str(k) + self.keycode = result[1:] + QTimer.singleShot(20, partial(self.got_key, pressed)) + + def got_key(self, pressed): + caption = self.ui.lineEdit.text() + if caption == "": + captions = [] + for k in pressed: + if type(k) == "list": + c = evdev.ecodes.KEY[k][0] + else: + c = evdev.ecodes.KEY[k] + c = c.replace("KEY_", "") + c = c[4:] if c.startswith("LEFT") and len(c) > 4 else c + c = c.lower() + + captions.append(c) + self.keyname = "+".join(captions) + self.printable = False + else: + self.printable = True + self.keyname = caption + self.caption = caption + self.accept() + + +class KbdProperties(QDialog): + def __init__(self, kbd): + super().__init__() + self.ui = Ui_KbdProperties() + self.ui.setupUi(self) + self._kbd = kbd + self.ui.description.setText(kbd.get("description")) + self.ui.layout.setText(kbd.get("keymap")) + self.ui.defaultcss.setPlainText(pkg_resources.resource_string("oskb", "default.css").decode("utf-8")) + self.ui.keyboardcss.setPlainText(self._kbd.get("style", "")) + + def accept(self): + self._kbd["description"] = self.ui.description.text() + self._kbd["keymap"] = self.ui.layout.text() + self._kbd["style"] = self.ui.keyboardcss.toPlainText() + super().accept() + + +class ValueEdit(QDialog): + def __init__(self, dict, valkey, default=1): + super().__init__() + self.ui = Ui_ValueEdit() + self.ui.setupUi(self) + self._dict = dict + self._valkey = valkey + self._backup = dict.get(valkey, default) + self.ui.doubleSpinBox.setProperty("value", self._backup) + self.ui.label.setText(valkey.capitalize() + ":") + self.ui.doubleSpinBox.valueChanged.connect(self._tryItOut) + self.setStyleSheet("QDoubleSpinBox { border: 1px solid #bcbebf; }") + + def _tryItOut(self): + self._dict[self._valkey] = round(self.ui.doubleSpinBox.value(), 1) + g_oskbwidget.initKeyboards() + g_oskbwidget.updateKeyboard() + + def reject(self): + self._dict[self._valkey] = self._backup + g_oskbwidget.initKeyboards() + g_oskbwidget.updateKeyboard() + super().reject() + + +class EditKey(QDialog): + def __init__(self, widget): + super().__init__() + self._backup = oskb.oskbCopy(widget.data) + self._d = widget.data + self.ui = Ui_EditKey() + self.ui.setupUi(self) + self.setStyleSheet( + "QLineEdit, QDoubleSpinBox { border: 1px solid #bcbebf; } QComboBox { padding-left: 5px; }" + ) + # Put in the KeyAction widgets for the three types of keypress + self.ui.keyactionwidgets = [None, None, None] + for idx, act in enumerate(["single", "double", "long"]): + if not widget.data.get(act): + widget.data[act] = {} + self.ui.keyactionwidgets[idx] = KeyActions(widget.data[act]) + self.ui.keyactionwidgets[idx].setParent(eval("self.ui." + act)) + # self.ui.keyactionwidgets[idx].setGeometry(10, 10, 560, 290) + # Stick in the values from the key dictionary + self.ui.width.setProperty("value", self._d.get("width", 1.0)) + self.ui.width.valueChanged.connect(self._tryItOut) + self.ui.caption.setText(self._d.get("caption", "").replace("\n", "\\n")) + self.ui.caption.editingFinished.connect(self._tryItOut) + self.ui.cssclass.setText(self._d.get("class", "")) + self.ui.cssclass.editingFinished.connect(self._tryItOut) + self.ui.style.setPlainText(self._d.get("style", "")) + self.ui.addcaption.clicked.connect(self._addcaption) + self.ui.deletecaption.clicked.connect(self._deletecaption) + for c, t in self._d.get("extracaptions", {}).items(): + rows = self.ui.extracaptions.rowCount() + self.ui.extracaptions.setRowCount(rows + 1) + item = QTableWidgetItem() + item.setText(c) + self.ui.extracaptions.setItem(rows, 0, item) + item = QTableWidgetItem() + item.setText(t.replace("\n", "\\n")) + self.ui.extracaptions.setItem(rows, 1, item) + + def _stickBack(self): + # Appearance + self._d["width"] = self.ui.width.value() + self._d["caption"] = self.ui.caption.text().replace("\\n", "\n") + self._d["class"] = self.ui.cssclass.text() + self._d["style"] = self.ui.style.toPlainText() + self._d["extracaptions"] = {} + for n in range(self.ui.extracaptions.rowCount()): + cssclass = self.ui.extracaptions.item(n, 0).text() + caption = self.ui.extracaptions.item(n, 1).text() + if caption and cssclass: + caption = caption.replace("\\n", "\n") + self._d["extracaptions"][cssclass] = caption + + def _addcaption(self): + rows = self.ui.extracaptions.rowCount() + self.ui.extracaptions.setRowCount(rows + 1) + + def _deletecaption(self): + row = self.ui.extracaptions.currentRow() + self.ui.extracaptions.removeRow(row) + + def _tryItOut(self): + self._stickBack() + g_oskbwidget.initKeyboards() + g_oskbwidget.updateKeyboard() + + def reject(self): + super().reject() + oskb.oskbCopy(self._backup, self._d) + g_oskbwidget.initKeyboards() + g_oskbwidget.updateKeyboard() + + def accept(self): + self._stickBack() + # Tell KeyAction widgets to save their stuff as well + for idx, act in enumerate(["single", "double", "long"]): + self.ui.keyactionwidgets[idx].stickBack() + super().accept() + + +class KeyActions(QWidget): + def __init__(self, a): + super().__init__() + self._a = a + self.ui = Ui_KeyActions() + self.ui.setupUi(self) + self.setStyleSheet("QLineEdit, QDoubleSpinBox { border: 1px solid #bcbebf; }") + if not g_kbdinput: + self.ui.send_wiz.setEnabled(False) + self.ui.modifier_wiz.setEnabled(False) + else: + self.ui.send_wiz.clicked.connect( + partial(self._wiz, self.ui.send_keycode, self.ui.send_name, self.ui.send_printable) + ) + self.ui.modifier_wiz.clicked.connect( + partial( + self._wiz, self.ui.modifier_keycode, self.ui.modifier_name, self.ui.modifier_printable + ) + ) + if a.get("send", {}): + self.ui.send.setChecked(True) + self.ui.send_keycode.setText(a["send"].get("keycode", "")) + self.ui.send_name.setText(a["send"].get("name", "")) + self.ui.send_printable.setChecked(a["send"].get("printable", True)) + for v in g_oskbwidget.getViews(): + self.ui.view_name.addItem(v) + self.ui.view_thenview.addItem(v) + if a.get("view", {}): + self.ui.view.setChecked(True) + self.ui.view_name.setCurrentText(a["view"].get("name", "")) + if a["view"].get("thenview"): + self.ui.view_until_checkbox.setChecked(True) + self.ui.view_thenview.setCurrentText(a["view"].get("thenview", "")) + self.ui.view_until.setText(a["view"].get("until", "")) + if a.get("modifier", {}): + self.ui.modifier.setChecked(True) + self.ui.modifier_keycode.setText(a["modifier"].get("keycode", "")) + self.ui.modifier_name.setText(a["modifier"].get("name", "")) + self.ui.modifier_action.setCurrentText(a["modifier"].get("action", "toggle")) + self.ui.modifier_printable.setChecked(a["modifier"].get("printable", True)) + if a.get("keyboard", {}): + self.ui.keyboard.setChecked(True) + self.ui.keyboard_name.setText(a["keyboard"].get("name", "")) + + def _wiz(self, keycode_lineedit, name_lineedit, printable_checkbox): + wiz = KeyWizard() + if not wiz.exec(): + return + name_lineedit.setText(wiz.keyname) + keycode_lineedit.setText(wiz.keycode) + printable_checkbox.setChecked(wiz.printable) + + # Called from EditKey.accept() + def stickBack(self): + a = self._a + a["send"] = {} + if self.ui.send.isChecked(): + a["send"]["keycode"] = self.ui.send_keycode.text() + a["send"]["name"] = self.ui.send_name.text() + a["send"]["printable"] = self.ui.send_printable.isChecked() + a["view"] = {} + if self.ui.view.isChecked(): + a["view"]["name"] = self.ui.view_name.currentText() + if self.ui.view_until_checkbox.isChecked(): + a["view"]["thenview"] = self.ui.view_thenview.currentText() + a["view"]["until"] = self.ui.view_until.text() + a["modifier"] = {} + if self.ui.modifier.isChecked(): + a["modifier"]["keycode"] = self.ui.modifier_keycode.text() + a["modifier"]["name"] = self.ui.modifier_name.text() + a["modifier"]["action"] = self.ui.modifier_action.currentText() + a["modifier"]["printable"] = self.ui.modifier_printable.isChecked() + a["keyboard"] = {} + if self.ui.keyboard.isChecked(): + a["keyboard"]["name"] = self.ui.keyboard_name.text() + + +if __name__ == "__main__": + main() diff --git a/src/lib/oskb/ui/EditKey.ui b/src/lib/oskb/ui/EditKey.ui new file mode 100644 index 0000000..320a008 --- /dev/null +++ b/src/lib/oskb/ui/EditKey.ui @@ -0,0 +1,410 @@ + + + EditKey + + + + 0 + 0 + 632 + 444 + + + + + 0 + 0 + + + + + 632 + 444 + + + + + 632 + 444 + + + + Edit key properties + + + true + + + + + 330 + 400 + 291 + 41 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + 10 + 10 + 611 + 381 + + + + + 0 + 0 + + + + + 611 + 381 + + + + + 611 + 381 + + + + 0 + + + + Appearance + + + + + 300 + 10 + 151 + 16 + + + + Additional captions: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 110 + 20 + 101 + 21 + + + + + + + 40 + 130 + 221 + 19 + + + + + + + 300 + 30 + 261 + 121 + + + + + 11 + + + + true + + + 0 + + + 2 + + + false + + + true + + + false + + + false + + + + CSS class + + + + + Caption + + + + + + + 40 + 90 + 191 + 41 + + + + Additional CSS classes, separated by spaces: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + + + + + + 20 + 20 + 81 + 16 + + + + Caption: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 20 + 190 + 541 + 131 + + + + + + + 40 + 170 + 291 + 16 + + + + CSS StyleSheet specific to this key: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 20 + 320 + 541 + 16 + + + + (Better to add CSS class and put style info in the keyboard stylesheet) + + + Qt::AlignCenter + + + + + + 20 + 50 + 81 + 16 + + + + Key width: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 110 + 50 + 81 + 24 + + + + 1 + + + 0.100000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + 530 + 150 + 31 + 21 + + + + + 75 + true + + + + - + + + false + + + false + + + + + + 500 + 150 + 31 + 21 + + + + + + + + false + + + false + + + + + + Action + + + + + 20 + 20 + 571 + 321 + + + + 0 + + + + Single Tap + + + + + Double Tap + + + + + Press and hold + + + + + + + + maintabs + caption + width + cssclass + extracaptions + addcaption + deletecaption + style + actiontabs + + + + + cancelsavebuttons + accepted() + EditKey + accept() + + + 488 + 404 + + + 157 + 274 + + + + + cancelsavebuttons + rejected() + EditKey + reject() + + + 550 + 410 + + + 286 + 274 + + + + + diff --git a/src/lib/oskb/ui/KbdProperties.ui b/src/lib/oskb/ui/KbdProperties.ui new file mode 100644 index 0000000..2dedc2e --- /dev/null +++ b/src/lib/oskb/ui/KbdProperties.ui @@ -0,0 +1,205 @@ + + + KbdProperties + + + + 0 + 0 + 515 + 401 + + + + + 0 + 0 + + + + + 515 + 401 + + + + + 515 + 401 + + + + Keyboard Properties + + + + + 150 + 360 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + 20 + 80 + 481 + 271 + + + + 1 + + + + default.css (readonly) + + + + + 10 + 10 + 451 + 221 + + + + false + + + true + + + false + + + + + + Keyboard Stylesheet + + + + + 10 + 10 + 451 + 221 + + + + false + + + false + + + false + + + + + + + + 120 + 10 + 201 + 21 + + + + 30 + + + + + + 120 + 40 + 113 + 21 + + + + 16 + + + + + + 20 + 10 + 91 + 21 + + + + Description: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 20 + 40 + 91 + 20 + + + + Layout: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + buttonBox + accepted() + KbdProperties + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + KbdProperties + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/lib/oskb/ui/KeyActions.ui b/src/lib/oskb/ui/KeyActions.ui new file mode 100644 index 0000000..2fce8a8 --- /dev/null +++ b/src/lib/oskb/ui/KeyActions.ui @@ -0,0 +1,410 @@ + + + KeyActions + + + + 0 + 0 + 560 + 290 + + + + + + + + + 180 + 220 + 131 + 26 + + + + + 10 + + + + + + + 330 + 210 + 61 + 20 + + + + name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 30 + 20 + 151 + 20 + + + + + 75 + true + + + + Send keypress + + + + + + 120 + 110 + 101 + 20 + + + + printable + + + + + + 230 + 250 + 71 + 21 + + + + + + + 310 + 180 + 211 + 20 + + + + + 75 + true + + + + Jump to other keyboard + + + + + + 50 + 190 + 131 + 26 + + + + + 10 + + + + + + + 30 + 250 + 191 + 21 + + + + After keyname matches: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + 400 + 50 + 101 + 21 + + + + + + + 310 + 50 + 81 + 16 + + + + keycode: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 310 + 110 + 81 + 16 + + + + action: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 30 + 50 + 81 + 16 + + + + keycode: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 120 + 50 + 101 + 21 + + + + + + + 400 + 110 + 91 + 26 + + + + + 10 + + + + + toggle + + + + + lock + + + + + + + 400 + 140 + 111 + 20 + + + + printable + + + + + + 30 + 160 + 181 + 20 + + + + + 75 + true + + + + Jump to other view + + + + + + 400 + 210 + 101 + 21 + + + + + + + 120 + 80 + 101 + 21 + + + + + + + 310 + 20 + 161 + 20 + + + + + 75 + true + + + + Be a modifier key + + + + + + 50 + 220 + 131 + 20 + + + + Then jump to + + + + + + 400 + 80 + 101 + 21 + + + + + + + 310 + 80 + 81 + 16 + + + + name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 30 + 80 + 81 + 16 + + + + name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 220 + 50 + 41 + 21 + + + + wiz + + + + + + 500 + 50 + 41 + 21 + + + + wiz + + + + + send + send_keycode + send_wiz + send_name + send_printable + view + view_name + view_until_checkbox + view_thenview + view_until + modifier + modifier_keycode + modifier_wiz + modifier_name + modifier_action + modifier_printable + keyboard + keyboard_name + + + + diff --git a/src/lib/oskb/ui/KeyWizard.ui b/src/lib/oskb/ui/KeyWizard.ui new file mode 100644 index 0000000..3f447a7 --- /dev/null +++ b/src/lib/oskb/ui/KeyWizard.ui @@ -0,0 +1,69 @@ + + + KeyWizard + + + + 0 + 0 + 400 + 189 + + + + + 0 + 0 + + + + Key Wizard + + + true + + + + + 30 + 20 + 341 + 141 + + + + This is the Key Wizard. Simply press the key you would like to assign, and the key caption and keycodes will be set automatically. If you want to add functions for long presses or doubleclicks, or if you would like to add more captions, you can always edit the key manually later by doubleclicking it. + + + Qt::AlignJustify|Qt::AlignTop + + + true + + + + + + 350 + 160 + 41 + 21 + + + + BlankCursor + + + false + + + background-color: transparent; border:none + + + false + + + + + + diff --git a/src/lib/oskb/ui/README.md b/src/lib/oskb/ui/README.md new file mode 100644 index 0000000..6dcc21b --- /dev/null +++ b/src/lib/oskb/ui/README.md @@ -0,0 +1,5 @@ +# User Interface design files + +These files hold the types and positions of all the elements in the various dialog windows. These files are edited with a program called "Qt Designer" which is normally part of a much bigger package called QT Creator, which is a complete development environment and several gigabytes in size. You can also download it standalone [here](https://build-system.fman.io/qt-designer-download). + +Once you have edited a file, you can compile it to its corresponding file starting with `ui_` in the directory above this one by entering (for example) `pyuic5 EditKey.ui > ../ui_editkey.py`. (`pyuic5` comes with PyQt5 that was installed when you installed oskb.) Do not edit these `ui_something.py` files manually, as they will be overwritten next time someone compiles `SomeThing.ui`. \ No newline at end of file diff --git a/src/lib/oskb/ui/ValueEdit.ui b/src/lib/oskb/ui/ValueEdit.ui new file mode 100644 index 0000000..54b38a0 --- /dev/null +++ b/src/lib/oskb/ui/ValueEdit.ui @@ -0,0 +1,124 @@ + + + ValueEdit + + + + 0 + 0 + 247 + 103 + + + + + 0 + 0 + + + + + 247 + 103 + + + + + 247 + 103 + + + + Enter Value + + + + + 60 + 60 + 171 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + 130 + 20 + 81 + 24 + + + + 1 + + + 0.100000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + + 10 + 20 + 111 + 21 + + + + Width: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + buttonBox + accepted() + ValueEdit + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ValueEdit + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/lib/oskb/ui_editkey.py b/src/lib/oskb/ui_editkey.py new file mode 100644 index 0000000..76189c1 --- /dev/null +++ b/src/lib/oskb/ui_editkey.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'EditKey.ui' +# +# Created by: PyQt5 UI code generator 5.14.0 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_EditKey(object): + def setupUi(self, EditKey): + EditKey.setObjectName("EditKey") + EditKey.resize(632, 444) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(EditKey.sizePolicy().hasHeightForWidth()) + EditKey.setSizePolicy(sizePolicy) + EditKey.setMinimumSize(QtCore.QSize(632, 444)) + EditKey.setMaximumSize(QtCore.QSize(632, 444)) + EditKey.setModal(True) + self.cancelsavebuttons = QtWidgets.QDialogButtonBox(EditKey) + self.cancelsavebuttons.setGeometry(QtCore.QRect(330, 400, 291, 41)) + self.cancelsavebuttons.setOrientation(QtCore.Qt.Horizontal) + self.cancelsavebuttons.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save) + self.cancelsavebuttons.setObjectName("cancelsavebuttons") + self.maintabs = QtWidgets.QTabWidget(EditKey) + self.maintabs.setGeometry(QtCore.QRect(10, 10, 611, 381)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.maintabs.sizePolicy().hasHeightForWidth()) + self.maintabs.setSizePolicy(sizePolicy) + self.maintabs.setMinimumSize(QtCore.QSize(611, 381)) + self.maintabs.setMaximumSize(QtCore.QSize(611, 381)) + self.maintabs.setObjectName("maintabs") + self.appearance = QtWidgets.QWidget() + self.appearance.setObjectName("appearance") + self.lbl_4 = QtWidgets.QLabel(self.appearance) + self.lbl_4.setGeometry(QtCore.QRect(300, 10, 151, 16)) + self.lbl_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.lbl_4.setObjectName("lbl_4") + self.caption = QtWidgets.QLineEdit(self.appearance) + self.caption.setGeometry(QtCore.QRect(110, 20, 101, 21)) + self.caption.setObjectName("caption") + self.cssclass = QtWidgets.QLineEdit(self.appearance) + self.cssclass.setGeometry(QtCore.QRect(40, 130, 221, 19)) + self.cssclass.setObjectName("cssclass") + self.extracaptions = QtWidgets.QTableWidget(self.appearance) + self.extracaptions.setGeometry(QtCore.QRect(300, 30, 261, 121)) + font = QtGui.QFont() + font.setPointSize(11) + self.extracaptions.setFont(font) + self.extracaptions.setShowGrid(True) + self.extracaptions.setRowCount(0) + self.extracaptions.setColumnCount(2) + self.extracaptions.setObjectName("extracaptions") + item = QtWidgets.QTableWidgetItem() + self.extracaptions.setHorizontalHeaderItem(0, item) + item = QtWidgets.QTableWidgetItem() + self.extracaptions.setHorizontalHeaderItem(1, item) + self.extracaptions.horizontalHeader().setCascadingSectionResizes(False) + self.extracaptions.horizontalHeader().setStretchLastSection(True) + self.extracaptions.verticalHeader().setVisible(False) + self.extracaptions.verticalHeader().setStretchLastSection(False) + self.lbl_3 = QtWidgets.QLabel(self.appearance) + self.lbl_3.setGeometry(QtCore.QRect(40, 90, 191, 41)) + self.lbl_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.lbl_3.setWordWrap(True) + self.lbl_3.setObjectName("lbl_3") + self.lbl_1 = QtWidgets.QLabel(self.appearance) + self.lbl_1.setGeometry(QtCore.QRect(20, 20, 81, 16)) + self.lbl_1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_1.setObjectName("lbl_1") + self.style = QtWidgets.QPlainTextEdit(self.appearance) + self.style.setGeometry(QtCore.QRect(20, 190, 541, 131)) + self.style.setObjectName("style") + self.lbl_5 = QtWidgets.QLabel(self.appearance) + self.lbl_5.setGeometry(QtCore.QRect(40, 170, 291, 16)) + self.lbl_5.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.lbl_5.setObjectName("lbl_5") + self.lbl_6 = QtWidgets.QLabel(self.appearance) + self.lbl_6.setGeometry(QtCore.QRect(20, 320, 541, 16)) + self.lbl_6.setAlignment(QtCore.Qt.AlignCenter) + self.lbl_6.setObjectName("lbl_6") + self.lbl_2 = QtWidgets.QLabel(self.appearance) + self.lbl_2.setGeometry(QtCore.QRect(20, 50, 81, 16)) + self.lbl_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_2.setObjectName("lbl_2") + self.width = QtWidgets.QDoubleSpinBox(self.appearance) + self.width.setGeometry(QtCore.QRect(110, 50, 81, 24)) + self.width.setDecimals(1) + self.width.setMinimum(0.1) + self.width.setSingleStep(0.1) + self.width.setProperty("value", 1.0) + self.width.setObjectName("width") + self.deletecaption = QtWidgets.QPushButton(self.appearance) + self.deletecaption.setGeometry(QtCore.QRect(530, 150, 31, 21)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.deletecaption.setFont(font) + self.deletecaption.setDefault(False) + self.deletecaption.setFlat(False) + self.deletecaption.setObjectName("deletecaption") + self.addcaption = QtWidgets.QPushButton(self.appearance) + self.addcaption.setGeometry(QtCore.QRect(500, 150, 31, 21)) + self.addcaption.setDefault(False) + self.addcaption.setFlat(False) + self.addcaption.setObjectName("addcaption") + self.maintabs.addTab(self.appearance, "") + self.action = QtWidgets.QWidget() + self.action.setObjectName("action") + self.actiontabs = QtWidgets.QTabWidget(self.action) + self.actiontabs.setGeometry(QtCore.QRect(20, 20, 571, 321)) + self.actiontabs.setObjectName("actiontabs") + self.single = QtWidgets.QWidget() + self.single.setObjectName("single") + self.actiontabs.addTab(self.single, "") + self.double = QtWidgets.QWidget() + self.double.setObjectName("double") + self.actiontabs.addTab(self.double, "") + self.long = QtWidgets.QWidget() + self.long.setObjectName("long") + self.actiontabs.addTab(self.long, "") + self.maintabs.addTab(self.action, "") + + self.retranslateUi(EditKey) + self.maintabs.setCurrentIndex(0) + self.actiontabs.setCurrentIndex(0) + self.cancelsavebuttons.accepted.connect(EditKey.accept) + self.cancelsavebuttons.rejected.connect(EditKey.reject) + QtCore.QMetaObject.connectSlotsByName(EditKey) + EditKey.setTabOrder(self.maintabs, self.caption) + EditKey.setTabOrder(self.caption, self.width) + EditKey.setTabOrder(self.width, self.cssclass) + EditKey.setTabOrder(self.cssclass, self.extracaptions) + EditKey.setTabOrder(self.extracaptions, self.addcaption) + EditKey.setTabOrder(self.addcaption, self.deletecaption) + EditKey.setTabOrder(self.deletecaption, self.style) + EditKey.setTabOrder(self.style, self.actiontabs) + + def retranslateUi(self, EditKey): + _translate = QtCore.QCoreApplication.translate + EditKey.setWindowTitle(_translate("EditKey", "Edit key properties")) + self.lbl_4.setText(_translate("EditKey", "Additional captions:")) + item = self.extracaptions.horizontalHeaderItem(0) + item.setText(_translate("EditKey", "CSS class")) + item = self.extracaptions.horizontalHeaderItem(1) + item.setText(_translate("EditKey", "Caption")) + self.lbl_3.setText(_translate("EditKey", "Additional CSS classes, separated by spaces:")) + self.lbl_1.setText(_translate("EditKey", "Caption:")) + self.lbl_5.setText(_translate("EditKey", "CSS StyleSheet specific to this key:")) + self.lbl_6.setText(_translate("EditKey", "(Better to add CSS class and put style info in the keyboard stylesheet)")) + self.lbl_2.setText(_translate("EditKey", "Key width:")) + self.deletecaption.setText(_translate("EditKey", "-")) + self.addcaption.setText(_translate("EditKey", "+")) + self.maintabs.setTabText(self.maintabs.indexOf(self.appearance), _translate("EditKey", "Appearance")) + self.actiontabs.setTabText(self.actiontabs.indexOf(self.single), _translate("EditKey", "Single Tap")) + self.actiontabs.setTabText(self.actiontabs.indexOf(self.double), _translate("EditKey", "Double Tap")) + self.actiontabs.setTabText(self.actiontabs.indexOf(self.long), _translate("EditKey", "Press and hold")) + self.maintabs.setTabText(self.maintabs.indexOf(self.action), _translate("EditKey", "Action")) diff --git a/src/lib/oskb/ui_kbdproperties.py b/src/lib/oskb/ui_kbdproperties.py new file mode 100644 index 0000000..816684f --- /dev/null +++ b/src/lib/oskb/ui_kbdproperties.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'KbdProperties.ui' +# +# Created by: PyQt5 UI code generator 5.14.0 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_KbdProperties(object): + def setupUi(self, KbdProperties): + KbdProperties.setObjectName("KbdProperties") + KbdProperties.resize(515, 401) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(KbdProperties.sizePolicy().hasHeightForWidth()) + KbdProperties.setSizePolicy(sizePolicy) + KbdProperties.setMinimumSize(QtCore.QSize(515, 401)) + KbdProperties.setMaximumSize(QtCore.QSize(515, 401)) + self.buttonBox = QtWidgets.QDialogButtonBox(KbdProperties) + self.buttonBox.setGeometry(QtCore.QRect(150, 360, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save) + self.buttonBox.setObjectName("buttonBox") + self.maintabs = QtWidgets.QTabWidget(KbdProperties) + self.maintabs.setGeometry(QtCore.QRect(20, 80, 481, 271)) + self.maintabs.setObjectName("maintabs") + self.tab = QtWidgets.QWidget() + self.tab.setObjectName("tab") + self.defaultcss = QtWidgets.QTextEdit(self.tab) + self.defaultcss.setGeometry(QtCore.QRect(10, 10, 451, 221)) + self.defaultcss.setAcceptDrops(False) + self.defaultcss.setReadOnly(True) + self.defaultcss.setAcceptRichText(False) + self.defaultcss.setObjectName("defaultcss") + self.maintabs.addTab(self.tab, "") + self.tab_2 = QtWidgets.QWidget() + self.tab_2.setObjectName("tab_2") + self.keyboardcss = QtWidgets.QTextEdit(self.tab_2) + self.keyboardcss.setGeometry(QtCore.QRect(10, 10, 451, 221)) + self.keyboardcss.setAcceptDrops(False) + self.keyboardcss.setReadOnly(False) + self.keyboardcss.setAcceptRichText(False) + self.keyboardcss.setObjectName("keyboardcss") + self.maintabs.addTab(self.tab_2, "") + self.description = QtWidgets.QLineEdit(KbdProperties) + self.description.setGeometry(QtCore.QRect(120, 10, 201, 21)) + self.description.setMaxLength(30) + self.description.setObjectName("description") + self.layout = QtWidgets.QLineEdit(KbdProperties) + self.layout.setGeometry(QtCore.QRect(120, 40, 113, 21)) + self.layout.setMaxLength(16) + self.layout.setObjectName("layout") + self.label = QtWidgets.QLabel(KbdProperties) + self.label.setGeometry(QtCore.QRect(20, 10, 91, 21)) + self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label.setObjectName("label") + self.label_2 = QtWidgets.QLabel(KbdProperties) + self.label_2.setGeometry(QtCore.QRect(20, 40, 91, 20)) + self.label_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_2.setObjectName("label_2") + + self.retranslateUi(KbdProperties) + self.maintabs.setCurrentIndex(1) + self.buttonBox.accepted.connect(KbdProperties.accept) + self.buttonBox.rejected.connect(KbdProperties.reject) + QtCore.QMetaObject.connectSlotsByName(KbdProperties) + + def retranslateUi(self, KbdProperties): + _translate = QtCore.QCoreApplication.translate + KbdProperties.setWindowTitle(_translate("KbdProperties", "Keyboard Properties")) + self.maintabs.setTabText(self.maintabs.indexOf(self.tab), _translate("KbdProperties", "default.css (readonly)")) + self.maintabs.setTabText(self.maintabs.indexOf(self.tab_2), _translate("KbdProperties", "Keyboard Stylesheet")) + self.label.setText(_translate("KbdProperties", "Description:")) + self.label_2.setText(_translate("KbdProperties", "Layout:")) diff --git a/src/lib/oskb/ui_keyactions.py b/src/lib/oskb/ui_keyactions.py new file mode 100644 index 0000000..4dcfd5b --- /dev/null +++ b/src/lib/oskb/ui_keyactions.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'KeyActions.ui' +# +# Created by: PyQt5 UI code generator 5.14.0 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_KeyActions(object): + def setupUi(self, KeyActions): + KeyActions.setObjectName("KeyActions") + KeyActions.resize(560, 290) + KeyActions.setWindowTitle("") + self.view_thenview = QtWidgets.QComboBox(KeyActions) + self.view_thenview.setGeometry(QtCore.QRect(180, 220, 131, 26)) + font = QtGui.QFont() + font.setPointSize(10) + self.view_thenview.setFont(font) + self.view_thenview.setObjectName("view_thenview") + self.lbl_16 = QtWidgets.QLabel(KeyActions) + self.lbl_16.setGeometry(QtCore.QRect(330, 210, 61, 20)) + self.lbl_16.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_16.setObjectName("lbl_16") + self.send = QtWidgets.QCheckBox(KeyActions) + self.send.setGeometry(QtCore.QRect(30, 20, 151, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.send.setFont(font) + self.send.setObjectName("send") + self.send_printable = QtWidgets.QCheckBox(KeyActions) + self.send_printable.setGeometry(QtCore.QRect(120, 110, 101, 20)) + self.send_printable.setObjectName("send_printable") + self.view_until = QtWidgets.QLineEdit(KeyActions) + self.view_until.setGeometry(QtCore.QRect(230, 250, 71, 21)) + self.view_until.setObjectName("view_until") + self.keyboard = QtWidgets.QCheckBox(KeyActions) + self.keyboard.setGeometry(QtCore.QRect(310, 180, 211, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.keyboard.setFont(font) + self.keyboard.setObjectName("keyboard") + self.view_name = QtWidgets.QComboBox(KeyActions) + self.view_name.setGeometry(QtCore.QRect(50, 190, 131, 26)) + font = QtGui.QFont() + font.setPointSize(10) + self.view_name.setFont(font) + self.view_name.setObjectName("view_name") + self.lbl_12 = QtWidgets.QLabel(KeyActions) + self.lbl_12.setGeometry(QtCore.QRect(30, 250, 191, 21)) + self.lbl_12.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_12.setWordWrap(True) + self.lbl_12.setObjectName("lbl_12") + self.modifier_keycode = QtWidgets.QLineEdit(KeyActions) + self.modifier_keycode.setGeometry(QtCore.QRect(400, 50, 101, 21)) + self.modifier_keycode.setObjectName("modifier_keycode") + self.lbl_13 = QtWidgets.QLabel(KeyActions) + self.lbl_13.setGeometry(QtCore.QRect(310, 50, 81, 16)) + self.lbl_13.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_13.setObjectName("lbl_13") + self.lbl_15 = QtWidgets.QLabel(KeyActions) + self.lbl_15.setGeometry(QtCore.QRect(310, 110, 81, 16)) + self.lbl_15.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_15.setObjectName("lbl_15") + self.lbl_10 = QtWidgets.QLabel(KeyActions) + self.lbl_10.setGeometry(QtCore.QRect(30, 50, 81, 16)) + self.lbl_10.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_10.setObjectName("lbl_10") + self.send_keycode = QtWidgets.QLineEdit(KeyActions) + self.send_keycode.setGeometry(QtCore.QRect(120, 50, 101, 21)) + self.send_keycode.setObjectName("send_keycode") + self.modifier_action = QtWidgets.QComboBox(KeyActions) + self.modifier_action.setGeometry(QtCore.QRect(400, 110, 91, 26)) + font = QtGui.QFont() + font.setPointSize(10) + self.modifier_action.setFont(font) + self.modifier_action.setObjectName("modifier_action") + self.modifier_action.addItem("") + self.modifier_action.addItem("") + self.modifier_printable = QtWidgets.QCheckBox(KeyActions) + self.modifier_printable.setGeometry(QtCore.QRect(400, 140, 111, 20)) + self.modifier_printable.setObjectName("modifier_printable") + self.view = QtWidgets.QCheckBox(KeyActions) + self.view.setGeometry(QtCore.QRect(30, 160, 181, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.view.setFont(font) + self.view.setObjectName("view") + self.keyboard_name = QtWidgets.QLineEdit(KeyActions) + self.keyboard_name.setGeometry(QtCore.QRect(400, 210, 101, 21)) + self.keyboard_name.setObjectName("keyboard_name") + self.send_name = QtWidgets.QLineEdit(KeyActions) + self.send_name.setGeometry(QtCore.QRect(120, 80, 101, 21)) + self.send_name.setObjectName("send_name") + self.modifier = QtWidgets.QCheckBox(KeyActions) + self.modifier.setGeometry(QtCore.QRect(310, 20, 161, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.modifier.setFont(font) + self.modifier.setObjectName("modifier") + self.view_until_checkbox = QtWidgets.QCheckBox(KeyActions) + self.view_until_checkbox.setGeometry(QtCore.QRect(50, 220, 131, 20)) + self.view_until_checkbox.setObjectName("view_until_checkbox") + self.modifier_name = QtWidgets.QLineEdit(KeyActions) + self.modifier_name.setGeometry(QtCore.QRect(400, 80, 101, 21)) + self.modifier_name.setObjectName("modifier_name") + self.lbl_14 = QtWidgets.QLabel(KeyActions) + self.lbl_14.setGeometry(QtCore.QRect(310, 80, 81, 16)) + self.lbl_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_14.setObjectName("lbl_14") + self.lbl_11 = QtWidgets.QLabel(KeyActions) + self.lbl_11.setGeometry(QtCore.QRect(30, 80, 81, 16)) + self.lbl_11.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.lbl_11.setObjectName("lbl_11") + self.send_wiz = QtWidgets.QPushButton(KeyActions) + self.send_wiz.setGeometry(QtCore.QRect(220, 50, 41, 21)) + self.send_wiz.setObjectName("send_wiz") + self.modifier_wiz = QtWidgets.QPushButton(KeyActions) + self.modifier_wiz.setGeometry(QtCore.QRect(500, 50, 41, 21)) + self.modifier_wiz.setObjectName("modifier_wiz") + + self.retranslateUi(KeyActions) + QtCore.QMetaObject.connectSlotsByName(KeyActions) + KeyActions.setTabOrder(self.send, self.send_keycode) + KeyActions.setTabOrder(self.send_keycode, self.send_wiz) + KeyActions.setTabOrder(self.send_wiz, self.send_name) + KeyActions.setTabOrder(self.send_name, self.send_printable) + KeyActions.setTabOrder(self.send_printable, self.view) + KeyActions.setTabOrder(self.view, self.view_name) + KeyActions.setTabOrder(self.view_name, self.view_until_checkbox) + KeyActions.setTabOrder(self.view_until_checkbox, self.view_thenview) + KeyActions.setTabOrder(self.view_thenview, self.view_until) + KeyActions.setTabOrder(self.view_until, self.modifier) + KeyActions.setTabOrder(self.modifier, self.modifier_keycode) + KeyActions.setTabOrder(self.modifier_keycode, self.modifier_wiz) + KeyActions.setTabOrder(self.modifier_wiz, self.modifier_name) + KeyActions.setTabOrder(self.modifier_name, self.modifier_action) + KeyActions.setTabOrder(self.modifier_action, self.modifier_printable) + KeyActions.setTabOrder(self.modifier_printable, self.keyboard) + KeyActions.setTabOrder(self.keyboard, self.keyboard_name) + + def retranslateUi(self, KeyActions): + _translate = QtCore.QCoreApplication.translate + self.lbl_16.setText(_translate("KeyActions", "name:")) + self.send.setText(_translate("KeyActions", "Send keypress")) + self.send_printable.setText(_translate("KeyActions", "printable")) + self.keyboard.setText(_translate("KeyActions", "Jump to other keyboard")) + self.lbl_12.setText(_translate("KeyActions", "After keyname matches:")) + self.lbl_13.setText(_translate("KeyActions", "keycode:")) + self.lbl_15.setText(_translate("KeyActions", "action:")) + self.lbl_10.setText(_translate("KeyActions", "keycode:")) + self.modifier_action.setItemText(0, _translate("KeyActions", "toggle")) + self.modifier_action.setItemText(1, _translate("KeyActions", "lock")) + self.modifier_printable.setText(_translate("KeyActions", "printable")) + self.view.setText(_translate("KeyActions", "Jump to other view")) + self.modifier.setText(_translate("KeyActions", "Be a modifier key")) + self.view_until_checkbox.setText(_translate("KeyActions", "Then jump to")) + self.lbl_14.setText(_translate("KeyActions", "name:")) + self.lbl_11.setText(_translate("KeyActions", "name:")) + self.send_wiz.setText(_translate("KeyActions", "wiz")) + self.modifier_wiz.setText(_translate("KeyActions", "wiz")) diff --git a/src/lib/oskb/ui_keywizard.py b/src/lib/oskb/ui_keywizard.py new file mode 100644 index 0000000..7a98d99 --- /dev/null +++ b/src/lib/oskb/ui_keywizard.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'KeyWizard.ui' +# +# Created by: PyQt5 UI code generator 5.14.0 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_KeyWizard(object): + def setupUi(self, KeyWizard): + KeyWizard.setObjectName("KeyWizard") + KeyWizard.resize(400, 189) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(KeyWizard.sizePolicy().hasHeightForWidth()) + KeyWizard.setSizePolicy(sizePolicy) + KeyWizard.setModal(True) + self.label = QtWidgets.QLabel(KeyWizard) + self.label.setGeometry(QtCore.QRect(30, 20, 341, 141)) + self.label.setAlignment(QtCore.Qt.AlignJustify|QtCore.Qt.AlignTop) + self.label.setWordWrap(True) + self.label.setObjectName("label") + self.lineEdit = QtWidgets.QLineEdit(KeyWizard) + self.lineEdit.setGeometry(QtCore.QRect(350, 160, 41, 21)) + self.lineEdit.setCursor(QtGui.QCursor(QtCore.Qt.BlankCursor)) + self.lineEdit.setAutoFillBackground(False) + self.lineEdit.setStyleSheet("background-color: transparent; border:none") + self.lineEdit.setClearButtonEnabled(False) + self.lineEdit.setObjectName("lineEdit") + + self.retranslateUi(KeyWizard) + QtCore.QMetaObject.connectSlotsByName(KeyWizard) + + def retranslateUi(self, KeyWizard): + _translate = QtCore.QCoreApplication.translate + KeyWizard.setWindowTitle(_translate("KeyWizard", "Key Wizard")) + self.label.setText(_translate("KeyWizard", "This is the Key Wizard. Simply press the key you would like to assign, and the key caption and keycodes will be set automatically. If you want to add functions for long presses or doubleclicks, or if you would like to add more captions, you can always edit the key manually later by doubleclicking it.")) diff --git a/src/lib/oskb/ui_valueedit.py b/src/lib/oskb/ui_valueedit.py new file mode 100644 index 0000000..b66c478 --- /dev/null +++ b/src/lib/oskb/ui_valueedit.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ValueEdit.ui' +# +# Created by: PyQt5 UI code generator 5.14.0 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_ValueEdit(object): + def setupUi(self, ValueEdit): + ValueEdit.setObjectName("ValueEdit") + ValueEdit.resize(247, 103) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(ValueEdit.sizePolicy().hasHeightForWidth()) + ValueEdit.setSizePolicy(sizePolicy) + ValueEdit.setMinimumSize(QtCore.QSize(247, 103)) + ValueEdit.setMaximumSize(QtCore.QSize(247, 103)) + self.buttonBox = QtWidgets.QDialogButtonBox(ValueEdit) + self.buttonBox.setGeometry(QtCore.QRect(60, 60, 171, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save) + self.buttonBox.setObjectName("buttonBox") + self.doubleSpinBox = QtWidgets.QDoubleSpinBox(ValueEdit) + self.doubleSpinBox.setGeometry(QtCore.QRect(130, 20, 81, 24)) + self.doubleSpinBox.setDecimals(1) + self.doubleSpinBox.setMinimum(0.1) + self.doubleSpinBox.setSingleStep(0.1) + self.doubleSpinBox.setProperty("value", 0.5) + self.doubleSpinBox.setObjectName("doubleSpinBox") + self.label = QtWidgets.QLabel(ValueEdit) + self.label.setGeometry(QtCore.QRect(10, 20, 111, 21)) + self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label.setObjectName("label") + + self.retranslateUi(ValueEdit) + self.buttonBox.accepted.connect(ValueEdit.accept) + self.buttonBox.rejected.connect(ValueEdit.reject) + QtCore.QMetaObject.connectSlotsByName(ValueEdit) + + def retranslateUi(self, ValueEdit): + _translate = QtCore.QCoreApplication.translate + ValueEdit.setWindowTitle(_translate("ValueEdit", "Enter Value")) + self.label.setText(_translate("ValueEdit", "Width:")) diff --git a/src/ui/login/login.py b/src/ui/login/login.py index c01a251..88e0763 100755 --- a/src/ui/login/login.py +++ b/src/ui/login/login.py @@ -1,6 +1,8 @@ import platform import subprocess import sys +import time + from components import ArchiveSynchronizer from lib.db import Session, Users from PyQt5.QtCore import QTimer, pyqtSignal @@ -63,3 +65,6 @@ class Login(Widget): self.password_le.setFocus() if platform.system().lower() == 'windows': subprocess.Popen(["open_osk.bat"]) # OPEN ON SCREEN KEYBOARD + else: + subprocess.Popen(["./open_oskb.sh"]) # OPEN ON SCREEN KEYBOARD + time.sleep(0.5)