PopupMenu: introduce PopupMenuSection

Complex popup menus require the ability to manager sequences of items
as "sections", to which you can add and and remove items, as well
as hide and show.
PopupMenuSection does exactly that, leveraging the existing machinery
for submenus, but without being exposed as a submenu to the user.
Also, make getMenuItems() private, since it is used for different things
now and may change semantics in the future.

https://bugzilla.gnome.org/show_bug.cgi?id=621707
This commit is contained in:
Giovanni Campagna 2011-01-25 22:04:57 +01:00
parent 6e236546ea
commit f34ce9271c

View File

@ -632,38 +632,28 @@ PopupMenuBase.prototype = {
})); }));
}, },
addMenuItem: function(menuItem, position) { /**
let before_item = null; * _connectSubMenuSignals:
if (position == undefined) { * @object: a menu item, or a menu section
this.box.add(menuItem.actor); * @menu: a sub menu, or a menu section
} else { *
let items = this.getMenuItems(); * Connects to signals on @menu that are necessary for
if (position < items.length) { * operating the submenu, and stores the ids on @object.
before_item = items[position].actor; */
this.box.insert_before(menuItem.actor, before_item); _connectSubMenuSignals: function(object, menu) {
} else object._subMenuActivateId = menu.connect('activate', Lang.bind(this, function() {
this.box.add(menuItem.actor);
}
if (menuItem instanceof PopupSubMenuMenuItem) {
if (before_item == null)
this.box.add(menuItem.menu.actor);
else
this.box.insert_before(menuItem.menu.actor, before_item);
menuItem._subMenuActivateId = menuItem.menu.connect('activate', Lang.bind(this, function() {
this.emit('activate'); this.emit('activate');
this.close(true); this.close(true);
})); }));
menuItem._subMenuActiveChangeId = menuItem.menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) { object._subMenuActiveChangeId = menu.connect('active-changed', Lang.bind(this, function(submenu, submenuItem) {
if (this._activeMenuItem && this._activeMenuItem != submenuItem) if (this._activeMenuItem && this._activeMenuItem != submenuItem)
this._activeMenuItem.setActive(false); this._activeMenuItem.setActive(false);
this._activeMenuItem = submenuItem; this._activeMenuItem = submenuItem;
this.emit('active-changed', submenuItem); this.emit('active-changed', submenuItem);
})); }));
menuItem._closingId = this.connect('open-state-changed', function(self, open) { },
if (!open)
menuItem.menu.close(false); _connectItemSignals: function(menuItem) {
});
}
menuItem._activeChangeId = menuItem.connect('active-changed', Lang.bind(this, function (menuItem, active) { menuItem._activeChangeId = menuItem.connect('active-changed', Lang.bind(this, function (menuItem, active) {
if (active && this._activeMenuItem != menuItem) { if (active && this._activeMenuItem != menuItem) {
if (this._activeMenuItem) if (this._activeMenuItem)
@ -692,6 +682,41 @@ PopupMenuBase.prototype = {
})); }));
}, },
addMenuItem: function(menuItem, position) {
let before_item = null;
if (position == undefined) {
this.box.add(menuItem.actor);
} else {
let items = this._getMenuItems();
if (position < items.length) {
before_item = items[position].actor;
this.box.insert_before(menuItem.actor, before_item);
} else
this.box.add(menuItem.actor);
}
if (menuItem instanceof PopupMenuSection) {
this._connectSubMenuSignals(menuItem, menuItem);
menuItem.connect('destroy', Lang.bind(this, function() {
menuItem.disconnect(menuItem._subMenuActivateId);
menuItem.disconnect(menuItem._subMenuActiveChangeId);
}));
} else if (menuItem instanceof PopupSubMenuMenuItem) {
if (before_item == null)
this.box.add(menuItem.menu.actor);
else
this.box.insert_before(menuItem.menu.actor, before_item);
this._connectSubMenuSignals(menuItem, menuItem.menu);
this._connectItemSignals(menuItem);
menuItem._closingId = this.connect('open-state-changed', function(self, open) {
if (!open)
menuItem.menu.close(false);
});
} else if (menuItem instanceof PopupBaseMenuItem)
this._connectItemSignals(menuItem);
else
throw TypeError("Invalid argument to PopupMenuBase.addMenuItem()");
},
getColumnWidths: function() { getColumnWidths: function() {
let columnWidths = []; let columnWidths = [];
let items = this.box.get_children(); let items = this.box.get_children();
@ -719,12 +744,16 @@ PopupMenuBase.prototype = {
this.box.add(actor); this.box.add(actor);
}, },
getMenuItems: function() { _getMenuItems: function() {
return this.box.get_children().map(function (actor) { return actor._delegate; }).filter(function(item) { return item instanceof PopupBaseMenuItem; }); return this.box.get_children().map(function (actor) {
return actor._delegate;
}).filter(function(item) {
return item instanceof PopupBaseMenuItem || item instanceof PopupMenuSection;
});
}, },
removeAll: function() { removeAll: function() {
let children = this.getMenuItems(); let children = this._getMenuItems();
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
let item = children[i]; let item = children[i];
item.destroy(); item.destroy();
@ -935,6 +964,34 @@ PopupSubMenu.prototype = {
} }
}; };
/**
* PopupMenuSection:
*
* A section of a PopupMenu which is handled like a submenu
* (you can add and remove items, you can destroy it, you
* can add it to another menu), but is completely transparent
* to the user
*/
function PopupMenuSection() {
this._init.apply(this, arguments);
}
PopupMenuSection.prototype = {
__proto__: PopupMenuBase.prototype,
_init: function() {
PopupMenuBase.prototype._init.call(this);
this.actor = this.box;
this.actor._delegate = this;
this.isOpen = true;
},
// deliberately ignore any attempt to open() or close()
open: function(animate) { },
close: function() { },
}
function PopupSubMenuMenuItem() { function PopupSubMenuMenuItem() {
this._init.apply(this, arguments); this._init.apply(this, arguments);
} }