messageTray: Don't use focus grabs
We can easily implement much of the same behavior ourselves by keeping track of Clutter's focus events. Reintroduce heavily modified FocusGrabber to do the work for us. This will temporarily break when the user selects a window until we can make gnome-shell automatically set the stage focus. This also removes our only use of focus grabs, so remove those as well. https://bugzilla.gnome.org/show_bug.cgi?id=700735
This commit is contained in:
parent
3790e924e9
commit
eef593a34e
@ -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 });
|
||||
}
|
||||
});
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user