Enable insertion and removal of menus

This patch adds the method "removeMenu" to PopupMenuManager, to allow
for removal of menus after they're inserted. In order to do this, it
needs to store along with the menu all the relevant signal connections,
that are disconnected when the menu is removed.
Also adds a parameter "position" to "addMenu", so that menus can added
in arbitrary order (in particular to reintroduce those which were removed).
This patch is intended towards dynamic menu users, like extensions for
application lists, docks, sidebars showing recent documents or favourites,
as well as advanced system tray implementations.

https://bugzilla.gnome.org/show_bug.cgi?id=622730
This commit is contained in:
Giovanni Campagna 2010-06-25 14:55:03 +02:00 committed by Dan Winship
parent 4aa80105ca
commit d8df46d4a1

View File

@ -429,17 +429,45 @@ PopupMenuManager.prototype = {
this._delayedMenus = [];
},
addMenu: function(menu, noGrab) {
this._menus.push(menu);
menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState));
menu.connect('activate', Lang.bind(this, this._onMenuActivated));
addMenu: function(menu, noGrab, 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)),
enterId: 0,
buttonPressId: 0
};
let source = menu.sourceActor;
if (source) {
source.connect('enter-event', Lang.bind(this, this._onMenuSourceEnter, menu));
menudata.enterId = source.connect('enter-event', Lang.bind(this, this._onMenuSourceEnter, menu));
if (!noGrab)
source.connect('button-press-event', Lang.bind(this, this._onMenuSourcePress, menu));
menudata.buttonPressId = source.connect('button-press-event', Lang.bind(this, this._onMenuSourcePress, menu));
}
if (position == undefined)
this._menus.push(menudata);
else
this._menus.splice(position, 0, menudata);
},
removeMenu: function(menu) {
if (menu == this._activeMenu)
this._closeMenu();
let position = this._findMenu(menu);
if (position == -1) // not a menu we manage
return;
let menudata = this._menus[position];
menu.disconnect(menudata.openStateChangeId);
menu.disconnect(menudata.activateId);
if (menudata.enterId)
menu.sourceActor.disconnect(menudata.enterId);
if (menudata.buttonPressId)
menu.sourceActor.disconnect(menudata.buttonPressId);
this._menus.splice(position, 1);
},
grab: function() {
@ -505,13 +533,22 @@ PopupMenuManager.prototype = {
_eventIsOnAnyMenuSource: function(event) {
let src = event.get_source();
for (let i = 0; i < this._menus.length; i++) {
let menu = this._menus[i];
let menu = this._menus[i].menu;
if (menu.sourceActor && menu.sourceActor.contains(src))
return true;
}
return false;
},
_findMenu: function(item) {
for (let i = 0; i < this._menus.length; i++) {
let menudata = this._menus[i];
if (item == menudata.menu)
return i;
}
return -1;
},
_onEventCapture: function(actor, event) {
if (!this.grabbed)
return false;
@ -542,7 +579,8 @@ PopupMenuManager.prototype = {
&& (event.get_key_symbol() == Clutter.Left
|| event.get_key_symbol() == Clutter.Right)) {
let direction = event.get_key_symbol() == Clutter.Right ? 1 : -1;
let next = findNextInCycle(this._menus, this._activeMenu, direction);
let pos = this._findMenu(this._activeMenu);
let next = this._menus[mod(pos + direction, this._menus.length)].menu;
if (next != this._activeMenu) {
this._activeMenu.close();
next.open();