From c09ac976ea6e1bb745e456d91e290ebfd44cfb12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Hedin=20Br=C3=B8nner?= Date: Sat, 30 Mar 2019 16:28:20 +0100 Subject: [PATCH] navigator: Rewrite the keybind handler Using a SwitcherPopup was just tiresome. --- navigator.js | 117 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 44 deletions(-) diff --git a/navigator.js b/navigator.js index cfd2a24..247ac39 100644 --- a/navigator.js +++ b/navigator.js @@ -30,35 +30,35 @@ var display = global.display; var scale = 0.9; var navigating = false; -var PreviewedWindowNavigator = utils.registerClass( -class PreviewedWindowNavigator extends SwitcherPopup.SwitcherPopup { - _init() { - // Do the absolute minimal here, as `parent.show` is buggy and can - // return early making cleanup hard. We do most initialization in - // `_initialSelection` instead. +/** + Handle catching keyevents and dispatching actions - // HACK: workaround to enable moving from empty workspace. See check in - // SwitcherPopup.show - super._init([1]); - if (!('actor' in this)) { // 3.30 compat - this.actor = this; + Adapted from SwitcherPopup + */ +var ActionDispatcher = class { + constructor() { + this.actor = new Clutter.Actor(); + Main.uiGroup.add_actor(this.actor); + if (!Main.pushModal(this.actor)) { + // Probably someone else has a pointer grab, try again with keyboard only + if (!Main.pushModal(this.actor, { + options: Meta.ModalOptions.POINTER_ALREADY_GRABBED + })) throw new Error('Could not grap a modal');; } - this._switcherList = new SwitcherPopup.SwitcherList(); - if (!('actor' in this._switcherList)) { - this._switcherList.actor = this._switcherList; - } - debug('#preview', 'init', this._switcherList); + + this.actor.connect('key-press-event', this._keyPressEvent.bind(this)); + this.actor.connect('key-release-event', this._keyReleaseEvent.bind(this)); + } - _initialSelection(backward, actionName) { - debug('#preview', '_initialSelection'); + show(backward, binding, mask) { + this._modifierMask = SwitcherPopup.primaryModifier(mask); this.navigator = getNavigator(); - this._switcherList.actor.hide(); - let actionId = Keybindings.idOf(actionName); + let actionId = Keybindings.idOf(binding); if(actionId === Meta.KeyBindingAction.NONE) { try { // Check for built-in actions - actionId = Meta.prefs_get_keybinding_action(actionName); + actionId = Meta.prefs_get_keybinding_action(binding); } catch(e) { debug("Couldn't resolve action name"); return; @@ -68,6 +68,52 @@ class PreviewedWindowNavigator extends SwitcherPopup.SwitcherPopup { this._doAction(actionId); } + _keyPressEvent(actor, event) { + let keysym = event.get_key_symbol(); + let action = global.display.get_keybinding_action(event.get_key_code(), event.get_state()); + + if (this._keyPressHandler(keysym, action) != Clutter.EVENT_PROPAGATE) + return Clutter.EVENT_STOP; + + // Popping the modal on keypress doesn't work properly, as the release + // event will leak to the active window. To work around this we initate + // visual destruction on key-press and signal to the release handler + // that we should destroy the dispactcher too + // https://github.com/paperwm/PaperWM/issues/70 + if (keysym == Clutter.Escape || keysym == Clutter.Tab) { + this.navigator.destroy(); + this._destroy = true; + } + + return Clutter.EVENT_STOP; + } + + _keyReleaseEvent(actor, event) { + let keysym = event.get_key_symbol(); + if (this._destroy) { + this.destroy(); + } + + if (this._modifierMask) { + let [x, y, mods] = global.get_pointer(); + let state = mods & this._modifierMask; + + if (state == 0) + this._finish(event.get_time()); + } + + return Clutter.EVENT_STOP; + } + + + _keyPressHandler(keysym, action) { + if (keysym !== Clutter.KEY_Escape && this._doAction(action)) { + return Clutter.EVENT_STOP; + } else { + return Clutter.EVENT_PROPAGATE; + } + } + _doAction(mutterActionId) { let action = Keybindings.byId(mutterActionId); @@ -91,35 +137,18 @@ class PreviewedWindowNavigator extends SwitcherPopup.SwitcherPopup { return false; } - _keyPressHandler(keysym, action) { - if (keysym !== Clutter.KEY_Escape && this._doAction(action)) { - return Clutter.EVENT_STOP; - } else { - return Clutter.EVENT_PROPAGATE; - } - } - _finish(timestamp) { debug('#preview', 'finish'); this.navigator.accept(); - super._finish(timestamp); - } - - _itemEnteredHandler() { - // The item-enter (mouse hover) event is triggered even after a item is - // accepted. This can cause _select to run on the item below the pointer - // ensuring the wrong window. - if(!this.was_accepted) { - super._itemEnteredHandler.apply(this, arguments); - } + this.destroy(); } destroy() { - this.actor.hide(); // Prevents finalized crap - super.destroy(); - this.navigator.destroy(); + Main.popModal(this.actor); + this.actor.destroy(); + !this._destroy && this.navigator.destroy(); } -}); +} var navigator; var Navigator = class Navigator { @@ -258,6 +287,6 @@ function getNavigator() { } function preview_navigate(meta_window, space, {display, screen, binding}) { - let tabPopup = new PreviewedWindowNavigator(); + let tabPopup = new ActionDispatcher(); tabPopup.show(binding.is_reversed(), binding.get_name(), binding.get_mask()); }