[gnome-shell/wip/pressure: 6/14] Introduce a new GrabHelper
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/pressure: 6/14] Introduce a new GrabHelper
- Date: Mon, 30 Jul 2012 23:49:26 +0000 (UTC)
commit 32d4b8c8b746481868d7eed4fe429e8f7352da90
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Tue Feb 28 12:16:12 2012 -0500
Introduce a new GrabHelper
PopupMenu.PopupMenuManager and MessageTray.FocusGrabber had a lot of
code in common. Let's refactor this common code out into a new class,
"GrabHelper". This replaces FocusGrabber completely, and nukes half
of PopupMenuManager.
Based on a patch by Dan Winship <danw gnome org>
https://bugzilla.gnome.org/show_bug.cgi?id=643687
https://bugzilla.gnome.org/show_bug.cgi?id=671001
js/Makefile.am | 1 +
js/ui/grabHelper.js | 235 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 236 insertions(+), 0 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index 97d95a9..d018e9c 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -52,6 +52,7 @@ nobase_dist_js_DATA = \
ui/extensionDownloader.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
+ ui/grabHelper.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/keyringPrompt.js \
diff --git a/js/ui/grabHelper.js b/js/ui/grabHelper.js
new file mode 100644
index 0000000..eb4205d
--- /dev/null
+++ b/js/ui/grabHelper.js
@@ -0,0 +1,235 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Signals = imports.signals;
+const Shell = imports.gi.Shell;
+
+const Main = imports.ui.main;
+const Params = imports.misc.params;
+
+// GrabHelper:
+// @owner: the actor that owns the GrabHelper
+// @params: parameters, described below
+//
+// Creates a new GrabHelper object, for dealing with keyboard and pointer
+// grabs associated with a set of actors.
+//
+// If @params contains { modal: true }, then grab() will call
+// Main.pushModal(), and ungrab will call Main.popModal() (and, as a
+// result, if the grab is ended by a click outside the grabbed area,
+// that click will be eaten.).
+//
+// If @params contains { grabFocus: true }, then if you call grab()
+// while the shell is outside the overview, it will set the stage
+// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
+// revert it back, and re-focus the previously-focused window (if
+// another window hasn't been explicitly focused before then).
+
+// The GrabHelper will emit 'grabbed' after a grab(), and 'ungrabbed'
+// after an ungrab() (with an argument indicating if the grab was lost
+// due to user action rather than due to an explicit ungrab() call).
+const GrabHelper = new Lang.Class({
+ Name: 'GrabHelper',
+
+ _init: function(owner, params) {
+ params = Params.parse(params, { modal: false,
+ grabFocus: false });
+ this._owner = owner;
+ this._modal = params.modal;
+ this._grabFocus = params.grabFocus;
+
+ this.grabbed = false;
+
+ this._actors = [];
+ this._capturedEventId = 0;
+ this._eventId = 0;
+ this._keyFocusNotifyId = 0;
+ this._focusWindowChangedId = 0;
+ },
+
+ // addActor:
+ // @actor: an actor
+ //
+ // Adds @actor to the set of actors that are allowed to process events
+ // during a grab.
+ addActor: function(actor) {
+ actor.__grabHelperDestroyId = actor.connect('destroy', Lang.bind(this, function() { this._removeActor(actor); }));
+ this._actors.push(actor);
+ },
+
+ // removeActor:
+ // @actor: an actor
+ //
+ // Removes @actor from the set of actors that are allowed to
+ // process events during a grab.
+ removeActor: function(actor) {
+ let index = this._actors.indexOf(actor);
+ if (index != -1)
+ this._actors.splice(index, 1);
+ if (actor.__grabHelperDestroyId) {
+ actor.disconnect(actor.__grabHelperDestroyId);
+ delete actor.__grabHelperDestroyId;
+ }
+ },
+
+ _isWithinGrabbedActor: function(actor) {
+ while (actor) {
+ if (this._actors.indexOf(actor) != -1)
+ return true;
+ actor = actor.get_parent();
+ }
+ return false;
+ },
+
+ // grab:
+ // @newFocus: (allow-none): new focus container
+ //
+ // Grabs the mouse and keyboard, according to the GrabHelper's
+ // parameters. If @newFocus is not %null, then the keyboard focus
+ // is moved to the first #StWidget:can-focus widget inside it.
+ //
+ // The grab will automatically be dropped if:
+ // - The user clicks outside the grabbed actors
+ // - The user types Escape
+ // - The keyboard focus is moved outside the grabbed actors
+ // - A window is focused
+ grab: function(newFocus) {
+ if (this.grabbed)
+ return;
+ this.grabbed = true;
+
+ let metaDisplay = global.screen.get_display();
+
+ let focus = global.stage.key_focus;
+ let hadFocus = focus && this._isWithinGrabbedActor(focus);
+
+ this._grabbedFromKeynav = hadFocus;
+ this._preGrabInputMode = global.stage_input_mode;
+ this._prevFocusedWindow = null;
+
+ if (this._modal) {
+ Main.pushModal(this._owner);
+ if (hadFocus)
+ focus.grab_key_focus();
+ }
+
+ if (this._grabFocus) {
+ this._prevFocusedWindow = metaDisplay.focus_window;
+ if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
+ this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
+ global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
+ }
+ }
+
+ if (newFocus)
+ newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
+
+ this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
+ this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
+ 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.emit('grabbed');
+ },
+
+ // ungrab:
+ // @newFocus: (allow-none): new focus container
+ // @userAction: Whether the ungrab was the cause of a user
+ // action.
+ //
+ // Ungrabs the mouse and keyboard.
+ //
+ // Normally, the keyboard focus will be reverted to wherever it
+ // was before the grab. However, if @newFocus is not %null, and
+ // the grab was both initiated by and ended by a keyboard action
+ // on a grabbed actor, then the focus will be set to @newFocus
+ // after releasing the grab. (This allows you to ensure that the
+ // keynav focus reverts to the expected location, which may not be
+ // the same actor as it was on before the grab.)
+ ungrab: function(newFocus, userAction) {
+ if (!this.grabbed)
+ return;
+ this.grabbed = false;
+
+ global.stage.disconnect(this._capturedEventId);
+ this._capturedEventId = 0;
+ global.stage.disconnect(this._eventId);
+ this._eventId = 0;
+ global.stage.disconnect(this._keyFocusNotifyId);
+ this._keyFocusNotifyId = 0;
+ let metaDisplay = global.screen.get_display();
+ metaDisplay.disconnect(this._focusWindowChangedId);
+ this._focusWindowChangedId = 0;
+
+ let focus = global.stage.key_focus;
+ let hadFocus = focus && this._isWithinGrabbedActor(focus);
+ let prePopInputMode = global.stage_input_mode;
+
+ if (this._modal) {
+ Main.popModal(this._owner);
+ global.sync_pointer();
+ }
+
+ if (this._grabbedFromKeynav) {
+ if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
+ prePopInputMode != Shell.StageInputMode.FULLSCREEN)
+ global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
+ if (hadFocus && newFocus)
+ newFocus.grab_key_focus();
+ }
+
+ if (this._prevFocusedWindow) {
+ let metaDisplay = global.screen.get_display();
+ if (!metaDisplay.focus_window) {
+ metaDisplay.set_input_focus_window(this._prevFocusedWindow,
+ false, global.get_current_time());
+ }
+ }
+
+ this.emit('ungrabbed', userAction);
+ },
+
+ _onCapturedEvent: function(actor, event) {
+ let type = event.type();
+ let button = (type == Clutter.EventType.BUTTON_PRESS ||
+ type == Clutter.EventType.BUTTON_RELEASE);
+
+ if (!button && !this._modal)
+ return false;
+
+ if (this._isWithinGrabbedActor(event.get_source()))
+ return false;
+
+ if (button)
+ this.ungrab(null, true);
+ return this._modal;
+ },
+
+ // We catch 'event' rather than 'key-press-event' so that we get
+ // a chance to run before the overview's own Escape check
+ _onEvent: function(actor, event) {
+ if (event.type() == Clutter.EventType.KEY_PRESS &&
+ event.get_key_symbol() == Clutter.KEY_Escape) {
+ this.ungrab(null, true);
+ return true;
+ }
+
+ return false;
+ },
+
+ _onKeyFocusChanged: function() {
+ let focus = global.stage.key_focus;
+ if (!focus || !this._isWithinGrabbedActor(focus))
+ this.ungrab(null, true);
+ },
+
+ _focusWindowChanged: function() {
+ let metaDisplay = global.screen.get_display();
+ if (metaDisplay.focus_window != null)
+ this.ungrab(null, true);
+ }
+});
+Signals.addSignalMethods(GrabHelper.prototype);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]