diff --git a/js/ui/grabHelper.js b/js/ui/grabHelper.js index 690ba6eee..d69a5345e 100644 --- a/js/ui/grabHelper.js +++ b/js/ui/grabHelper.js @@ -32,13 +32,9 @@ const GrabHelper = new Lang.Class({ this._actors = []; this._capturedEventId = 0; - this._keyFocusNotifyId = 0; - this._focusWindowChangedId = 0; this._ignoreRelease = false; - this._isUngrabbingCount = 0; this._modalCount = 0; - this._grabFocusCount = 0; }, // addActor: @@ -149,7 +145,6 @@ const GrabHelper = new Lang.Class({ grab: function(params) { params = Params.parse(params, { actor: null, modal: false, - grabFocus: false, focus: null, onUngrab: null }); @@ -165,19 +160,16 @@ const GrabHelper = new Lang.Class({ if (params.modal && !this._takeModalGrab()) return false; - if (params.grabFocus && !this._takeFocusGrab(hadFocus)) - return false; - this._grabStack.push(params); if (params.focus) { params.focus.grab_key_focus(); - } else if (newFocus && (hadFocus || params.grabFocus)) { + } else if (newFocus && hadFocus) { if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false)) newFocus.grab_key_focus(); } - if ((params.grabFocus || params.modal) && !this._capturedEventId) + if (params.modal && !this._capturedEventId) this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); return true; @@ -203,52 +195,6 @@ const GrabHelper = new Lang.Class({ global.sync_pointer(); }, - _takeFocusGrab: function(hadFocus) { - let firstGrab = (this._grabFocusCount == 0); - if (firstGrab) { - let metaDisplay = global.screen.get_display(); - - this._grabbedFromKeynav = hadFocus; - this._preGrabInputMode = global.stage_input_mode; - - if (this._preGrabInputMode == Shell.StageInputMode.NORMAL) - global.set_stage_input_mode(Shell.StageInputMode.FOCUSED); - - this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged)); - this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged)); - } - - this._grabFocusCount++; - return true; - }, - - _releaseFocusGrab: function() { - this._grabFocusCount--; - if (this._grabFocusCount > 0) - return; - - if (this._keyFocusNotifyId > 0) { - global.stage.disconnect(this._keyFocusNotifyId); - this._keyFocusNotifyId = 0; - } - - if (this._focusWindowChangedId > 0) { - let metaDisplay = global.screen.get_display(); - metaDisplay.disconnect(this._focusWindowChangedId); - this._focusWindowChangedId = 0; - } - - let prePopInputMode = global.stage_input_mode; - - if (this._grabbedFromKeynav) { - if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED && - prePopInputMode != Shell.StageInputMode.FULLSCREEN) - global.set_stage_input_mode(Shell.StageInputMode.FOCUSED); - } - - global.screen.focus_default_window(global.display.get_current_time_roundtrip()); - }, - // ignoreRelease: // // Make sure that the next button release event evaluated by the @@ -278,14 +224,6 @@ const GrabHelper = new Lang.Class({ if (grabStackIndex < 0) return; - // We may get key focus changes when calling onUngrab, which - // would cause an extra ungrab() on the next actor in the - // stack, which is wrong. Ignore key focus changes during the - // ungrab, and restore the saved key focus ourselves afterwards. - // We use a count as ungrab() may be re-entrant, as onUngrab() - // may ungrab additional actors. - this._isUngrabbingCount++; - let focus = global.stage.key_focus; let hadFocus = focus && this._isWithinGrabbedActor(focus); @@ -302,9 +240,6 @@ const GrabHelper = new Lang.Class({ if (poppedGrab.modal) this._releaseModalGrab(); - - if (poppedGrab.grabFocus) - this._releaseFocusGrab(); } if (!this.grabbed && this._capturedEventId > 0) { @@ -319,8 +254,6 @@ const GrabHelper = new Lang.Class({ if (poppedGrab.savedFocus) poppedGrab.savedFocus.grab_key_focus(); } - - this._isUngrabbingCount--; }, _onCapturedEvent: function(actor, event) { @@ -341,9 +274,6 @@ const GrabHelper = new Lang.Class({ return true; } - if (!button && this._modalCount == 0) - return false; - if (this._isWithinGrabbedActor(event.get_source())) return false; @@ -360,21 +290,6 @@ const GrabHelper = new Lang.Class({ return true; } - return this._modalCount > 0; + return true; }, - - _onKeyFocusChanged: function() { - if (this._isUngrabbingCount > 0) - return; - - let focus = global.stage.key_focus; - if (!focus || !this._isWithinGrabbedActor(focus)) - this.ungrab({ isUser: true }); - }, - - _focusWindowChanged: function() { - let metaDisplay = global.screen.get_display(); - if (metaDisplay.focus_window != null) - this.ungrab({ isUser: true }); - } }); diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index afbcf269a..3bd15c3d1 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -97,6 +97,65 @@ function _fixMarkup(text, allowMarkup) { return GLib.markup_escape_text(text, -1); } +const FocusGrabber = new Lang.Class({ + Name: 'FocusGrabber', + + _init: function(actor) { + this._actor = actor; + this._prevKeyFocusActor = null; + this._focusActorChangedId = 0; + this._focused = false; + }, + + grabFocus: function() { + if (this._focused) + return; + + this._prevFocusedWindow = global.display.focus_window; + this._prevKeyFocusActor = global.stage.get_key_focus(); + + this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged)); + + if (!this._actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false)) + this._actor.grab_key_focus(); + + this._focused = true; + }, + + _focusUngrabbed: function() { + if (!this._focused) + return false; + + if (this._focusActorChangedId > 0) { + global.stage.disconnect(this._focusActorChangedId); + this._focusActorChangedId = 0; + } + + this._focused = false; + return true; + }, + + _focusActorChanged: function() { + let focusedActor = global.stage.get_key_focus(); + if (!focusedActor || !this._actor.contains(focusedActor)) + this._focusUngrabbed(); + }, + + ungrabFocus: function() { + if (!this._focusUngrabbed()) + return; + + if (this._prevKeyFocusActor) { + global.stage.set_key_focus(this._prevKeyFocusActor); + this._prevKeyFocusActor = null; + } else { + let focusedActor = global.stage.get_key_focus(); + if (focusedActor && this._actor.contains(focusedActor)) + global.stage.set_key_focus(null); + } + } +}); + const URLHighlighter = new Lang.Class({ Name: 'URLHighlighter', @@ -1574,6 +1633,7 @@ const MessageTray = new Lang.Class({ this._notificationBin.set_y_align(Clutter.ActorAlign.START); this._notificationWidget.add_actor(this._notificationBin); this._notificationWidget.hide(); + this._notificationFocusGrabber = new FocusGrabber(this._notificationWidget); this._notificationQueue = []; this._notification = null; this._notificationClickedId = 0; @@ -1623,7 +1683,6 @@ const MessageTray = new Lang.Class({ { keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY }); this._grabHelper.addActor(this._summaryBoxPointer.actor); this._grabHelper.addActor(this.actor); - this._grabHelper.addActor(this._notificationWidget); Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged)); @@ -2520,19 +2579,7 @@ const MessageTray = new Lang.Class({ }, _hideNotification: function() { - // HACK! - // There seems to be a reentrancy issue in calling .ungrab() here, - // which causes _updateState to be called before _notificationState - // becomes HIDING. That hides the notification again, nullifying the - // object but not setting _notificationState (and that's the weird part) - // As then _notificationState is stuck into SHOWN but _notification - // is null, every new _updateState fails and the message tray is - // lost forever. - // - // See more at https://bugzilla.gnome.org/show_bug.cgi?id=683986 - this._notificationState = State.HIDING; - - this._grabHelper.ungrab({ actor: this._notification.actor }); + this._notificationFocusGrabber.ungrabFocus(); if (this._notificationExpandedId) { this._notification.disconnect(this._notificationExpandedId); @@ -2630,8 +2677,7 @@ const MessageTray = new Lang.Class({ }, _ensureNotificationFocused: function() { - this._grabHelper.grab({ actor: this._notification.actor, - grabFocus: true }); + this._notificationFocusGrabber.grabFocus(); }, _onSourceDoneDisplayingContent: function(source, closeTray) {