/** * © gnome-shell-extension-tiling-assistant * Source: https://git.launchpad.net/ubuntu/+source/gnome-shell-extension-tiling-assistant/tree/tiling-assistant@leleat-on-github/src/prefs/shortcutListener.js * * This code is partially reused from Ubuntu tiling assistant extension (GPL-2.0). * * enableArrowBinding and disableArrowBinding are new methods. * Other methods are modified for our use case. */ import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import Meta from 'gi://Meta'; import Shell from 'gi://Shell'; import GLib from 'gi://GLib'; import {Shortcut} from '../common.js'; import {Direction} from '../tileWindowManager.js'; const MAX_RESIZE_GAP = 50; const MIN_RESIZE_GAP = 10; /** * Class to handle the keyboard shortcuts (on the extension side) except the * ones related to the Layouts. For those, see layoutsManager.js. */ export default class KeybindingHandler { _keyBindings; _windowManager; _extensionObject = null; _settings; _waitingAction; _enableArrow = false; _prevArrowTyped; constructor(windowManager, extension) { this._extensionObject = extension; this._settings = extension.getSettings(); this._windowManager = windowManager; this._prevArrowTyped = [Direction.North, 0]; this._keyBindings = Shortcut.getShortcuts(); this._keyBindings.forEach(key => { Main.wm.addKeybinding(key, this._settings, Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, Shell.ActionMode.NORMAL, this._onCustomKeybindingPressed.bind(this, key)); }); } destroy() { this._keyBindings.forEach(key => Main.wm.removeKeybinding(key)); this.disableArrowBinding(); } enableArrowBinding() { let bindings = ['arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'key-escape']; bindings.forEach(key => { Main.wm.addKeybinding(key, this._settings, Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, Shell.ActionMode.NORMAL, this._onCustomKeybindingPressed.bind(this, key)); }); } disableArrowBinding() { let bindings = ['arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'key-escape']; bindings.forEach(key => Main.wm.removeKeybinding(key)); } compute_gap(direction) { const t = GLib.get_real_time(); const dir = this._prevArrowTyped[0]; const time = Math.min(Math.max(t - this._prevArrowTyped[1], 150000), 300000) - 150000; this._prevArrowTyped = [direction, t]; let gap = dir === direction ? MAX_RESIZE_GAP - ((time * (MAX_RESIZE_GAP - MIN_RESIZE_GAP) / 150000)) : MIN_RESIZE_GAP; return gap; } /** * @param {string} shortcutName */ _onCustomKeybindingPressed(shortcutName) { switch (shortcutName) { case 'keybinding-close': global.display.get_focus_window().delete(global.get_current_time()); break; case 'keybinding-open-settings': this._extensionObject?.openPreferences(); break; case 'keybinding-rotation': this._windowManager.rotateWindow(global.display.get_focus_window()); break; case 'keybinding-maximize': this._windowManager.maximizeTile(global.display.get_focus_window()); break; case 'keybinding-search': this._windowManager.createSearchBar(); break; case 'keybinding-refresh': // Unused this._windowManager.refresh(); break; case 'keybinding-move-left': this._windowManager.moveTile(Direction.West); break; case 'keybinding-move-right': this._windowManager.moveTile(Direction.East); break; case 'keybinding-move-top': this._windowManager.moveTile(Direction.North); break; case 'keybinding-move-bottom': this._windowManager.moveTile(Direction.South); break; case 'keybinding-move': this._waitingAction = shortcutName; this.enableArrowBinding(); break; case 'keybinding-resize': this._waitingAction = shortcutName; this.enableArrowBinding(); break; case 'keybinding-resize-left': this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_W); break; case 'keybinding-resize-right': this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_E); break; case 'keybinding-resize-top': this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_N); break; case 'keybinding-resize-bottom': this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_S); break; case 'keybinding-focus': this._waitingAction = shortcutName; this.enableArrowBinding(); break; case 'keybinding-focus-left': this._windowManager.changeFocus(Direction.West); break; case 'keybinding-focus-right': this._windowManager.changeFocus(Direction.East); break; case 'keybinding-focus-top': this._windowManager.changeFocus(Direction.North); break; case 'keybinding-focus-bottom': this._windowManager.changeFocus(Direction.South); break; case 'keybinding-next-workspace': this._windowManager.moveToWorkspace(true); break; case 'keybinding-previous-workspace': this._windowManager.moveToWorkspace(false); break; case 'keybinding-next-monitor': this._windowManager.moveToNextMonitor(); break; case 'arrow-up': if (this._waitingAction === 'keybinding-move') { this._windowManager.moveTile(Direction.North); this.disableArrowBinding(); } else if (this._waitingAction === 'keybinding-resize') { this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_N, this.compute_gap(Direction.North)); } else if (this._waitingAction === 'keybinding-focus') { this._windowManager.changeFocus(Direction.North); } break; case 'arrow-down': if (this._waitingAction === 'keybinding-move') { this._windowManager.moveTile(Direction.South); this.disableArrowBinding(); } else if (this._waitingAction === 'keybinding-resize') { this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_S, this.compute_gap(Direction.South)); } else if (this._waitingAction === 'keybinding-focus') { this._windowManager.changeFocus(Direction.South); } break; case 'arrow-left': if (this._waitingAction === 'keybinding-move') { this._windowManager.moveTile(Direction.West); this.disableArrowBinding(); } else if (this._waitingAction === 'keybinding-resize') { this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_W, this.compute_gap(Direction.West)); } else if (this._waitingAction === 'keybinding-focus') { this._windowManager.changeFocus(Direction.West); } break; case 'arrow-right': if (this._waitingAction === 'keybinding-move') { this._windowManager.moveTile(Direction.East); this.disableArrowBinding(); } else if (this._waitingAction === 'keybinding-resize') { this._windowManager.resizeFocusedWindow(Meta.GrabOp.RESIZING_E, this.compute_gap(Direction.East)); } else if (this._waitingAction === 'keybinding-focus') { this._windowManager.changeFocus(Direction.East); } break; case 'key-escape': this.disableArrowBinding(); break; default: break; } } }