From b087191d2b486b33d7b04db4aa8c82c37f61958f Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Wed, 21 Dec 2011 23:11:40 +0100 Subject: [PATCH] RemoteMenu: add support for section labels According to the GIO docs, sections can have labels too. We support them by inserting a non reactive menu item at the beginning of the section. This item is specially flagged to be ignored while processing changed signals from the model (since it does not correspond to any model item) https://bugzilla.gnome.org/show_bug.cgi?id=666681 --- js/ui/popupMenu.js | 53 ++++++++++++++++++++++++++++++++-------- src/test-gapplication.js | 2 +- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index 93ac2f33e..1d847f8c8 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -1398,12 +1398,23 @@ const PopupMenuSection = new Lang.Class({ this.actor = this.box; this.actor._delegate = this; this.isOpen = true; + + // an array of externally managed separators + this.separators = []; }, // deliberately ignore any attempt to open() or close(), but emit the // corresponding signal so children can still pick it up open: function(animate) { this.emit('open-state-changed', true); }, close: function() { this.emit('open-state-changed', false); }, + + destroy: function() { + for (let i = 0; i < this.separators.length; i++) + this.separators[i].destroy(); + this.separators = []; + + this.parent(); + } }); const PopupSubMenuMenuItem = new Lang.Class({ @@ -1737,17 +1748,25 @@ const RemoteMenu = new Lang.Class({ }, _createMenuItem: function(model, index) { + let labelValue = model.get_item_attribute_value(index, Gio.MENU_ATTRIBUTE_LABEL, null); + let label = labelValue ? labelValue.deep_unpack() : ''; + // remove all underscores that are not followed by another underscore + label = label.replace(/_([^_])/, '$1'); + let section_link = model.get_item_link(index, Gio.MENU_LINK_SECTION); if (section_link) { let item = new PopupMenuSection(); + if (label) { + let title = new PopupMenuItem(label, { reactive: false, + style_class: 'popup-subtitle-menu-item' }); + item._titleMenuItem = title; + title._ignored = true; + item.addMenuItem(title); + } this._modelChanged(section_link, 0, 0, section_link.get_n_items(), item); return [item, true, '']; } - // labels are not checked for existance, as they're required for all items - let label = model.get_item_attribute_value(index, Gio.MENU_ATTRIBUTE_LABEL, null).deep_unpack(); - // remove all underscores that are not followed by another underscore - label = label.replace(/_([^_])/, '$1'); let submenu_link = model.get_item_link(index, Gio.MENU_LINK_SUBMENU); if (submenu_link) { @@ -1831,8 +1850,13 @@ const RemoteMenu = new Lang.Class({ let currentItems = target._getMenuItems(); - for (j0 = 0, k0 = 0; j0 < position; j0++, k0++) { - if (currentItems[k0] instanceof PopupSeparatorMenuItem) + k0 = 0; + // skip ignored items at the beginning + while (k0 < currentItems.length && currentItems[k0]._ignored) + k0++; + // find the right menu item matching the model item + for (j0 = 0; j0 < position; j0++, k0++) { + if (currentItems[k0]._ignored) k0++; } @@ -1844,7 +1868,7 @@ const RemoteMenu = new Lang.Class({ for (j = j0, k = k0; j < j0 + removed; j++, k++) { currentItems[k].destroy(); - if (currentItems[k] instanceof PopupSeparatorMenuItem) + if (currentItems[k]._ignored) j--; } } @@ -1855,14 +1879,20 @@ const RemoteMenu = new Lang.Class({ if (item) { // separators must be added in the parent to make autohiding work if (addSeparator) { - target.addMenuItem(new PopupSeparatorMenuItem(), k+1); + let separator = new PopupSeparatorMenuItem(); + item.separators.push(separator); + separator._ignored = true; + target.addMenuItem(separator, k+1); k++; } target.addMenuItem(item, k); if (addSeparator) { - target.addMenuItem(new PopupSeparatorMenuItem(), k+1); + let separator = new PopupSeparatorMenuItem(); + item.separators.push(separator); + separator._ignored = true; + target.addMenuItem(separator, k+1); k++; } } else if (changeSignal) { @@ -1888,7 +1918,10 @@ const RemoteMenu = new Lang.Class({ } if (target instanceof PopupMenuSection) { - target.actor.visible = target.numMenuItems != 0; + if (target._titleMenuItem) + target.actor.visible = target.numMenuItems != 1; + else + target.actor.visible = target.numMenuItems != 0; } else { let sourceItem = target.sourceActor._delegate; if (sourceItem instanceof PopupSubMenuMenuItem) diff --git a/src/test-gapplication.js b/src/test-gapplication.js index dfae2abe9..8449c6ae8 100755 --- a/src/test-gapplication.js +++ b/src/test-gapplication.js @@ -68,7 +68,7 @@ function main() { section = new Gio.Menu(); section.append('Checkbox', 'app.toggle'); section.append('Disabled', 'app.disable'); - menu.append_section(null, section); + menu.append_section('Subsection', section); // empty sections or submenus should be invisible menu.append_section('Empty section', new Gio.Menu());