grabHelper: Introduce a stack of grab helpers

GrabHelpers use a 'captured-event' to steal events and emulate
modality or grab-like semantics. There can be issues when you try to
use multiple GrabHelpers stacked on each other. As Clutter follows
the DOM-like semantics of "first come, first serve", when a second
GrabHelper connects to 'captured-event', its callback will only be
processed *after* the first GrabHelper's callback is called.

This breaks the expectation of narrowing modality where new modals
take priority over the old ones.

Solving this globally in a cleaner manner would require a rewrite of
pushModal/GrabHelper. As a stopgap fix for now, use one shared
'captured-event' handler between all GrabHelper instances, and
delegate to the individual GrabHelpers.

https://bugzilla.gnome.org/show_bug.cgi?id=699272
This commit is contained in:
Jasper St. Pierre 2013-08-16 20:11:36 -04:00
parent 6d317d300c
commit 8d9aa6388d

View File

@ -10,6 +10,31 @@ const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
let _capturedEventId = 0;
let _grabHelperStack = [];
function _onCapturedEvent(actor, event) {
let grabHelper = _grabHelperStack[_grabHelperStack.length - 1];
return grabHelper.onCapturedEvent(event);
}
function _pushGrabHelper(grabHelper) {
_grabHelperStack.push(grabHelper);
if (_capturedEventId == 0)
_capturedEventId = global.stage.connect('captured-event', _onCapturedEvent);
}
function _popGrabHelper(grabHelper) {
let poppedHelper = _grabHelperStack.pop();
if (poppedHelper != grabHelper)
throw new Error("incorrect grab helper pop");
if (_grabHelperStack.length == 0) {
global.stage.disconnect(_capturedEventId);
_capturedEventId = 0;
}
}
// GrabHelper: // GrabHelper:
// @owner: the actor that owns the GrabHelper // @owner: the actor that owns the GrabHelper
// @params: optional parameters to pass to Main.pushModal() // @params: optional parameters to pass to Main.pushModal()
@ -31,7 +56,6 @@ const GrabHelper = new Lang.Class({
this._grabStack = []; this._grabStack = [];
this._actors = []; this._actors = [];
this._capturedEventId = 0;
this._ignoreRelease = false; this._ignoreRelease = false;
this._modalCount = 0; this._modalCount = 0;
@ -177,7 +201,7 @@ const GrabHelper = new Lang.Class({
if (!Main.pushModal(this._owner, this._modalParams)) if (!Main.pushModal(this._owner, this._modalParams))
return false; return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); _pushGrabHelper(this);
} }
this._modalCount++; this._modalCount++;
@ -189,8 +213,7 @@ const GrabHelper = new Lang.Class({
if (this._modalCount > 0) if (this._modalCount > 0)
return; return;
global.stage.disconnect(this._capturedEventId); _popGrabHelper(this);
this._capturedEventId = 0;
this._ignoreRelease = false; this._ignoreRelease = false;
@ -251,7 +274,7 @@ const GrabHelper = new Lang.Class({
} }
}, },
_onCapturedEvent: function(actor, event) { onCapturedEvent: function(event) {
let type = event.type(); let type = event.type();
if (type == Clutter.EventType.KEY_PRESS && if (type == Clutter.EventType.KEY_PRESS &&