popup-menu: Add support for child menus
Allow opening a popup menu from another menu. While the child menu is open, events on the parent menu are blocked. The parent menu is kept open when the child menu is closed; the child menu on the other hand is closed with the parent, e.g. when the focus moves to another toplevel menu. This feature will be used to implement combo box menu items. https://bugzilla.gnome.org/show_bug.cgi?id=652837
This commit is contained in:
parent
07660f7fcf
commit
5d2b7e2c9e
@ -800,6 +800,7 @@ PopupMenuBase.prototype = {
|
|||||||
this.passEvents = false;
|
this.passEvents = false;
|
||||||
|
|
||||||
this._activeMenuItem = null;
|
this._activeMenuItem = null;
|
||||||
|
this._childMenus = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
addAction: function(title, callback) {
|
addAction: function(title, callback) {
|
||||||
@ -831,6 +832,28 @@ PopupMenuBase.prototype = {
|
|||||||
return menuItem;
|
return menuItem;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isChildMenu: function(menu) {
|
||||||
|
return this._childMenus.indexOf(menu) != -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
addChildMenu: function(menu) {
|
||||||
|
if (this.isChildMenu(menu))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._childMenus.push(menu);
|
||||||
|
this.emit('child-menu-added', menu);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeChildMenu: function(menu) {
|
||||||
|
let index = this._childMenus.indexOf(menu);
|
||||||
|
|
||||||
|
if (index == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._childMenus.splice(index, 1);
|
||||||
|
this.emit('child-menu-removed', menu);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _connectSubMenuSignals:
|
* _connectSubMenuSignals:
|
||||||
* @object: a menu item, or a menu section
|
* @object: a menu item, or a menu section
|
||||||
@ -1400,6 +1423,7 @@ PopupMenuManager.prototype = {
|
|||||||
this._keyFocusNotifyId = 0;
|
this._keyFocusNotifyId = 0;
|
||||||
this._activeMenu = null;
|
this._activeMenu = null;
|
||||||
this._menus = [];
|
this._menus = [];
|
||||||
|
this._menuStack = [];
|
||||||
this._preGrabInputMode = null;
|
this._preGrabInputMode = null;
|
||||||
this._grabbedFromKeynav = false;
|
this._grabbedFromKeynav = false;
|
||||||
},
|
},
|
||||||
@ -1408,6 +1432,8 @@ PopupMenuManager.prototype = {
|
|||||||
let menudata = {
|
let menudata = {
|
||||||
menu: menu,
|
menu: menu,
|
||||||
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
||||||
|
childMenuAddedId: menu.connect('child-menu-added', Lang.bind(this, this._onChildMenuAdded)),
|
||||||
|
childMenuRemovedId: menu.connect('child-menu-removed', Lang.bind(this, this._onChildMenuRemoved)),
|
||||||
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
|
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
|
||||||
enterId: 0,
|
enterId: 0,
|
||||||
focusInId: 0
|
focusInId: 0
|
||||||
@ -1435,6 +1461,8 @@ PopupMenuManager.prototype = {
|
|||||||
|
|
||||||
let menudata = this._menus[position];
|
let menudata = this._menus[position];
|
||||||
menu.disconnect(menudata.openStateChangeId);
|
menu.disconnect(menudata.openStateChangeId);
|
||||||
|
menu.disconnect(menudata.childMenuAddedId);
|
||||||
|
menu.disconnect(menudata.childMenuRemovedId);
|
||||||
menu.disconnect(menudata.destroyId);
|
menu.disconnect(menudata.destroyId);
|
||||||
|
|
||||||
if (menudata.enterId)
|
if (menudata.enterId)
|
||||||
@ -1472,8 +1500,20 @@ PopupMenuManager.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onMenuOpenState: function(menu, open) {
|
_onMenuOpenState: function(menu, open) {
|
||||||
if (open)
|
if (open) {
|
||||||
|
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
|
||||||
|
this._menuStack.push(this._activeMenu);
|
||||||
|
menu.actor.grab_key_focus();
|
||||||
|
}
|
||||||
this._activeMenu = menu;
|
this._activeMenu = menu;
|
||||||
|
} else {
|
||||||
|
if (this._menuStack.length > 0) {
|
||||||
|
this._activeMenu = this._menuStack.pop();
|
||||||
|
if (menu.sourceActor)
|
||||||
|
menu.sourceActor.grab_key_focus();
|
||||||
|
this._didPop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check what the focus was before calling pushModal/popModal
|
// Check what the focus was before calling pushModal/popModal
|
||||||
let focus = global.stage.key_focus;
|
let focus = global.stage.key_focus;
|
||||||
@ -1506,6 +1546,14 @@ PopupMenuManager.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onChildMenuAdded: function(menu, childMenu) {
|
||||||
|
this.addMenu(childMenu);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onChildMenuRemoved: function(menu, childMenu) {
|
||||||
|
this.removeMenu(childMenu);
|
||||||
|
},
|
||||||
|
|
||||||
// change the currently-open menu without dropping grab
|
// change the currently-open menu without dropping grab
|
||||||
_changeMenu: function(newMenu) {
|
_changeMenu: function(newMenu) {
|
||||||
if (this._activeMenu) {
|
if (this._activeMenu) {
|
||||||
@ -1514,6 +1562,8 @@ PopupMenuManager.prototype = {
|
|||||||
// before closing it to keep that from happening
|
// before closing it to keep that from happening
|
||||||
let oldMenu = this._activeMenu;
|
let oldMenu = this._activeMenu;
|
||||||
this._activeMenu = null;
|
this._activeMenu = null;
|
||||||
|
for (let i = this._menuStack.length - 1; i >= 0; i--)
|
||||||
|
this._menuStack[i].close(false);
|
||||||
oldMenu.close(false);
|
oldMenu.close(false);
|
||||||
newMenu.open(false);
|
newMenu.open(false);
|
||||||
} else
|
} else
|
||||||
@ -1524,6 +1574,15 @@ PopupMenuManager.prototype = {
|
|||||||
if (!this.grabbed || menu == this._activeMenu)
|
if (!this.grabbed || menu == this._activeMenu)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (this._activeMenu && this._activeMenu.isChildMenu(menu))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this._menuStack.indexOf(menu) != -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
|
||||||
|
return false;
|
||||||
|
|
||||||
this._changeMenu(menu);
|
this._changeMenu(menu);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -1536,6 +1595,8 @@ PopupMenuManager.prototype = {
|
|||||||
if (focus) {
|
if (focus) {
|
||||||
if (this._activeMenuContains(focus))
|
if (this._activeMenuContains(focus))
|
||||||
return;
|
return;
|
||||||
|
if (this._menuStack.length > 0)
|
||||||
|
return;
|
||||||
if (focus._delegate && focus._delegate.menu &&
|
if (focus._delegate && focus._delegate.menu &&
|
||||||
this._findMenu(focus._delegate.menu) != -1)
|
this._findMenu(focus._delegate.menu) != -1)
|
||||||
return;
|
return;
|
||||||
@ -1594,6 +1655,11 @@ PopupMenuManager.prototype = {
|
|||||||
if (this._activeMenu != null && this._activeMenu.passEvents)
|
if (this._activeMenu != null && this._activeMenu.passEvents)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (this._didPop) {
|
||||||
|
this._didPop = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
let activeMenuContains = this._eventIsOnActiveMenu(event);
|
let activeMenuContains = this._eventIsOnActiveMenu(event);
|
||||||
let eventType = event.type();
|
let eventType = event.type();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user