[gnome-shell] popupMenu: fix up grab/ungrab handling
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] popupMenu: fix up grab/ungrab handling
- Date: Mon, 20 Dec 2010 22:43:51 +0000 (UTC)
commit f326595202e14eb4447e4fad1cf574884389254e
Author: Dan Winship <danw gnome org>
Date: Wed Nov 3 13:30:08 2010 -0400
popupMenu: fix up grab/ungrab handling
Fix the panel menus to avoid unnecessarily bouncing out of modal (bug
634194) and to do a better job of keeping the keyboard focus in the
right place
https://bugzilla.gnome.org/show_bug.cgi?id=618885
js/ui/panelMenu.js | 7 +--
js/ui/popupMenu.js | 112 +++++++++++++++++++++++++++++++++++++++------------
2 files changed, 87 insertions(+), 32 deletions(-)
---
diff --git a/js/ui/panelMenu.js b/js/ui/panelMenu.js
index 07a7220..0b3ce93 100644
--- a/js/ui/panelMenu.js
+++ b/js/ui/panelMenu.js
@@ -47,12 +47,9 @@ Button.prototype = {
},
_onOpenStateChanged: function(menu, open) {
- if (open) {
+ if (open)
this.actor.add_style_pseudo_class('pressed');
- let focus = global.stage.get_key_focus();
- if (!focus || (focus != this.actor && !menu.actor.contains(focus)))
- this.actor.grab_key_focus();
- } else
+ else
this.actor.remove_style_pseudo_class('pressed');
}
};
diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js
index 190f24a..6d04e3d 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -999,23 +999,24 @@ PopupMenuManager.prototype = {
this._leaveEventId = 0;
this._activeMenu = null;
this._menus = [];
- this._delayedMenus = [];
+ this._preGrabInputMode = null;
},
addMenu: function(menu, position) {
let menudata = {
menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
- activateId: menu.connect('activate', Lang.bind(this, this._onMenuActivated)),
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
enterId: 0,
- focusId: 0
+ focusInId: 0,
+ focusOutId: 0
};
let source = menu.sourceActor;
if (source) {
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
- menudata.focusId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
+ menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
+ menudata.focusOutId = source.connect('key-focus-out', Lang.bind(this, function() { this._onKeyFocusOut(menu); }));
}
if (position == undefined)
@@ -1034,18 +1035,19 @@ PopupMenuManager.prototype = {
let menudata = this._menus[position];
menu.disconnect(menudata.openStateChangeId);
- menu.disconnect(menudata.activateId);
menu.disconnect(menudata.destroyId);
if (menudata.enterId)
menu.sourceActor.disconnect(menudata.enterId);
- if (menudata.focusId)
- menu.sourceActor.disconnect(menudata.focusId);
+ if (menudata.focusInId)
+ menu.sourceActor.disconnect(menudata.focusInId);
+ if (menudata.focusOutId)
+ menu.sourceActor.disconnect(menudata.focusOutId);
this._menus.splice(position, 1);
},
- grab: function() {
+ _grab: function() {
Main.pushModal(this._owner.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
@@ -1057,7 +1059,7 @@ PopupMenuManager.prototype = {
this.grabbed = true;
},
- ungrab: function() {
+ _ungrab: function() {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
global.stage.disconnect(this._keyPressEventId);
@@ -1073,40 +1075,99 @@ PopupMenuManager.prototype = {
_onMenuOpenState: function(menu, open) {
if (open) {
+ if (!this.grabbed) {
+ this._preGrabInputMode = global.stage_input_mode;
+ this._grab();
+ }
this._activeMenu = menu;
- if (!this.grabbed)
- this.grab();
+
+ // if the focus is not already associated with the menu,
+ // then focus the menu
+ let focus = global.stage.key_focus;
+ if (!this._activeMenuContains(focus))
+ menu.sourceActor.grab_key_focus();
} else if (menu == this._activeMenu) {
- this._activeMenu = null;
+ let focus = global.stage.key_focus;
+ let fromActive = this._activeMenuContains(focus);
+
if (this.grabbed)
- this.ungrab();
+ this._ungrab();
+ this._activeMenu = null;
+
+ // If keynav was in effect before we grabbed, then we need
+ // to properly re-establish it after we ungrab. (popModal
+ // will have unset the focus.) If some part of the menu
+ // was focused at the time of the ungrab then focus its
+ // sourceActor. Otherwise just reset the focus to where it
+ // was right before the ungrab.
+ if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED) {
+ global.stage_input_mode = Shell.StageInputMode.FOCUSED;
+ if (fromActive)
+ menu.sourceActor.grab_key_focus();
+ else
+ focus.grab_key_focus();
+ }
+ }
+ },
+
+ // change the currently-open menu without dropping grab
+ _changeMenu: function(newMenu) {
+ if (this._activeMenu) {
+ // _onOpenMenuState will drop the grab if it sees
+ // this._activeMenu being closed; so clear _activeMenu
+ // before closing it to keep that from happening
+ let oldMenu = this._activeMenu;
+ this._activeMenu = null;
+ oldMenu.close();
}
+ newMenu.open();
},
_onMenuSourceEnter: function(menu) {
if (!this.grabbed || menu == this._activeMenu)
return false;
- if (this._activeMenu != null)
- this._activeMenu.close();
- menu.open();
+ this._changeMenu(menu);
return false;
},
- _onMenuActivated: function(menu, item) {
- if (this.grabbed)
- this.ungrab();
+ _onKeyFocusOut: function(menu) {
+ if (!this.grabbed || menu != this._activeMenu)
+ return;
+
+ // We want to close the menu if the focus has moved somewhere
+ // other than inside the menu or to another menu's sourceActor.
+ // Unfortunately, when key-focus-out is emitted,
+ // stage.key_focus will be null. So we have to wait until
+ // after it emits the key-focus-in as well.
+ let id = global.stage.connect('notify::key-focus', Lang.bind(this,
+ function () {
+ global.stage.disconnect(id);
+
+ if (menu != this._activeMenu)
+ return;
+
+ let focus = global.stage.key_focus;
+ if (!focus || this._activeMenuContains(focus))
+ return;
+ if (focus._delegate && this._findMenu(focus._delegate.menu) != -1)
+ return;
+ menu.close();
+ }));
},
_onMenuDestroy: function(menu) {
this.removeMenu(menu);
},
- _eventIsOnActiveMenu: function(event) {
- let src = event.get_source();
+ _activeMenuContains: function(actor) {
return this._activeMenu != null
- && (this._activeMenu.actor.contains(src) ||
- (this._activeMenu.sourceActor && this._activeMenu.sourceActor.contains(src)));
+ && (this._activeMenu.actor.contains(actor) ||
+ (this._activeMenu.sourceActor && this._activeMenu.sourceActor.contains(actor)));
+ },
+
+ _eventIsOnActiveMenu: function(event) {
+ return this._activeMenuContains(event.get_source());
},
_eventIsOnAnyMenuSource: function(event) {
@@ -1168,10 +1229,7 @@ PopupMenuManager.prototype = {
let pos = this._findMenu(this._activeMenu);
let next = this._menus[mod(pos + direction, this._menus.length)].menu;
if (next != this._activeMenu) {
- let oldMenu = this._activeMenu;
- this._activeMenu = next;
- oldMenu.close();
- next.open();
+ this._changeMenu(next);
next.activateFirst();
}
return true;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]