diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/extension.js b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/extension.js index dbe70a7..9d858e4 100644 --- a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/extension.js +++ b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/extension.js @@ -6,7 +6,6 @@ import St from 'gi://St'; import Shell from 'gi://Shell'; -import * as EdgeDragAction from 'resource:///org/gnome/shell/ui/edgeDragAction.js'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; @@ -17,7 +16,13 @@ import * as InputSourceManager from 'resource:///org/gnome/shell/ui/status/keybo import * as Config from 'resource:///org/gnome/shell/misc/config.js' const [major, minor] = Config.PACKAGE_VERSION.split('.').map(s => Number(s)); import { Dialog } from 'resource:///org/gnome/shell/ui/dialog.js'; - +let EdgeDragAction = null; +if (major < 49) { + try { + const edgedragimport = await import('resource:///org/gnome/shell/ui/edgeDragAction.js') + EdgeDragAction = edgedragimport ?? null + } catch { } +} import { Extension, gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js'; const State = { @@ -82,12 +87,14 @@ export default class GjsOskExtension extends Extension { _openKeyboard(instant) { if (this.Keyboard.state == State.CLOSED) { this.Keyboard.open(null, !instant ? null : true); + this.openBit.set_boolean('keyboard-visible', true); } } _closeKeyboard(instant) { if (this.Keyboard.state == State.OPENED) { this.Keyboard.close(!instant ? null : true); + this.openBit.set_boolean('keyboard-visible', false); } } @@ -104,7 +111,8 @@ export default class GjsOskExtension extends Extension { } open_interval() { - global.stage.disconnect(this.tapConnect) + if (this.tapConnect && GObject.signal_handler_is_connected(global.stage, this.tapConnect)) + global.stage.disconnect(this.tapConnect) if (this.openInterval !== null) { clearInterval(this.openInterval); this.openInterval = null; @@ -130,13 +138,26 @@ export default class GjsOskExtension extends Extension { }, 300); this.tapConnect = global.stage.connect("event", (_actor, event) => { if (event.type() !== 4 && event.type() !== 5) { - this.lastInputMethod = [false, event.type() >= 9 && event.type() <= 12, true][this.settings.get_int("enable-tap-gesture")] + this.lastInputMethod = [false, event.type() >= 9 && event.type() <= 12, true][this.settings.get_boolean("indicator-enabled") ? this.settings.get_int("enable-tap-gesture") : 0] } }) } + fail(error, method = null, instance = null) { + Main.notifyError("Error with extension GJS-OSK" + + (instance != null || method != null ? " in " : "") + + (instance != null ? instance + "." : "") + + (method != null ? method + "\n" : ""), + error.message + + "\n" + error.stack); + } + enable() { this.settings = this.getSettings(); + if (this.settings == null) { + return; + } + this.disableEdgeSwipeSettings = this.settings.get_boolean("disable-edge-swipe"); this.darkSchemeSettings = this.getSettings("org.gnome.desktop.interface"); this.inputLanguageSettings = InputSourceManager.getInputSourceManager(); this.gnomeKeyboardSettings = this.getSettings('org.gnome.desktop.a11y.applications'); @@ -158,44 +179,203 @@ export default class GjsOskExtension extends Extension { } let refresh = () => { - // [insert handwriting 2] - let currentMonitors = this.settings.get_string("default-monitor").split(";") - let currentMonitorMap = {}; - let monitors = Main.layoutManager.monitors; - for (var i of currentMonitors) { - let tmp = i.split(":"); - currentMonitorMap[tmp[0]] = tmp[1] + ""; - } - if (!Object.keys(currentMonitorMap).includes(monitors.length + "")) { - let allConfigs = Object.keys(currentMonitorMap).map(Number.parseInt).sort(); - currentMonitorMap[monitors.length + ""] = allConfigs[allConfigs.length - 1]; - } - try { - currentMonitorId = global.backend.get_monitor_manager().get_monitor_for_connector(currentMonitorMap[monitors.length + ""]); - if (currentMonitorId == -1) { + (async () => { + // [insert handwriting 2] + let prevOpenState = false; + if (this.Keyboard != null) { + prevOpenState = this.Keyboard.opened; + } + let currentMonitors = this.settings.get_string("default-monitor").split(";") + let currentMonitorMap = {}; + let monitors = Main.layoutManager.monitors; + if (Main.layoutManager.primaryMonitor == null) { + this.waitRefresh = setTimeout(refresh, 1000) + return; + } + for (var i of currentMonitors) { + let tmp = i.split(":"); + currentMonitorMap[tmp[0]] = tmp[1] + ""; + } + if (!Object.keys(currentMonitorMap).includes(monitors.length + "")) { + let allConfigs = Object.keys(currentMonitorMap).map(Number.parseInt).sort(); + currentMonitorMap[monitors.length + ""] = allConfigs[allConfigs.length - 1]; + } + try { + currentMonitorId = global.backend.get_monitor_manager().get_monitor_for_connector(currentMonitorMap[monitors.length + ""]); + if (currentMonitorId == -1) { + currentMonitorId = 0; + } + } catch { currentMonitorId = 0; } - } catch { - currentMonitorId = 0; - } - if (!Gio.File.new_for_path(extract_dir).query_exists(null)) { - Gio.File.new_for_path(extract_dir).make_directory(null); - Gio.File.new_for_path(extract_dir + "/keycodes").make_directory(null); - let [status, out, err, code] = GLib.spawn_command_line_sync("tar -Jxf " + this.path + "/keycodes.tar.xz -C " + extract_dir + "/keycodes") - if (err != "" || code != 0) { - throw new Error(err); - } - } - if (this.Keyboard != null) { - this.Keyboard.destroy(); - this.Keyboard = null; - } - let [ok, contents] = GLib.file_get_contents(extract_dir + "/keycodes/" + KeyboardManager.getKeyboardManager().currentLayout.id + '.json'); - if (ok) { - keycodes = JSON.parse(contents); - } - this.Keyboard = new Keyboard(this.settings, this); - this.Keyboard.refresh = refresh + const fileExists = async (file) => { + try { + return await new Promise((resolve) => { + file.query_info_async( + '*', + Gio.FileQueryInfoFlags.NONE, + GLib.PRIORITY_DEFAULT, + null, + (source, res) => { + try { + source.query_info_finish(res); + resolve(true); + } catch (e) { + resolve(false); + } + } + ); + }); + } catch (e) { + return false; + } + }; + + const ensureDirectory = async (dir) => { + try { + if (!(await fileExists(dir))) { + await new Promise((resolve, reject) => { + dir.make_directory_async(GLib.PRIORITY_DEFAULT, null, (source, res) => { + try { + source.make_directory_finish(res); + resolve(); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) { + reject(e); + } else { + resolve(); + } + } + }); + }); + } + return true; + } catch (e) { + console.error(`Failed to create directory ${dir.get_path()}: ${e.message}`); + return false; + } + }; + + const runCommand = (argv) => { + return new Promise((resolve) => { + const proc = Gio.Subprocess.new( + argv, + Gio.SubprocessFlags.STDOUT_PIPE | + Gio.SubprocessFlags.STDERR_PIPE + ); + proc.wait_check_async(null, (source, res) => { + try { + const success = source.wait_check_finish(res); + resolve(success); + } catch (e) { + console.error(`Command failed: ${argv.join(' ')}`); + resolve(false); + } + }); + }); + }; + + const extractKeycodes = async () => { + const baseDir = Gio.File.new_for_path(extract_dir); + const keycodesDir = Gio.File.new_for_path(extract_dir + "/keycodes"); + const layoutId = KeyboardManager.getKeyboardManager().currentLayout?.id || "us"; + const targetFile = Gio.File.new_for_path(`${extract_dir}/keycodes/${layoutId}.json`); + + if (await fileExists(targetFile)) { + return true; + } + + if (!(await ensureDirectory(baseDir))) { + console.error("Failed to create base directory"); + return false; + } + + if (!(await ensureDirectory(keycodesDir))) { + console.error("Failed to create keycodes directory"); + return false; + } + + try { + const success = await runCommand([ + "tar", + "-Jxf", + this.path + "/keycodes.tar.xz", + "-C", + keycodesDir.get_path(), + "--strip-components=1" + ]); + + if (!success) { + throw new Error("Failed to extract keycodes archive"); + } + + if (!(await fileExists(targetFile))) { + throw new Error("Keycodes file not found after extraction"); + } + + return true; + } catch (error) { + console.error(`Error extracting keycodes: ${error.message}`); + return false; + } + }; + + const initializeKeyboard = async () => { + const layoutId = KeyboardManager.getKeyboardManager().currentLayout?.id || "us"; + const keycodesPath = GLib.build_filenamev([ + extract_dir, + "keycodes", + `${layoutId}.json` + ]); + + const [ok, contents] = GLib.file_get_contents(keycodesPath); + if (!ok) { + throw new Error(`Failed to read keycodes from ${keycodesPath}`); + } + + keycodes = JSON.parse(contents); + + if (this.Keyboard) { + this.Keyboard.destroy(); + this.Keyboard = null; + } + + if (this.settings == null) { + return; + } + + function withErrorHandler(TargetClass, handler) { + return new Proxy(TargetClass, { + construct(Target, args) { + const instance = new Target(...args); + for (const key of Object.getOwnPropertyNames(Target.prototype)) { + const fn = instance[key]; + if (typeof fn === "function" && key !== "constructor") { + instance[key] = async (...fnArgs) => { + try { + return await fn.apply(instance, fnArgs); + } catch (err) { + handler(err, key, instance); + } + }; + } + } + + return instance; + } + }); + } + const SafeKeyboard = withErrorHandler(Keyboard, this.fail); + this.Keyboard = new SafeKeyboard(this.settings, this); + this.Keyboard.refresh = refresh; + if (prevOpenState) { + this.Keyboard.open(null, true); + this.openBit.set_boolean('keyboard-visible', true); + } + }; + await extractKeycodes(); + await initializeKeyboard(); + })().catch(this.fail) } refresh() @@ -226,21 +406,29 @@ export default class GjsOskExtension extends Extension { this._quick_settings_indicator.quickSettingsItems.push(this._toggle); Main.panel.statusArea.quickSettings.addExternalIndicator(this._quick_settings_indicator); this.open_interval(); - this.openFromCommandHandler = this.openBit.connect("changed", () => { - this.openBit.set_boolean("opened", false) - this._toggleKeyboard(); - }) + + this.keyboardVisibilityHandler = this.openBit.connect('changed::keyboard-visible', () => { + const shouldBeVisible = this.openBit.get_boolean('keyboard-visible'); + if (shouldBeVisible !== this.Keyboard?.opened) { + this._toggleKeyboard(true); + } + }); + + this.openFromCommandHandler = this.openBit.connect("changed::opened", () => { + if (this.openBit.get_boolean("opened")) { + this.openBit.set_boolean("opened", false); + this._toggleKeyboard(); + } + }); + + if (this.openBit.get_boolean('keyboard-visible') && this.Keyboard) { + this._openKeyboard(true); + } let settingsChanged = () => { - let opened; - if (this.Keyboard != null) - opened = this.Keyboard.opened - else - opened = false if (this.darkSchemeSettings.get_string("color-scheme") == "prefer-dark") this.settings.scheme = "-dark" else this.settings.scheme = "" - this.Keyboard.openedFromButton = false; refresh() this._toggle._refresh(); if (this.settings.get_boolean("indicator-enabled")) { @@ -274,8 +462,24 @@ export default class GjsOskExtension extends Extension { this.openInterval = null; } this.open_interval(); - if (opened) { - this._toggleKeyboard(true); + let disableEdgeSwipe = this.settings.get_boolean("disable-edge-swipe"); + if (this.disableEdgeSwipeSettings !== disableEdgeSwipe) { + this.disableEdgeSwipeSettings = disableEdgeSwipe; + if (disableEdgeSwipe) { + if (EdgeDragAction != null) { + global.stage.remove_action_by_name('osk'); + } else { + global.stage.remove_action_by_name('GJS-OSK Edge Drag Action'); + } + } else { + if (this.keyboard != null && this.keyboard.bottomDragAction != null) { + if (EdgeDragAction != null) { + global.stage.add_action_full('osk', Clutter.EventPhase.CAPTURE, this.keyboard.bottomDragAction); + } else { + global.stage.add_action(this.keyboard.bottomDragAction); + } + } + } } } this.settingsHandlers = [ @@ -306,15 +510,24 @@ export default class GjsOskExtension extends Extension { this.darkSchemeSettings = null; this.inputLanguageSettings = null; this.gnomeKeyboardSettings = null; - this.openBit.disconnect(this.openFromCommandHandler); + if (this.keyboardVisibilityHandler) { + this.openBit.disconnect(this.keyboardVisibilityHandler); + this.keyboardVisibilityHandler = null; + } + if (this.openFromCommandHandler) { + this.openBit.disconnect(this.openFromCommandHandler); + this.openFromCommandHandler = null; + } this.openBit = null; global.stage.disconnect(this.tapConnect) if (this.openInterval !== null) { clearInterval(this.openInterval); this.openInterval = null; } - this._toggle.destroy() - this._toggle = null + if (this._toggle !== null) { + this._toggle.destroy() + this._toggle = null + } this.settings = null this.Keyboard = null keycodes = null @@ -341,14 +554,15 @@ class Keyboard extends Dialog { this.settingsOpenFunction = extensionObject.openPrefs this.inputDevice = Clutter.get_default_backend().get_default_seat().create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE); this.settings = settings; - let monitor = Main.layoutManager.monitors[currentMonitorId]; - super._init(Main.layoutManager.modalDialogGroup, 'db-keyboard-content'); + let monitor = Main.layoutManager.monitors[currentMonitorId] ?? Main.layoutManager.primaryMonitor; + super._init(Main.uiGroup, 'db-keyboard-content'); this.box = new St.Widget({ reactive: true, layout_manager: new Clutter.GridLayout({ orientation: Clutter.Orientation.HORIZONTAL, }) }); + this.box.clear_actions(); this.widthPercent = (monitor.width > monitor.height) ? settings.get_int("landscape-width-percent") / 100 : settings.get_int("portrait-width-percent") / 100; this.heightPercent = (monitor.width > monitor.height) ? settings.get_int("landscape-height-percent") / 100 : settings.get_int("portrait-height-percent") / 100; this.nonDragBlocker = new Clutter.Actor(); @@ -367,7 +581,9 @@ class Keyboard extends Dialog { this.state = State.CLOSED; this.delta = []; this.monitorChecker = global.backend.get_monitor_manager().connect('monitors-changed', () => { - this.refresh() + if (Main.layoutManager.monitors.length > 0) { + this.refresh(); + } }); this._dragging = false; let side = null; @@ -389,41 +605,79 @@ class Keyboard extends Dialog { side = St.Side.BOTTOM; break; } - this.oldBottomDragAction = global.stage.get_action('osk'); + this.oldBottomDragAction = global.stage.get_action(EdgeDragAction == null ? 'OSK show bottom drag' : 'osk'); if (this.oldBottomDragAction !== null && this.oldBottomDragAction instanceof Clutter.Action) global.stage.remove_action(this.oldBottomDragAction); if (side != null) { const mode = Shell.ActionMode.ALL & ~Shell.ActionMode.LOCK_SCREEN; - const bottomDragAction = new EdgeDragAction.EdgeDragAction(side, mode); - bottomDragAction.connect('activated', () => { - this.open(true); - this.openedFromButton = true; - this.closedFromButton = false; - this.gestureInProgress = false; - }); - bottomDragAction.connect('progress', (_action, progress) => { - if (!this.gestureInProgress) - this.open(false) - this.setOpenState(Math.min(Math.max(0, (progress / (side % 2 == 0 ? this.box.height : this.box.width)) * 100), 100)) - this.gestureInProgress = true; - }); - bottomDragAction.connect('gesture-cancel', () => { - if (this.gestureInProgress) { - this.close() - this.openedFromButton = false; - this.closedFromButton = true; + let oskEdgeDragAction; + if (EdgeDragAction != null) { + oskEdgeDragAction = new EdgeDragAction.EdgeDragAction(side, mode); + oskEdgeDragAction.connect('activated', () => { + this.open(true); + this.openedFromButton = true; + this.closedFromButton = false; + this.gestureInProgress = false; + }); + oskEdgeDragAction.connect('gesture-cancel', () => { + if (this.gestureInProgress) { + this.close() + this.openedFromButton = false; + this.closedFromButton = true; + } + this.gestureInProgress = false; + return Clutter.EVENT_PROPAGATE; + }); + oskEdgeDragAction.connect('progress', (_action, progress) => { + if (!this.gestureInProgress) + this.open(false) + this.setOpenState(Math.min(Math.max(0, (progress / (side % 2 == 0 ? this.box.height : this.box.width)) * 100), 100)) + this.gestureInProgress = true; + }); + if (!this.settings.get_boolean("disable-edge-swipe")) { + global.stage.add_action_full('osk', Clutter.EventPhase.CAPTURE, oskEdgeDragAction); } - this.gestureInProgress = false; - return Clutter.EVENT_PROPAGATE; - }); - global.stage.add_action_full('osk', Clutter.EventPhase.CAPTURE, bottomDragAction); - this.bottomDragAction = bottomDragAction; + this.bottomDragAction = oskEdgeDragAction; + } else { + oskEdgeDragAction = new Shell.EdgeDragGesture({ + name: 'GJS-OSK Edge Drag Action', + side + }) + oskEdgeDragAction.connect('may-recognize', () => { + return Shell.ActionMode.ALL & ~Shell.ActionMode.LOCK_SCREEN & Main.actionMode; + }) + oskEdgeDragAction.connect('end', () => { + this.open(true); + this.openedFromButton = true; + this.closedFromButton = false; + this.gestureInProgress = false; + }); + oskEdgeDragAction.connect('cancel', () => { + if (this.gestureInProgress) { + this.close() + this.openedFromButton = false; + this.closedFromButton = true; + } + this.gestureInProgress = false; + return Clutter.EVENT_PROPAGATE; + }); + oskEdgeDragAction.connect('progress', (_action, progress) => { + if (!this.gestureInProgress) + this.open(false) + this.setOpenState(Math.min(Math.max(0, (progress / (side % 2 == 0 ? this.box.height : this.box.width)) * 100), 100)) + this.gestureInProgress = true; + }); + if (!this.settings.get_boolean("disable-edge-swipe")) { + global.stage.add_action(oskEdgeDragAction); + } + this.bottomDragAction = oskEdgeDragAction; + } } else { this.bottomDragAction = null; } this._oldMaybeHandleEvent = Main.keyboard.maybeHandleEvent Main.keyboard.maybeHandleEvent = (e) => { - let lastInputMethod = [e.type() == 11, e.type() == 11, e.type() == 7 || e.type() == 11][this.settings.get_int("enable-tap-gesture")] + let lastInputMethod = [e.type() == 11, e.type() == 11, e.type() == 7 || e.type() == 11][this.settings.get_boolean("indicator-enabled") ? this.settings.get_int("enable-tap-gesture") : 0] let ac = global.stage.get_event_actor(e) if (this.contains(ac)) { ac.event(e, true); @@ -438,9 +692,14 @@ class Keyboard extends Dialog { destroy() { Main.keyboard.maybeHandleEvent = this._oldMaybeHandleEvent - global.stage.remove_action_by_name('osk') - if (this.oldBottomDragAction !== null && this.oldBottomDragAction instanceof Clutter.Action) + if (this.oldBottomDragAction !== null && this.oldBottomDragAction instanceof Clutter.Action && EdgeDragAction != null) { + global.stage.remove_action_by_name('osk') global.stage.add_action_full('osk', Clutter.EventPhase.CAPTURE, this.oldBottomDragAction) + } + else if (this.oldBottomDragAction != null) { + global.stage.remove_action_by_name('GJS-OSK Edge Drag Action') + global.stage.add_action(this.oldBottomDragAction) + } if (this.textboxChecker !== null) { clearInterval(this.textboxChecker); this.textboxChecker = null; @@ -451,10 +710,16 @@ class Keyboard extends Dialog { } if (this.keyTimeout !== null) { clearTimeout(this.keyTimeout); + if (typeof this.keyTimeoutFunc === 'function') { + this.keyTimeoutFunc(); + } this.keyTimeout = null; + this.keyTimeoutFunc = null; } - this.keymap.disconnect(this.capslockConnect); - this.keymap.disconnect(this.numLockConnect); + if (this.capsLockConnect && GObject.signal_handler_is_connected(this.keymap, this.capsLockConnect)) + this.keymap.disconnect(this.capsLockConnect); + if (this.numLockConnect && GObject.signal_handler_is_connected(this.keymap, this.numLockConnect)) + this.keymap.disconnect(this.numLockConnect); global.backend.get_monitor_manager().disconnect(this.monitorChecker) super.destroy(); if (this.nonDragBlocker !== null) { @@ -474,10 +739,8 @@ class Keyboard extends Dialog { mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { } }); - let device = event.get_device(); let sequence = event.get_event_sequence(); this._grab = global.stage.grab(this); - this._grabbedDevice = device; this._grabbedSequence = sequence; this.emit('drag-begin'); let [absX, absY] = event.get_coords(); @@ -508,7 +771,6 @@ class Keyboard extends Dialog { onComplete: () => { } }); this._grabbedSequence = null; - this._grabbedDevice = null; this._dragging = false; this.delta = []; this.emit('drag-end'); @@ -532,7 +794,7 @@ class Keyboard extends Dialog { } snapMovement(xPos, yPos) { - let monitor = Main.layoutManager.monitors[currentMonitorId] + let monitor = Main.layoutManager.monitors[currentMonitorId] ?? Main.layoutManager.primaryMonitor if (xPos < monitor.x || yPos < monitor.y || xPos > monitor.x + monitor.width || yPos > monitor.y + monitor.width) { this.set_translation(xPos, yPos, 0); return; @@ -558,7 +820,7 @@ class Keyboard extends Dialog { } setOpenState(percent) { - let monitor = Main.layoutManager.monitors[currentMonitorId]; + let monitor = Main.layoutManager.monitors[currentMonitorId] ?? Main.layoutManager.primaryMonitor; let posX = [this.settings.get_int("snap-spacing-px"), ((monitor.width * .5) - ((this.width * .5))), monitor.width - this.width - this.settings.get_int("snap-spacing-px")][(this.settings.get_int("default-snap") % 3)]; let posY = [this.settings.get_int("snap-spacing-px"), ((monitor.height * .5) - ((this.height * .5))), monitor.height - this.height - this.settings.get_int("snap-spacing-px")][Math.floor((this.settings.get_int("default-snap") / 3))]; let mX = [-this.box.width, 0, this.box.width][(this.settings.get_int("default-snap") % 3)]; @@ -579,7 +841,7 @@ class Keyboard extends Dialog { this.show(); } if (noPrep == null || noPrep) { - let monitor = Main.layoutManager.monitors[currentMonitorId]; + let monitor = Main.layoutManager.monitors[currentMonitorId] ?? Main.layoutManager.primaryMonitor; let posX = [this.settings.get_int("snap-spacing-px"), ((monitor.width * .5) - ((this.width * .5))), monitor.width - this.width - this.settings.get_int("snap-spacing-px")][(this.settings.get_int("default-snap") % 3)]; let posY = [this.settings.get_int("snap-spacing-px"), ((monitor.height * .5) - ((this.height * .5))), monitor.height - this.height - this.settings.get_int("snap-spacing-px")][Math.floor((this.settings.get_int("default-snap") / 3))]; if (noPrep == null) { @@ -620,7 +882,7 @@ class Keyboard extends Dialog { close(instant = null) { this.prevKeyFocus = null; - let monitor = Main.layoutManager.monitors[currentMonitorId]; + let monitor = Main.layoutManager.monitors[currentMonitorId] ?? Main.layoutManager.primaryMonitor; let posX = [this.settings.get_int("snap-spacing-px"), ((monitor.width * .5) - ((this.width * .5))), monitor.width - this.width - this.settings.get_int("snap-spacing-px")][(this.settings.get_int("default-snap") % 3)]; let posY = [this.settings.get_int("snap-spacing-px"), ((monitor.height * .5) - ((this.height * .5))), monitor.height - this.height - this.settings.get_int("snap-spacing-px")][Math.floor((this.settings.get_int("default-snap") / 3))]; let mX = [-this.box.width, 0, this.box.width][(this.settings.get_int("default-snap") % 3)]; @@ -679,6 +941,14 @@ class Keyboard extends Dialog { return Clutter.EVENT_PROPAGATE; } + vfunc_key_press_event() { + return Clutter.EVENT_PROPAGATE; + } + + vfunc_key_release_event() { + return Clutter.EVENT_PROPAGATE; + } + vfunc_touch_event() { let event = Clutter.get_current_event(); let sequence = event.get_event_sequence(); @@ -701,9 +971,17 @@ class Keyboard extends Dialog { buildUI() { this.box.set_opacity(0); this.keys = []; - let monitor = Main.layoutManager.monitors[currentMonitorId] - let layoutName = Object.keys(layouts)[(monitor.width > monitor.height) ? this.settings.get_int("layout-landscape") : this.settings.get_int("layout-portrait")]; - this.box.width = Math.round((monitor.width - this.settings.get_int("snap-spacing-px") * 2) * (layoutName.includes("Split") ? 1 : this.widthPercent)) + let monitor = Main.layoutManager.monitors[currentMonitorId] ?? Main.layoutManager.primaryMonitor + let layoutIdx = (monitor.width > monitor.height) ? this.settings.get_int("layout-landscape") : this.settings.get_int("layout-portrait") + let currentLayout; + if (layoutIdx < 10) { + let layoutName = Object.keys(layouts)[layoutIdx]; + currentLayout = layouts[layoutName]; + } + else { + currentLayout = JSON.parse(this.settings.get_string("custom-layout")) + } + this.box.width = Math.round((monitor.width - this.settings.get_int("snap-spacing-px") * 2) * (currentLayout[currentLayout.length - 1].split ? 1 : this.widthPercent)) this.box.height = Math.round((monitor.height - this.settings.get_int("snap-spacing-px") * 2) * this.heightPercent) if (!this.settings.get_boolean("enable-drag")) { @@ -749,7 +1027,7 @@ class Keyboard extends Dialog { const grid = this.box.layout_manager grid.set_row_homogeneous(true) - grid.set_column_homogeneous(!layoutName.includes("Split")) + grid.set_column_homogeneous(!currentLayout[currentLayout.length - 1].split) let gridLeft; let gridRight; @@ -758,7 +1036,7 @@ class Keyboard extends Dialog { let right; let topBtnWidth; - if (layoutName.includes("Split")) { + if (currentLayout[currentLayout.length - 1].split) { this.box.reactive = false; left = new St.Widget({ reactive: true, @@ -792,17 +1070,16 @@ class Keyboard extends Dialog { this.shiftButtons = []; // [insert handwriting 7] - let currentLayout = layouts[layoutName]; let width = 0; for (const c of currentLayout[0]) { - width += (Object.hasOwn(c, "width") ? c.width : 1) + width += (("width" in c) ? c.width : 1) } let rowSize; let halfSize; let r = 0; let c; const doAddKey = (keydef) => { - const i = Object.hasOwn(keydef, "key") ? keycodes[keydef.key] : Object.hasOwn(keydef, "split") ? "split" : "empty space"; + const i = ("key" in keydef) ? keycodes[keydef.key] : ("split" in keydef) ? "split" : "empty space"; if (i != null && typeof i !== 'string') { if (i.layers.default == null) { for (var key of Object.keys(i.layers)) { @@ -816,7 +1093,7 @@ class Keyboard extends Dialog { let iconKeys = ["left", "up", "right", "down", "space"] if (this.settings.get_boolean("show-icons")) { - iconKeys = ["left", "up", "right", "down", "backspace", "tab", "capslock", "shift", "enter", "ctrl", "super", "alt", "space"] + iconKeys = ["left", "up", "right", "down", "backspace", "tab", "capslock", "shift", "enter", "ctrl", "super", "alt", "space", "menu"] } // [insert handwriting 8] if (iconKeys.some(j => { return i.layers.default.toLowerCase() == j })) { @@ -829,7 +1106,7 @@ class Keyboard extends Dialog { params.label = i.layers.default } i.isMod = false - if ([42, 54, 29, 125, 56, 100, 97, 58, 69].some(j => { return i.code == j })) { + if ([42, 54, 29, 125, 126, 56, 100, 97, 58, 69].some(j => { return i.code == j })) { i.isMod = true; } const keyBtn = new St.Button(params) @@ -837,7 +1114,7 @@ class Keyboard extends Dialog { keyBtn.char = i if (i.code == 58) { this.keymap = Clutter.get_default_backend().get_default_seat().get_keymap() - this.capslockConnect = this.keymap.connect("state-changed", (a, e) => { + this.capsLockConnect = this.keymap.connect("state-changed", (a, e) => { this.setCapsLock(keyBtn, this.keymap.get_caps_lock_state()) }) this.updateCapsLock = () => this.setCapsLock(keyBtn, this.keymap.get_caps_lock_state()) @@ -850,13 +1127,13 @@ class Keyboard extends Dialog { } else if (i.code == 42 || i.code == 54) { this.shiftButtons.push(keyBtn) } - currentGrid.attach(keyBtn, c, 5 + r, (Object.hasOwn(keydef, "width") ? keydef.width : 1) * 2, r == 0 ? 3 : (Object.hasOwn(keydef, "height") ? keydef.height : 1) * 4) + currentGrid.attach(keyBtn, c, 5 + r, (("width" in keydef) ? keydef.width : 1) * 2, r == 0 ? 3 : (("height" in keydef) ? keydef.height : 1) * 4) keyBtn.visible = true - c += (Object.hasOwn(keydef, "width") ? keydef.width : 1) * 2 + c += (("width" in keydef) ? keydef.width : 1) * 2 this.keys.push(keyBtn) // [insert handwriting 9] } else if (i == "empty space") { - c += (Object.hasOwn(keydef, "width") ? keydef.width : 1) * 2 + c += (("width" in keydef) ? keydef.width : 1) * 2 } else if (i == "split") { currentGrid = gridRight const size = c @@ -864,21 +1141,21 @@ class Keyboard extends Dialog { } } - for (const kRow of currentLayout) { + for (const kRow of currentLayout.slice(0, -1)) { c = 0; - if (layoutName.includes("Split")) { + if (currentLayout[currentLayout.length - 1].split) { currentGrid = gridLeft; } for (const keydef of kRow) { if (keydef instanceof Array) { - keydef.forEach(i => { doAddKey(i); r += 2; c -= (Object.hasOwn(i, "width") ? i.width : 1) * 2 }); - c += (Object.hasOwn(keydef[0], "width") ? keydef[0].width : 1) * 2; + keydef.forEach(i => { doAddKey(i); r += 2; c -= (("width" in i) ? i.width : 1) * 2 }); + c += (("width" in keydef[0]) ? keydef[0].width : 1) * 2; r -= 4; } else { doAddKey(keydef) } } - if (!topBtnWidth) topBtnWidth = ((Object.hasOwn(kRow[kRow.length - 1], "width") && (Object.hasOwn(kRow[kRow.length - 1], "key"))) ? kRow[kRow.length - 1].width : 1) + if (!topBtnWidth) topBtnWidth = ((("width" in kRow[kRow.length - 1]) && ("key" in kRow[kRow.length - 1])) ? kRow[kRow.length - 1].width : 1) const size = c; if (!rowSize) rowSize = size; r += r == 0 ? 3 : 4 @@ -907,37 +1184,60 @@ class Keyboard extends Dialog { left.add_style_class_name("regular"); right.add_style_class_name("regular"); } - const settingsBtn = new St.Button({ - x_expand: true, - y_expand: true - }) - settingsBtn.add_style_class_name("settings_btn") - settingsBtn.add_style_class_name("key") - settingsBtn.connect("clicked", () => { - this.settingsOpenFunction(); - }) - gridLeft.attach(settingsBtn, 0, 0, 2 * topBtnWidth, 3) - this.keys.push(settingsBtn) - const closeBtn = new St.Button({ - x_expand: true, - y_expand: true - }) - closeBtn.add_style_class_name("close_btn") - closeBtn.add_style_class_name("key") - closeBtn.connect("clicked", () => { - this.close(); - this.closedFromButton = true; - }) - gridRight.attach(closeBtn, (rowSize - 2 * topBtnWidth), 0, 2 * topBtnWidth, 3) - this.keys.push(closeBtn) + let mvBtnStartLeft = 2 * topBtnWidth + let mvBtnEndRight = 2 * topBtnWidth + + if (currentLayout[currentLayout.length - 1].settings) { + const settingsBtn = new St.Button({ + x_expand: true, + y_expand: true + }) + settingsBtn.add_style_class_name("settings_btn") + settingsBtn.add_style_class_name("key") + settingsBtn.connect("button-press-event", () => { + this.settingsOpenFunction(); + }) + settingsBtn.connect("touch-event", () => { + if (Clutter.get_current_event().type() == Clutter.EventType.TOUCH_BEGIN) + this.settingsOpenFunction(); + }) + this.keys.push(settingsBtn) + gridLeft.attach(settingsBtn, 0, 0, 2 * topBtnWidth, 3) + } else { + mvBtnStartLeft = 0 + } + + if (currentLayout[currentLayout.length - 1].close) { + const closeBtn = new St.Button({ + x_expand: true, + y_expand: true + }) + closeBtn.add_style_class_name("close_btn") + closeBtn.add_style_class_name("key") + closeBtn.connect("button-press-event", () => { + this.close(); + this.closedFromButton = true; + }) + closeBtn.connect("touch-event", () => { + if (Clutter.get_current_event().type() == Clutter.EventType.TOUCH_BEGIN) { + this.close(); + this.closedFromButton = true; + } + }) + gridRight.attach(closeBtn, (rowSize - 2 * topBtnWidth), 0, 2 * topBtnWidth, 3) + this.keys.push(closeBtn) + } else { + mvBtnEndRight = 0 + } let moveHandleLeft = new St.Button({ x_expand: true, y_expand: true }) + moveHandleLeft.clear_actions() moveHandleLeft.add_style_class_name("moveHandle") - moveHandleLeft.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? "5px;" : "0;") + "background-size: " + this.settings.get_int("font-size-px") + "px; font-weight: " + (this.settings.get_boolean("font-bold") ? "bold" : "normal") + "; border: " + this.settings.get_int("border-spacing-px") + "px solid transparent;"); + moveHandleLeft.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? "5px" : "0") + "; background-size: " + this.settings.get_int("font-size-px") + "px; font-weight: " + (this.settings.get_boolean("font-bold") ? "bold" : "normal") + "; border: " + this.settings.get_int("border-spacing-px") + "px solid transparent;"); if (this.lightOrDark()) { moveHandleLeft.add_style_class_name("inverted"); } else { @@ -950,14 +1250,15 @@ class Keyboard extends Dialog { } this.event(event, false) }) - gridLeft.attach(moveHandleLeft, 2 * topBtnWidth, 0, (halfSize - 2 * topBtnWidth), 3) + gridLeft.attach(moveHandleLeft, mvBtnStartLeft, 0, (halfSize - mvBtnStartLeft), 3) let moveHandleRight = new St.Button({ x_expand: true, y_expand: true }) + moveHandleRight.clear_actions() moveHandleRight.add_style_class_name("moveHandle") - moveHandleRight.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? "5px;" : "0;") + "background-size: " + this.settings.get_int("font-size-px") + "px; font-weight: " + (this.settings.get_boolean("font-bold") ? "bold" : "normal") + "; border: " + this.settings.get_int("border-spacing-px") + "px solid transparent;"); + moveHandleRight.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? "5px" : "0") + "; background-size: " + this.settings.get_int("font-size-px") + "px; font-weight: " + (this.settings.get_boolean("font-bold") ? "bold" : "normal") + "; border: " + this.settings.get_int("border-spacing-px") + "px solid transparent;"); if (this.lightOrDark()) { moveHandleRight.add_style_class_name("inverted"); } else { @@ -970,7 +1271,7 @@ class Keyboard extends Dialog { } this.event(event, false) }) - gridRight.attach(moveHandleRight, halfSize, 0, (rowSize - halfSize - 2 * topBtnWidth), 3) + gridRight.attach(moveHandleRight, halfSize, 0, (rowSize - halfSize - mvBtnEndRight), 3) gridLeft.attach(new St.Widget({ x_expand: true, y_expand: true }), 0, 3, halfSize, 1) gridRight.attach(new St.Widget({ x_expand: true, y_expand: true }), halfSize, 3, (rowSize - halfSize), 1) } else { @@ -990,30 +1291,50 @@ class Keyboard extends Dialog { this.box.add_style_class_name("regular"); } - const settingsBtn = new St.Button({ - x_expand: true, - y_expand: true - }) - settingsBtn.add_style_class_name("settings_btn") - settingsBtn.add_style_class_name("key") - settingsBtn.connect("clicked", () => { - this.settingsOpenFunction(); - }) - grid.attach(settingsBtn, 0, 0, 2 * topBtnWidth, 3) - this.keys.push(settingsBtn) + let mvBtnStartLeft = 2 * topBtnWidth + let mvBtnEndRight = 2 * topBtnWidth - const closeBtn = new St.Button({ - x_expand: true, - y_expand: true - }) - closeBtn.add_style_class_name("close_btn") - closeBtn.add_style_class_name("key") - closeBtn.connect("clicked", () => { - this.close(); - this.closedFromButton = true; - }) - grid.attach(closeBtn, (rowSize - 2 * topBtnWidth), 0, 2 * topBtnWidth, 3) - this.keys.push(closeBtn) + if (currentLayout[currentLayout.length - 1].settings) { + const settingsBtn = new St.Button({ + x_expand: true, + y_expand: true + }) + settingsBtn.add_style_class_name("settings_btn") + settingsBtn.add_style_class_name("key") + settingsBtn.connect("button-press-event", () => { + this.settingsOpenFunction(); + }) + settingsBtn.connect("touch-event", () => { + if (Clutter.get_current_event().type() == Clutter.EventType.TOUCH_BEGIN) + this.settingsOpenFunction(); + }) + this.keys.push(settingsBtn) + grid.attach(settingsBtn, 0, 0, 2 * topBtnWidth, 3) + } else { + mvBtnStartLeft = 0 + } + if (currentLayout[currentLayout.length - 1].close) { + const closeBtn = new St.Button({ + x_expand: true, + y_expand: true + }) + closeBtn.add_style_class_name("close_btn") + closeBtn.add_style_class_name("key") + closeBtn.connect("button-press-event", () => { + this.close(); + this.closedFromButton = true; + }) + closeBtn.connect("touch-event", () => { + if (Clutter.get_current_event().type() == Clutter.EventType.TOUCH_BEGIN) { + this.close(); + this.closedFromButton = true; + } + }) + grid.attach(closeBtn, (rowSize - 2 * topBtnWidth), 0, 2 * topBtnWidth, 3) + this.keys.push(closeBtn) + } else { + mvBtnEndRight = 0; + } // [insert handwriting 10] @@ -1021,8 +1342,9 @@ class Keyboard extends Dialog { x_expand: true, y_expand: true }) + moveHandle.clear_actions(); moveHandle.add_style_class_name("moveHandle") - moveHandle.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? "5px;" : "0;") + "background-size: " + this.settings.get_int("font-size-px") + "px; font-weight: " + (this.settings.get_boolean("font-bold") ? "bold" : "normal") + "; border: " + this.settings.get_int("border-spacing-px") + "px solid transparent;"); + moveHandle.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? "5px" : "0") + "; background-size: " + this.settings.get_int("font-size-px") + "px; font-weight: " + (this.settings.get_boolean("font-bold") ? "bold" : "normal") + "; border: " + this.settings.get_int("border-spacing-px") + "px solid transparent;"); if (this.lightOrDark()) { moveHandle.add_style_class_name("inverted"); } else { @@ -1035,12 +1357,12 @@ class Keyboard extends Dialog { } this.event(event, false) }) - grid.attach(moveHandle, 2 * topBtnWidth, 0, (rowSize - 4 * topBtnWidth), 3) // [insert handwriting 11] + grid.attach(moveHandle, mvBtnStartLeft, 0, (rowSize - mvBtnStartLeft - mvBtnEndRight), 3) // [insert handwriting 11] grid.attach(new St.Widget({ x_expand: true, y_expand: true }), 0, 3, rowSize, 1) } this.keys.forEach(item => { - item.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? (this.settings.get_int("border-spacing-px") + 5) + "px;" : "0;") + "background-size: " + this.settings.get_int("font-size-px") + "px; font-weight: " + (this.settings.get_boolean("font-bold") ? "bold" : "normal") + "; border: " + this.settings.get_int("border-spacing-px") + "px solid transparent;"); + item.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? (this.settings.get_int("border-spacing-px") + 5) + "px" : "0") + "; background-size: " + this.settings.get_int("font-size-px") + "px; font-weight: " + (this.settings.get_boolean("font-bold") ? "bold" : "normal") + "; border: " + this.settings.get_int("border-spacing-px") + "px solid transparent;"); if (this.lightOrDark()) { item.add_style_class_name("inverted"); } else { @@ -1070,17 +1392,26 @@ class Keyboard extends Dialog { item.space_motion_handler = null item.set_scale(1.2, 1.2) item.add_style_pseudo_class("pressed") - let player + let player = global.display.get_sound_player(); if (this.settings.get_boolean("play-sound")) { - player = global.display.get_sound_player(); - player.play_from_theme("dialog-information", "tap", null) + if (this.settings.get_string("sound-file") != "") { + const sound_file = Gio.File.new_for_path(this.settings.get_string("sound-file")) + player.play_from_file(sound_file, "tap", null) + } else { + player.play_from_theme("dialog-information", "tap", null) + } } if (["delete_btn", "backspace_btn", "up_btn", "down_btn", "left_btn", "right_btn"].some(e => item.has_style_class_name(e))) { item.button_pressed = setTimeout(() => { const oldModBtns = this.modBtns item.button_repeat = setInterval(() => { if (this.settings.get_boolean("play-sound")) { - player.play_from_theme("dialog-information", "tap", null) + if (this.settings.get_string("sound-file") != "") { + const sound_file = Gio.File.new_for_path(this.settings.get_string("sound-file")) + player.play_from_file(sound_file, "tap", null) + } else { + player.play_from_theme("dialog-information", "tap", null) + } } this.decideMod(item.char) @@ -1159,6 +1490,7 @@ class Keyboard extends Dialog { } item.key_pressed = false; } + item.clear_actions(); item.connect("button-press-event", () => pressEv("mouse")) item.connect("button-release-event", releaseEv) item.connect("touch-event", () => { @@ -1189,25 +1521,6 @@ class Keyboard extends Dialog { return hsp > 127.5 } releaseAllKeys() { - let instances = []; - - function traverse(obj) { - for (let key in obj) { - if (obj.hasOwnProperty(key)) { - if (key === "code") { - instances.push(obj[key]); - } else if (typeof obj[key] === 'object' && obj[key] !== null) { - traverse(obj[key]); - } - } - } - } - - traverse(keycodes); - instances.forEach(i => { - this.inputDevice.notify_key(Clutter.get_current_event_time(), i, Clutter.KeyState.RELEASED); - }) - this.keys.forEach(item => { item.key_pressed = false; if (item.button_pressed !== null) { @@ -1218,7 +1531,7 @@ class Keyboard extends Dialog { clearInterval(item.button_repeat) item.button_repeat == null } - if (item.space_motion_handler !== null) { + if (item.space_motion_handler !== null && GObject.signal_handler_is_connected(item, item.space_motion_handler)) { item.disconnect(item.space_motion_handler) item.space_motion_handler = null; } @@ -1226,25 +1539,38 @@ class Keyboard extends Dialog { } sendKey(keys) { try { + if (this.keyInProgress) { + return; + } + this.keyInProgress = true; + let event_time = Clutter.get_current_event_time() * 1000; for (var i = 0; i < keys.length; i++) { - this.inputDevice.notify_key(Clutter.get_current_event_time(), keys[i], Clutter.KeyState.PRESSED); + this.inputDevice.notify_key(event_time, keys[i], Clutter.KeyState.PRESSED); } if (this.keyTimeout !== null) { clearTimeout(this.keyTimeout); - this.keyTimeout = null; - } - this.keyTimeout = setTimeout(() => { - for (var j = keys.length - 1; j >= 0; j--) { - this.inputDevice.notify_key(Clutter.get_current_event_time(), keys[j], Clutter.KeyState.RELEASED); + if (typeof this.keyTimeoutFunc === 'function') { + this.keyTimeoutFunc(); } - }, 100); + this.keyTimeout = null; + this.keyTimeoutFunc = null; + } + this.keyTimeoutFunc = () => { + const currKeys = keys + for (var j = currKeys.length - 1; j >= 0; j--) { + this.inputDevice.notify_key(event_time, currKeys[j], Clutter.KeyState.RELEASED); + } + this.keyInProgress = false; + } + this.keyTimeout = setTimeout(this.keyTimeoutFunc, 100); } catch (err) { + this.keyInProgress = false; throw new Error("GJS-OSK: An unknown error occured. Please report this bug to the Issues page (https://github.com/Vishram1123/gjs-osk/issues):\n\n" + err + "\n\nKeys Pressed: " + keys); } } decideMod(i, mBtn) { - if (i.code == 29 || i.code == 56 || i.code == 97 || i.code == 125) { + if (i.code == 29 || i.code == 56 || i.code == 97 || i.code == 125 || i.code == 126) { this.setNormMod(mBtn); } else if (i.code == 100) { this.setAlt(mBtn); @@ -1326,6 +1652,7 @@ class Keyboard extends Dialog { button.remove_style_class_name("selected"); this.modBtns.splice(this.modBtns.indexOf(button), this.modBtns.indexOf(button) + 1); this.inputDevice.notify_key(Clutter.get_current_event_time(), button.char.code, Clutter.KeyState.RELEASED); + this.sendKey([button.char.code]) } else { if (!(button.char.code == 42) && !(button.char.code == 54)) button.add_style_class_name("selected"); diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/keycodes.tar.xz b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/keycodes.tar.xz index a57820a..ce3fc1b 100644 Binary files a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/keycodes.tar.xz and b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/keycodes.tar.xz differ diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/metadata.json b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/metadata.json index 419a6f1..e43b080 100644 --- a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/metadata.json +++ b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/metadata.json @@ -7,9 +7,10 @@ "45", "46", "47", - "48" + "48", + "49" ], "url": "https://github.com/Vishram1123/gjs-osk", "uuid": "gjsosk@vishram1123.com", "version": 100000 -} +} \ No newline at end of file diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/physicalLayouts.json b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/physicalLayouts.json index 368bffe..1f10f32 100644 --- a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/physicalLayouts.json +++ b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/physicalLayouts.json @@ -5,7 +5,8 @@ [{"key":"TAB", "width":1.5}, {"key":"AD01"}, {"key":"AD02"}, {"key":"AD03"}, {"key":"AD04"}, {"key":"AD05"}, {"key":"AD06"}, {"key":"AD07"}, {"key":"AD08"}, {"key":"AD09"}, {"key":"AD10"}, {"key":"AD11"}, {"key":"AD12"}, {"key":"RTRN", "width":1.5, "height":2}, {"key":"DELE"}, {"key":"END"}, {"key":"PGDN"}, {"key":"KP7"}, {"key":"KP8"}, {"key":"KP9"}, {"key":"KPAD", "width":1, "height":2}], [{"key":"CAPS", "width":2}, {"key":"AC01"}, {"key":"AC02"}, {"key":"AC03"}, {"key":"AC04"}, {"key":"AC05"}, {"key":"AC06"}, {"key":"AC07"}, {"key":"AC08"}, {"key":"AC09"}, {"key":"AC10"}, {"key":"AC11"}, {"key":"BKSL", "width":0.5}, {"width":1.5}, {"width":3}, {"key":"KP4"}, {"key":"KP5"}, {"key":"KP6"}, {"width":1}], [{"key":"LFSH", "width":2}, {"key":"LSGT"}, {"key":"AB01"}, {"key":"AB02"}, {"key":"AB03"}, {"key":"AB04"}, {"key":"AB05"}, {"key":"AB06"}, {"key":"AB07"}, {"key":"AB08"}, {"key":"AB09"}, {"key":"AB10"}, {"key":"RTSH", "width":2}, {"width":1}, {"key":"UP"}, {"width":1}, {"key":"KP1"}, {"key":"KP2"}, {"key":"KP3"}, {"key":"KPEN", "height":2}], - [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":10}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, {"key":"DOWN"}, {"key":"RGHT"}, {"key":"KP0", "width":2}, {"key":"KPDL"}, {"width":1}] + [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":10}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, {"key":"DOWN"}, {"key":"RGHT"}, {"key":"KP0", "width":2}, {"key":"KPDL"}, {"width":1}], + { "split": false, "settings": true, "close": true } ], "Full Sized US":[ @@ -14,7 +15,8 @@ [{"key":"TAB", "width":1.5}, {"key":"AD01"}, {"key":"AD02"}, {"key":"AD03"}, {"key":"AD04"}, {"key":"AD05"}, {"key":"AD06"}, {"key":"AD07"}, {"key":"AD08"}, {"key":"AD09"}, {"key":"AD10"}, {"key":"AD11"}, {"key":"AD12"}, {"key":"BKSL", "width":1.5}, {"key":"DELE"}, {"key":"END"}, {"key":"PGDN"}, {"key":"KP7"}, {"key":"KP8"}, {"key":"KP9"}, {"key":"KPAD", "width":1, "height":2}], [{"key":"CAPS", "width":2}, {"key":"AC01"}, {"key":"AC02"}, {"key":"AC03"}, {"key":"AC04"}, {"key":"AC05"}, {"key":"AC06"}, {"key":"AC07"}, {"key":"AC08"}, {"key":"AC09"}, {"key":"AC10"}, {"key":"AC11"}, {"key": "RTRN", "width": 2}, {"width":3}, {"key":"KP4"}, {"key":"KP5"}, {"key":"KP6"}, {"width":1}], [{"key":"LFSH", "width":2}, {"key":"LSGT"}, {"key":"AB01"}, {"key":"AB02"}, {"key":"AB03"}, {"key":"AB04"}, {"key":"AB05"}, {"key":"AB06"}, {"key":"AB07"}, {"key":"AB08"}, {"key":"AB09"}, {"key":"AB10"}, {"key":"RTSH", "width":2}, {"width":1}, {"key":"UP"}, {"width":1}, {"key":"KP1"}, {"key":"KP2"}, {"key":"KP3"}, {"key":"KPEN", "height":2}], - [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":10}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, {"key":"DOWN"}, {"key":"RGHT"}, {"key":"KP0", "width":2}, {"key":"KPDL"}, {"width":1}] + [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":10}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, {"key":"DOWN"}, {"key":"RGHT"}, {"key":"KP0", "width":2}, {"key":"KPDL"}, {"width":1}], + { "split": false, "settings": true, "close": true } ], "Tenkeyless International":[ @@ -23,7 +25,8 @@ [{"key":"TAB", "width":1.5}, {"key":"AD01"}, {"key":"AD02"}, {"key":"AD03"}, {"key":"AD04"}, {"key":"AD05"}, {"key":"AD06"}, {"key":"AD07"}, {"key":"AD08"}, {"key":"AD09"}, {"key":"AD10"}, {"key":"AD11"}, {"key":"AD12"}, {"key":"RTRN", "width":1.5, "height":2}, {"key":"DELE"}, {"key":"END"}, {"key":"PGDN"}], [{"key":"CAPS", "width":2}, {"key":"AC01"}, {"key":"AC02"}, {"key":"AC03"}, {"key":"AC04"}, {"key":"AC05"}, {"key":"AC06"}, {"key":"AC07"}, {"key":"AC08"}, {"key":"AC09"}, {"key":"AC10"}, {"key":"AC11"}, {"key":"BKSL", "width":0.5}, {"width":1.5}, {"width":3}], [{"key":"LFSH", "width":2}, {"key":"LSGT"}, {"key":"AB01"}, {"key":"AB02"}, {"key":"AB03"}, {"key":"AB04"}, {"key":"AB05"}, {"key":"AB06"}, {"key":"AB07"}, {"key":"AB08"}, {"key":"AB09"}, {"key":"AB10"}, {"key":"RTSH", "width":2}, {"width":1}, {"key":"UP"}, {"width":1}], - [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":10}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, {"key":"DOWN"}, {"key":"RGHT"}] + [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":10}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, {"key":"DOWN"}, {"key":"RGHT"}], + { "split": false, "settings": true, "close": true } ], "Tenkeyless US":[ @@ -32,7 +35,8 @@ [{"key":"TAB", "width":1.5}, {"key":"AD01"}, {"key":"AD02"}, {"key":"AD03"}, {"key":"AD04"}, {"key":"AD05"}, {"key":"AD06"}, {"key":"AD07"}, {"key":"AD08"}, {"key":"AD09"}, {"key":"AD10"}, {"key":"AD11"}, {"key":"AD12"}, {"key":"BKSL", "width":1.5}, {"key":"DELE"}, {"key":"END"}, {"key":"PGDN"}], [{"key":"CAPS", "width":2}, {"key":"AC01"}, {"key":"AC02"}, {"key":"AC03"}, {"key":"AC04"}, {"key":"AC05"}, {"key":"AC06"}, {"key":"AC07"}, {"key":"AC08"}, {"key":"AC09"}, {"key":"AC10"}, {"key":"AC11"}, {"key": "RTRN", "width": 2}, {"width":3}], [{"key":"LFSH", "width":2}, {"key":"LSGT"}, {"key":"AB01"}, {"key":"AB02"}, {"key":"AB03"}, {"key":"AB04"}, {"key":"AB05"}, {"key":"AB06"}, {"key":"AB07"}, {"key":"AB08"}, {"key":"AB09"}, {"key":"AB10"}, {"key":"RTSH", "width":2}, {"width":1}, {"key":"UP"}, {"width":1}], - [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":10}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, {"key":"DOWN"}, {"key":"RGHT"}] + [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":10}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, {"key":"DOWN"}, {"key":"RGHT"}], + { "split": false, "settings": true, "close": true } ], "Compact International":[ @@ -41,7 +45,8 @@ [{"key":"TAB", "width":1.5}, {"key":"AD01"}, {"key":"AD02"}, {"key":"AD03"}, {"key":"AD04"}, {"key":"AD05"}, {"key":"AD06"}, {"key":"AD07"}, {"key":"AD08"}, {"key":"AD09"}, {"key":"AD10"}, {"key":"AD11"}, {"key":"AD12"}, {"key":"RTRN", "width":1.5, "height":2}], [{"key":"CAPS", "width":2}, {"key":"AC01"}, {"key":"AC02"}, {"key":"AC03"}, {"key":"AC04"}, {"key":"AC05"}, {"key":"AC06"}, {"key":"AC07"}, {"key":"AC08"}, {"key":"AC09"}, {"key":"AC10"}, {"key":"AC11"}, {"key":"BKSL", "width":0.5}, {"width":1.5}], [{"key":"LFSH", "width":2}, {"key":"LSGT"}, {"key":"AB01"}, {"key":"AB02"}, {"key":"AB03"}, {"key":"AB04"}, {"key":"AB05"}, {"key":"AB06"}, {"key":"AB07"}, {"key":"AB08"}, {"key":"AB09"}, {"key":"AB10"}, {"key":"RTSH", "width": 2}], - [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":7}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, [{"key":"UP", "height": 0.5}, {"key":"DOWN", "height": 0.5}], {"key":"RGHT"}] + [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":7}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, [{"key":"UP", "height": 0.5}, {"key":"DOWN", "height": 0.5}], {"key":"RGHT"}], + { "split": false, "settings": true, "close": true } ], "Compact US":[ @@ -50,7 +55,8 @@ [{"key":"TAB", "width":1.5}, {"key":"AD01"}, {"key":"AD02"}, {"key":"AD03"}, {"key":"AD04"}, {"key":"AD05"}, {"key":"AD06"}, {"key":"AD07"}, {"key":"AD08"}, {"key":"AD09"}, {"key":"AD10"}, {"key":"AD11"}, {"key":"AD12"}, {"key":"BKSL", "width":1.5}], [{"key":"CAPS", "width":2}, {"key":"AC01"}, {"key":"AC02"}, {"key":"AC03"}, {"key":"AC04"}, {"key":"AC05"}, {"key":"AC06"}, {"key":"AC07"}, {"key":"AC08"}, {"key":"AC09"}, {"key":"AC10"}, {"key":"AC11"}, {"key": "RTRN", "width": 2}], [{"key":"LFSH", "width":2}, {"key":"LSGT"}, {"key":"AB01"}, {"key":"AB02"}, {"key":"AB03"}, {"key":"AB04"}, {"key":"AB05"}, {"key":"AB06"}, {"key":"AB07"}, {"key":"AB08"}, {"key":"AB09"}, {"key":"AB10"}, {"key":"RTSH", "width": 2}], - [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":7}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, [{"key":"UP", "height": 0.5}, {"key":"DOWN", "height": 0.5}], {"key":"RGHT"}] + [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":7}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, [{"key":"UP", "height": 0.5}, {"key":"DOWN", "height": 0.5}], {"key":"RGHT"}], + { "split": false, "settings": true, "close": true } ], "Split International":[ @@ -59,7 +65,8 @@ [{"key":"TAB", "width":1.5}, {"key":"AD01"}, {"key":"AD02"}, {"key":"AD03"}, {"key":"AD04"}, {"key":"AD05"}, {"key":"AD06"}, {"split":true}, {"key":"AD07"}, {"key":"AD08"}, {"key":"AD09"}, {"key":"AD10"}, {"key":"AD11"}, {"key":"AD12"}, {"key":"RTRN", "height":2}], [{"key":"CAPS", "width":2}, {"key":"AC01"}, {"key":"AC02"}, {"key":"AC03"}, {"key":"AC04"}, {"key":"AC05"}, {"split":true}, {"key":"AC06"}, {"key":"AC07"}, {"key":"AC08"}, {"key":"AC09"}, {"key":"AC10"}, {"key":"AC11"}, {"key":"BKSL", "width":0.5}, {"width":1}], [{"key":"LFSH", "width":2}, {"key":"LSGT"}, {"key":"AB01"}, {"key":"AB02"}, {"key":"AB03"}, {"key":"AB04"}, {"split":true}, {"key":"AB05"}, {"key":"AB06"}, {"key":"AB07"}, {"key":"AB08"}, {"key":"AB09"}, {"key":"AB10"}, {"key":"RTSH", "width": 1.5}], - [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":4}, {"split":true}, {"key":"SPCE", "width":2.5}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, [{"key":"UP", "height": 0.5}, {"key":"DOWN", "height": 0.5}], {"key":"RGHT"}] + [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":4}, {"split":true}, {"key":"SPCE", "width":2.5}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, [{"key":"UP", "height": 0.5}, {"key":"DOWN", "height": 0.5}], {"key":"RGHT"}], + { "split": true, "settings": true, "close": true } ], "Split US":[ @@ -68,24 +75,27 @@ [{"key":"TAB", "width":1.5}, {"key":"AD01"}, {"key":"AD02"}, {"key":"AD03"}, {"key":"AD04"}, {"key":"AD05"}, {"key":"AD06"}, {"split":true}, {"key":"AD07"}, {"key":"AD08"}, {"key":"AD09"}, {"key":"AD10"}, {"key":"AD11"}, {"key":"AD12"}, {"key":"BKSL"}], [{"key":"CAPS", "width":2}, {"key":"AC01"}, {"key":"AC02"}, {"key":"AC03"}, {"key":"AC04"}, {"key":"AC05"}, {"split":true}, {"key":"AC06"}, {"key":"AC07"}, {"key":"AC08"}, {"key":"AC09"}, {"key":"AC10"}, {"key":"AC11"}, {"key": "RTRN", "width": 1.5}], [{"key":"LFSH", "width":2}, {"key":"LSGT"}, {"key":"AB01"}, {"key":"AB02"}, {"key":"AB03"}, {"key":"AB04"}, {"split":true}, {"key":"AB05"}, {"key":"AB06"}, {"key":"AB07"}, {"key":"AB08"}, {"key":"AB09"}, {"key":"AB10"}, {"key":"RTSH", "width": 1.5}], - [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":4}, {"split":true}, {"key":"SPCE", "width":2.5}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, [{"key":"UP", "height": 0.5}, {"key":"DOWN", "height": 0.5}], {"key":"RGHT"}] + [{"key":"LCTL"}, {"key":"LWIN"}, {"key":"LALT"}, {"key":"SPCE", "width":4}, {"split":true}, {"key":"SPCE", "width":2.5}, {"key":"RALT"}, {"key":"RCTL"}, {"key":"LEFT"}, [{"key":"UP", "height": 0.5}, {"key":"DOWN", "height": 0.5}], {"key":"RGHT"}], + { "split": true, "settings": true, "close": true } ], "Mobile": [ [{"key":"TLDE", "width": 2}, {"key":"TAB", "width": 2}, {"key":"ESC", "width": 2}, {"key":"FK01", "width": 2}, {"key":"FK02", "width": 2}, {"key":"FK03", "width": 2}, {"key":"FK04", "width": 2}, {"key":"FK05", "width": 2}, {"key":"FK06", "width": 2}, {"key":"FK07", "width": 2}, {"key":"FK08", "width": 2}, {"key":"FK09", "width": 2}, {"key":"FK10", "width": 2}, {"key":"FK11", "width": 2}, {"key":"FK12", "width": 2}, {"key":"PRSC", "width": 2}, {"key":"DELE", "width": 2}, {"key":"BKSL", "width": 2}], [{"key":"AE11", "width": 3}, {"key":"AE01", "width": 3}, {"key":"AE02", "width": 3}, {"key":"AE03", "width": 3}, {"key":"AE04", "width": 3}, {"key":"AE05", "width": 3}, {"key":"AE06", "width": 3}, {"key":"AE07", "width": 3}, {"key":"AE08", "width": 3}, {"key":"AE09", "width": 3}, {"key":"AE10", "width": 3}, {"key":"AE12", "width": 3}], [{"key":"AD11", "width": 3}, {"key":"AD01", "width": 3}, {"key":"AD02", "width": 3}, {"key":"AD03", "width": 3}, {"key":"AD04", "width": 3}, {"key":"AD05", "width": 3}, {"key":"AD06", "width": 3}, {"key":"AD07", "width": 3}, {"key":"AD08", "width": 3}, {"key":"AD09", "width": 3}, {"key":"AD10", "width": 3}, {"key":"AD12", "width": 3}], - [{"width": 1.5}, {"key":"AC10", "width": 3}, {"key":"AC01", "width": 3}, {"key":"AC02", "width": 3}, {"key":"AC03", "width": 3}, {"key":"AC04", "width": 3}, {"key":"AC05", "width": 3}, {"key":"AC06", "width": 3}, {"key":"AC07", "width": 3}, {"key":"AC08", "width": 3}, {"key":"AC09", "width": 3}, {"key":"AC11", "width": 3}], + [{"width": 1.5}, {"key":"AC11", "width": 3}, {"key":"AC01", "width": 3}, {"key":"AC02", "width": 3}, {"key":"AC03", "width": 3}, {"key":"AC04", "width": 3}, {"key":"AC05", "width": 3}, {"key":"AC06", "width": 3}, {"key":"AC07", "width": 3}, {"key":"AC08", "width": 3}, {"key":"AC09", "width": 3}, {"key":"AC10", "width": 3}], [{"key":"LFSH", "width": 2.5}, {"key":"LSGT", "width": 2}, {"key":"AB08", "width": 3}, {"key":"AB01", "width": 3}, {"key":"AB02", "width": 3}, {"key":"AB03", "width": 3}, {"key":"AB04", "width": 3}, {"key":"AB05", "width": 3}, {"key":"AB06", "width": 3}, {"key":"AB07", "width": 3}, {"key":"AB09", "width": 3}, {"key":"AB10", "width": 2}, {"key": "BKSP", "width": 2.5}], - [{"key":"CAPS", "width": 2.5}, {"key":"LCTL", "width": 2.5}, {"key":"LWIN", "width": 2.5}, {"key":"LALT", "width": 2.5}, {"key":"SPCE", "width":10}, {"key":"RALT", "width": 2.5}, {"key":"RCTL", "width": 2.5}, {"key":"LEFT", "width": 2.5}, [{"key":"UP", "width": 2.5, "height": 0.5}, {"key":"DOWN", "width": 2.5, "height": 0.5}], {"key":"RGHT", "width": 2.5}, {"key":"RTRN", "width": 3.5}] + [{"key":"CAPS", "width": 2.5}, {"key":"LCTL", "width": 2.5}, {"key":"LWIN", "width": 2.5}, {"key":"LALT", "width": 2.5}, {"key":"SPCE", "width":10}, {"key":"RALT", "width": 2.5}, {"key":"RCTL", "width": 2.5}, {"key":"LEFT", "width": 2.5}, [{"key":"UP", "width": 2.5, "height": 0.5}, {"key":"DOWN", "width": 2.5, "height": 0.5}], {"key":"RGHT", "width": 2.5}, {"key":"RTRN", "width": 3.5}], + { "split": false, "settings": true, "close": true } ], "Split Mobile": [ [{"key":"TLDE", "width": 2}, {"key":"TAB", "width": 2}, {"key":"ESC", "width": 2}, {"key":"FK01", "width": 2}, {"key":"FK02", "width": 2}, {"key":"FK03", "width": 2}, {"key":"FK04", "width": 2}, {"key":"FK05", "width": 2}, {"key":"FK06", "width": 2}, {"split":true}, {"key":"FK07", "width": 2}, {"key":"FK08", "width": 2}, {"key":"FK09", "width": 2}, {"key":"FK10", "width": 2}, {"key":"FK11", "width": 2}, {"key":"FK12", "width": 2}, {"key":"PRSC", "width": 2}, {"key":"DELE", "width": 3}, {"key":"BKSL", "width": 2.5}], [{"key":"AE11", "width": 3}, {"key":"AE01", "width": 3}, {"key":"AE02", "width": 3}, {"key":"AE03", "width": 3}, {"key":"AE04", "width": 3}, {"key":"AE05", "width": 3}, {"split":true}, {"key":"AE06", "width": 3}, {"key":"AE07", "width": 3}, {"key":"AE08", "width": 3}, {"key":"AE09", "width": 3}, {"key":"AE10", "width": 3}, {"key":"AE12", "width": 4.5}], [{"key":"AD11", "width": 3}, {"key":"AD01", "width": 3}, {"key":"AD02", "width": 3}, {"key":"AD03", "width": 3}, {"key":"AD04", "width": 3}, {"key":"AD05", "width": 3}, {"split":true}, {"key":"AD06", "width": 3}, {"key":"AD07", "width": 3}, {"key":"AD08", "width": 3}, {"key":"AD09", "width": 3}, {"key":"AD10", "width": 3}, {"key":"AD12", "width": 4.5}], - [{"key":"AC10", "width": 3}, {"key":"AC01", "width": 3}, {"key":"AC02", "width": 3}, {"key":"AC03", "width": 3}, {"key":"AC04", "width": 3}, {"key":"AC05", "width": 3}, {"split":true}, {"key":"AC05", "width": 3}, {"key":"AC06", "width": 3}, {"key":"AC07", "width": 3}, {"key":"AC08", "width": 3}, {"key":"AC09", "width": 3}, {"key":"AC11", "width": 4.5}], + [{"key":"AC11", "width": 3}, {"key":"AC01", "width": 3}, {"key":"AC02", "width": 3}, {"key":"AC03", "width": 3}, {"key":"AC04", "width": 3}, {"key":"AC05", "width": 3}, {"split":true}, {"key":"AC05", "width": 3}, {"key":"AC06", "width": 3}, {"key":"AC07", "width": 3}, {"key":"AC08", "width": 3}, {"key":"AC09", "width": 3}, {"key":"AC10", "width": 3}, {"width": 1.5}], [{"key":"LFSH", "width": 2}, {"key":"LSGT", "width": 2}, {"key":"AB08", "width": 2}, {"key":"AB01", "width": 3}, {"key":"AB02", "width": 3}, {"key":"AB03", "width": 3}, {"key":"AB04", "width": 3}, {"split":true}, {"key":"AB04", "width": 3}, {"key":"AB05", "width": 3}, {"key":"AB06", "width": 3}, {"key":"AB07", "width": 3}, {"key":"AB09", "width": 3}, {"key":"AB10", "width": 2}, {"key": "BKSP", "width": 2.5}], - [{"key":"CAPS", "width": 2.5}, {"key":"LCTL", "width": 2.5}, {"key":"LWIN", "width": 2.5}, {"key":"LALT", "width": 2.5}, {"key":"SPCE", "width":8}, {"split":true}, {"key":"SPCE", "width":4}, {"key":"RALT", "width": 2.5}, {"key":"RCTL", "width": 2.5}, {"key":"LEFT", "width": 2.5}, [{"key":"UP", "width": 2.5, "height": 0.5}, {"key":"DOWN", "width": 2.5, "height": 0.5}], {"key":"RGHT", "width": 2.5}, {"key":"RTRN", "width": 3}] + [{"key":"CAPS", "width": 2.5}, {"key":"LCTL", "width": 2.5}, {"key":"LWIN", "width": 2.5}, {"key":"LALT", "width": 2.5}, {"key":"SPCE", "width":8}, {"split":true}, {"key":"SPCE", "width":4}, {"key":"RALT", "width": 2.5}, {"key":"RCTL", "width": 2.5}, {"key":"LEFT", "width": 2.5}, [{"key":"UP", "width": 2.5, "height": 0.5}, {"key":"DOWN", "width": 2.5, "height": 0.5}], {"key":"RGHT", "width": 2.5}, {"key":"RTRN", "width": 3}], + { "split": true, "settings": true, "close": true } ] } \ No newline at end of file diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/prefs.js b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/prefs.js index d9a83ac..8306afb 100644 --- a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/prefs.js +++ b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/prefs.js @@ -44,6 +44,7 @@ export default class GjsOskPreferences extends ExtensionPreferences { } let layoutList = Object.keys(layouts); + layoutList.push("Custom") let layoutLandscapeDrop = Gtk.DropDown.new_from_strings(layoutList); layoutLandscapeDrop.valign = Gtk.Align.CENTER; layoutLandscapeDrop.selected = settings.get_int("layout-landscape"); @@ -63,6 +64,40 @@ export default class GjsOskPreferences extends ExtensionPreferences { layoutPortraitRow.add_suffix(layoutPortraitDrop); layoutPortraitRow.activatable_widget = layoutPortraitDrop; + const customLayoutRow = new Adw.EntryRow({ + title: _('Custom Layout') + }); + layoutRow.add_row(customLayoutRow); + + customLayoutRow.set_text(settings.get_string("custom-layout")); + customLayoutRow.set_show_apply_button(true); + + const createKeyboardLayoutRow = new Adw.ActionRow({ + title: _('Create/edit a custom keyboard layout') + }) + layoutRow.add_row(createKeyboardLayoutRow) + + const layoutLink = new Gtk.LinkButton({ + label: 'Keyboard Layout Editor', + uri: 'https://vishram1123.github.io/gjs-osk' + }) + + createKeyboardLayoutRow.add_suffix(layoutLink) + createKeyboardLayoutRow.activatable_widget = layoutLink + + const disableEdgeSwipeRow = new Adw.ActionRow({ + title: _('Disable Edge Swipe') + }); + behaviorGroup.add(disableEdgeSwipeRow); + + const disableEdgeSwipeDT = new Gtk.Switch({ + active: settings.get_boolean('disable-edge-swipe'), + valign: Gtk.Align.CENTER, + }); + + disableEdgeSwipeRow.add_suffix(disableEdgeSwipeDT); + disableEdgeSwipeRow.activatable_widget = disableEdgeSwipeDT; + const enableDragRow = new Adw.ActionRow({ title: _('Enable Dragging') }); @@ -217,18 +252,73 @@ export default class GjsOskPreferences extends ExtensionPreferences { defaultPosition.add_suffix(snapDrop); defaultPosition.activatable_widget = snapDrop; - const soundPlayRow = new Adw.ActionRow({ - title: _('Play sound') + const soundPlayRow = new Adw.ExpanderRow({ + title: _('Play sound'), + show_enable_switch: true }); behaviorGroup.add(soundPlayRow); + soundPlayRow.enable_expansion = settings.get_boolean('play-sound') - const soundPlayDT = new Gtk.Switch({ - active: settings.get_boolean('play-sound'), + const fileRow = new Adw.ActionRow({ + title: _('Sound file'), + subtitle: settings.get_string('sound-file') || _('No file selected'), + activatable: false, // prevent clicking the whole row + }); + + const fileButton = new Gtk.Button({ + label: settings.get_string('sound-file') ? _('Clear') : _('Choose'), valign: Gtk.Align.CENTER, }); - soundPlayRow.add_suffix(soundPlayDT); - soundPlayRow.activatable_widget = soundPlayDT; + fileRow.add_suffix(fileButton); + fileRow.activatable_widget = fileButton; + + fileButton.connect('clicked', () => { + const currentPath = settings.get_string('sound-file'); + if (currentPath) { + settings.set_string('sound-file', ''); + fileRow.subtitle = _('No file selected'); + fileButton.label = _('Choose'); + } else { + const fileChooser = new Gtk.FileChooserNative({ + title: _('Select OGG File'), + transient_for: window, + action: Gtk.FileChooserAction.OPEN, + accept_label: _('Open'), + cancel_label: _('Cancel'), + }); + + const filter = new Gtk.FileFilter(); + filter.add_mime_type('audio/ogg'); + filter.set_name(_('OGG files')); + fileChooser.add_filter(filter); + + fileChooser.connect('response', (dlg, response) => { + if (response === Gtk.ResponseType.ACCEPT) { + const file = dlg.get_file(); + if (file) { + const path = file.get_path(); + settings.set_string('sound-file', path); + fileRow.subtitle = path; + fileButton.label = _('Clear'); + + // Optionally play the file immediately + try { + let player = global.display.get_sound_player(); + player.play_from_file(path, null); + } catch (e) { + logError(e, 'Failed to play sound file'); + } + } + } + dlg.destroy(); + }); + + fileChooser.show(); + } + }); + + soundPlayRow.add_row(fileRow); const appearanceGroup = new Adw.PreferencesGroup({ title: _("Appearance") @@ -405,7 +495,7 @@ export default class GjsOskPreferences extends ExtensionPreferences { context.add_class("title-1"); let another_label = new Gtk.Label({ - label: _("Autorelease ") + `776e35f` + label: _("Autorelease ") + `3f0ef02` }); let links_pref_group = new Adw.PreferencesGroup(); @@ -447,6 +537,10 @@ export default class GjsOskPreferences extends ExtensionPreferences { settings.bind("layout-landscape", layoutLandscapeDrop, "selected", 0); settings.bind("layout-portrait", layoutPortraitDrop, "selected", 0); + customLayoutRow.connect("apply", () => { + settings.set_string("custom-layout", customLayoutRow.get_text()); + }); + settings.bind("disable-edge-swipe", disableEdgeSwipeDT, "active", 0); settings.bind("enable-drag", dragEnableDT, "active", 0); settings.bind("enable-tap-gesture", dragOpt, "selected", 0); settings.bind("indicator-enabled", indEnabled, "active", 0); @@ -472,7 +566,7 @@ export default class GjsOskPreferences extends ExtensionPreferences { settings.bind("outer-spacing-px", numChanger_outer, "value", 0); settings.bind("snap-spacing-px", numChanger_snap, "value", 0) settings.bind("round-key-corners", roundKeyCDT, "active", 0); - settings.bind("play-sound", soundPlayDT, "active", 0); + settings.bind("play-sound", soundPlayRow, "enable-expansion", 0); settings.bind("show-icons", showIconDT, "active", 0) settings.bind("default-snap", snapDrop, "selected", 0); monitorDrop.connect("notify::selected", () => { @@ -492,6 +586,8 @@ export default class GjsOskPreferences extends ExtensionPreferences { window.connect("close-request", () => { settings.set_int("layout-landscape", layoutLandscapeDrop.selected); settings.set_int("layout-portrait", layoutPortraitDrop.selected); + settings.set_string("custom-layout", customLayoutRow.get_text()); + settings.set_boolean("disable-edge-swipe", disableEdgeSwipeDT.active); settings.set_boolean("enable-drag", dragEnableDT.active); settings.set_int("enable-tap-gesture", dragOpt.selected); settings.set_boolean("indicator-enabled", indEnabled.active); @@ -513,7 +609,7 @@ export default class GjsOskPreferences extends ExtensionPreferences { settings.set_int("outer-spacing-px", numChanger_outer.value); settings.set_int("snap-spacing-px", numChanger_snap.value) settings.set_boolean("round-key-corners", roundKeyCDT.active); - settings.set_boolean("play-sound", soundPlayDT.active); + settings.set_boolean("play-sound", soundPlayRow.enable_expansion); settings.set_boolean("show-icons", showIconDT.active) settings.set_int("default-snap", snapDrop.selected); currentMonitorMap[monitors.length + ""] = monitors.map(m => { return m.get_connector() })[monitorDrop.selected]; diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/schemas/gschemas.compiled b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/schemas/gschemas.compiled index 6b8f5b6..07062d6 100644 Binary files a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/schemas/gschemas.compiled and b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/schemas/gschemas.compiled differ diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/schemas/org.gnome.shell.extensions.gjsosk.gschema.xml b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/schemas/org.gnome.shell.extensions.gjsosk.gschema.xml index d2ef8de..ab5beda 100644 --- a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/schemas/org.gnome.shell.extensions.gjsosk.gschema.xml +++ b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/schemas/org.gnome.shell.extensions.gjsosk.gschema.xml @@ -7,6 +7,9 @@ 0 + + "" + 100 @@ -19,6 +22,9 @@ 30 + + false + true @@ -82,8 +88,11 @@ true + + "" + - true + true @@ -92,5 +101,8 @@ false + + false + diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/stylesheet.css b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/stylesheet.css index f4e201c..60a1203 100644 --- a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/stylesheet.css +++ b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/stylesheet.css @@ -28,7 +28,6 @@ box-sizing: border-box; background-image: url(ui/icons/hicolor/scalable/actions/transparent.svg); background-repeat: no-repeat; - background-position: center; } .dr-b { @@ -55,7 +54,6 @@ .down_btn, .right_btn { background-repeat: no-repeat; - background-position: center; } .close_btn.regular, @@ -158,6 +156,16 @@ background-image: url(ui/icons/hicolor/scalable/actions/alt-dark.svg); } +.menu_btn.regular, +.menu_btn.selected.inverted { + background-image: url(ui/icons/hicolor/scalable/actions/menu.svg); +} + +.menu_btn.inverted, +.menu_btn.selected.regular { + background-image: url(ui/icons/hicolor/scalable/actions/menu-dark.svg); +} + .space_btn.regular, .space_btn.selected.inverted { background-image: url(ui/icons/hicolor/scalable/actions/space.svg); diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/ui/icons/hicolor/scalable/actions/menu-dark.svg b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/ui/icons/hicolor/scalable/actions/menu-dark.svg new file mode 100644 index 0000000..7d37fa2 --- /dev/null +++ b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/ui/icons/hicolor/scalable/actions/menu-dark.svg @@ -0,0 +1,60 @@ + + + + + + + + + + diff --git a/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/ui/icons/hicolor/scalable/actions/menu.svg b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/ui/icons/hicolor/scalable/actions/menu.svg new file mode 100644 index 0000000..3b7f08f --- /dev/null +++ b/gnome/.local/share/gnome-shell/extensions/gjsosk@vishram1123.com/ui/icons/hicolor/scalable/actions/menu.svg @@ -0,0 +1,61 @@ + + + + + + + + + +