From: Emilio Pozuelo Monfort <[email protected]>
Date: Wed, 9 Oct 2019 11:17:55 +0200
Subject: Add support for nested menus
Bug-Kali: https://bugs.kali.org/view.php?id=2223
Bug: https://bugzilla.gnome.org/show_bug.cgi?id=739480
---
extensions/apps-menu/extension.js | 167 ++++++++++++++++++++++++++++++--------
1 file changed, 134 insertions(+), 33 deletions(-)
--- a/extensions/apps-menu/extension.js
+++ b/extensions/apps-menu/extension.js
@@ -97,30 +97,36 @@ class ApplicationMenuItem extends PopupM
});
var CategoryMenuItem = GObject.registerClass(
-class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
- _init(button, category) {
- super._init();
- this._category = category;
- this._button = button;
-
- this._oldX = -1;
- this._oldY = -1;
+class CategoryMenuItem extends PopupMenu.PopupMenuItem {
+ /* Kali patch note: the use of PopupMenuItem instead of
+ * PopupBaseMenuItem means that all the code in _onMotionEvent and
+ * isNavigatingSubmenu is no longer used. And the menu name needs to
+ * be passed to the parent constructor instead of being added in the
+ * menu item by ourselves. The _oldX/_oldY variables are not
+ * initialized as they are only needed for isNavigatingSubmenu */
+ _init(button, category) {
let name;
- if (this._category)
- name = this._category.get_name();
+ if (category)
+ name = category.get_name();
else
name = _('Favorites');
- this.add_child(new St.Label({ text: name }));
+ super._init(name);
+
+ this._category = category;
+ this._button = button;
+
this.connect('motion-event', this._onMotionEvent.bind(this));
this.connect('notify::active', this._onActiveChanged.bind(this));
}
activate(event) {
this._button.selectCategory(this._category);
- this._button.scrollToCatButton(this);
- super.activate(event);
+ //this._button.scrollToCatButton(this);
+ // we don't chain up here so that clicking on a category doesn't
+ // close the menu
+ //super.activate(event);
}
_isNavigatingSubmenu([x, y]) {
@@ -209,10 +215,60 @@ class CategoryMenuItem extends PopupMenu
return;
this._button.selectCategory(this._category);
+ //this._button.scrollToCatButton(this);
+ }
+});
+
+var ParentCategoryMenuItem = GObject.registerClass(
+class ParentCategoryMenuItem extends PopupMenu.PopupSubMenuMenuItem {
+ _init(button, category) {
+ let name;
+ if (category)
+ name = category.get_name();
+ else
+ name = _("Favorites");
+
+ super._init(name, false);
+
+ this._category = category;
+ this._button = button;
+ this.connect('notify::active', this._onActiveChanged.bind(this));
+ }
+
+ activate(event) {
+ this._button.selectCategory(this._category);
this._button.scrollToCatButton(this);
+ super.activate(event);
+ }
+
+ _onActiveChanged(active) {
+ if (active) {
+ this._button.selectCategory(this._category);
+ //this._button.scrollToCatButton(this);
+ }
}
});
+class PopupMenuScrollView extends PopupMenu.PopupMenuSection {
+ constructor() {
+ super();
+
+ this.actor = new St.ScrollView({ style_class: 'vfade',
+ hscrollbar_policy: Gtk.PolicyType.NEVER,
+ vscrollbar_policy: Gtk.PolicyType.AUTOMATIC });
+
+ this.actor.add_actor(this.box);
+ this.actor._delegate = this;
+ this.actor.clip_to_allocation = true;
+ }
+
+ _setOpenedSubMenu(menu) {
+ // We don't close the currently opened submenu so that nested submenus work
+ // https://mail.gnome.org/archives/gnome-shell-list/2014-January/msg00010.html
+ this.openChildMenu = menu;
+ }
+};
+
class ApplicationsMenu extends PopupMenu.PopupMenu {
constructor(sourceActor, arrowAlignment, arrowSide, button) {
super(sourceActor, arrowAlignment, arrowSide);
@@ -412,7 +468,7 @@ class ApplicationsButton extends PanelMe
_onTreeChanged() {
if (this.menu.isOpen) {
this._redisplay();
- this.mainBox.show();
+ this.mainBox.actor.show();
} else {
this.reloadFlag = true;
}
@@ -477,7 +533,7 @@ class ApplicationsButton extends PanelMe
this._redisplay();
this.reloadFlag = false;
}
- this.mainBox.show();
+ this.mainBox.actor.show();
}
super._onOpenStateChanged(menu, open);
}
@@ -490,13 +546,14 @@ class ApplicationsButton extends PanelMe
_redisplay() {
this.applicationsBox.destroy_all_children();
- this.categoriesBox.destroy_all_children();
+ this.categoriesBox.removeAll();
this._display();
}
- _loadCategory(categoryId, dir) {
+ _loadCategory(dir, parentCategory) {
let iter = dir.iter();
let nextType;
+ let categoryId = dir.get_menu_id();
while ((nextType = iter.next()) !== GMenu.TreeItemType.INVALID) {
if (nextType === GMenu.TreeItemType.ENTRY) {
let entry = iter.get_entry();
@@ -515,8 +572,17 @@ class ApplicationsButton extends PanelMe
this.applicationsByCategory[categoryId].push('separator');
} else if (nextType === GMenu.TreeItemType.DIRECTORY) {
let subdir = iter.get_directory();
- if (!subdir.get_is_nodisplay())
- this._loadCategory(categoryId, subdir);
+ if (!subdir.get_is_nodisplay()) {
+ let subdirCategoryId = subdir.get_menu_id();
+ this.applicationsByCategory[subdirCategoryId] = [];
+ let categoryMenuItem = new ParentCategoryMenuItem(this, subdir);
+ this._loadCategory(subdir, categoryMenuItem);
+ if (categoryMenuItem.menu.isEmpty())
+ categoryMenuItem = new CategoryMenuItem(this, subdir);
+ if (this.applicationsByCategory[subdirCategoryId].length > 0 || !categoryMenuItem.menu.isEmpty()) {
+ parentCategory.menu.addMenuItem(categoryMenuItem);
+ }
+ }
}
}
}
@@ -537,8 +603,8 @@ class ApplicationsButton extends PanelMe
}
scrollToCatButton(button) {
- let catsScrollBoxAdj = this.categoriesScrollBox.get_vscroll_bar().get_adjustment();
- let catsScrollBoxAlloc = this.categoriesScrollBox.get_allocation_box();
+ let catsScrollBoxAdj = this.categoriesBox.actor.get_vscroll_bar().get_adjustment();
+ let catsScrollBoxAlloc = this.categoriesBox.actor.get_allocation_box();
let currentScrollValue = catsScrollBoxAdj.get_value();
let boxHeight = catsScrollBoxAlloc.y2 - catsScrollBoxAlloc.y1;
let buttonAlloc = button.get_allocation_box();
@@ -554,8 +620,9 @@ class ApplicationsButton extends PanelMe
_createLayout() {
let section = new PopupMenu.PopupMenuSection();
this.menu.addMenuItem(section);
- this.mainBox = new St.BoxLayout({ vertical: false });
- this.leftBox = new St.BoxLayout({ vertical: true });
+ this.mainBox = new PopupMenu.PopupMenuSection();
+ this.mainBox.actor.vertical = false;
+ this.leftBox = new PopupMenu.PopupMenuSection();
this.applicationsScrollBox = new St.ScrollView({
style_class: 'apps-menu vfade',
x_expand: true,
@@ -568,6 +635,7 @@ class ApplicationsButton extends PanelMe
vscroll.connect('scroll-stop', () => {
this.menu.passEvents = false;
});
+/*
this.categoriesScrollBox = new St.ScrollView({
style_class: 'vfade',
});
@@ -576,9 +644,10 @@ class ApplicationsButton extends PanelMe
vscroll.connect('scroll-start', () => (this.menu.passEvents = true));
vscroll.connect('scroll-stop', () => (this.menu.passEvents = false));
this.leftBox.add_child(this.categoriesScrollBox);
-
+*/
this.applicationsBox = new St.BoxLayout({ vertical: true });
this.applicationsScrollBox.add_actor(this.applicationsBox);
+/*
this.categoriesBox = new St.BoxLayout({ vertical: true });
this.categoriesScrollBox.add_actor(this.categoriesBox);
@@ -586,19 +655,29 @@ class ApplicationsButton extends PanelMe
this.mainBox.add_child(this._createVertSeparator());
this.mainBox.add_child(this.applicationsScrollBox);
section.actor.add_actor(this.mainBox);
+*/
+ this.categoriesBox = new PopupMenuScrollView();
+ vscroll = this.categoriesBox.actor.get_vscroll_bar();
+ vscroll.connect('scroll-start', () => { this.menu.passEvents = true; });
+ vscroll.connect('scroll-stop', () => { this.menu.passEvents = false; });
+ this.leftBox.actor.add_actor(this.categoriesBox.actor);
+ this.mainBox.addMenuItem(this.leftBox);
+ this.mainBox.actor.add_actor(this._createVertSeparator());
+ this.mainBox.actor.add_actor(this.applicationsScrollBox);
+ section.addMenuItem(this.mainBox);
}
_display() {
this._applicationsButtons.clear();
- this.mainBox.style = 'width: 35em;';
- this.mainBox.hide();
+ this.mainBox.actor.style = 'width: 35em;';
+ this.mainBox.actor.hide();
// Load categories
this.applicationsByCategory = {};
this._tree.load_sync();
let root = this._tree.get_root_directory();
let categoryMenuItem = new CategoryMenuItem(this, null);
- this.categoriesBox.add_actor(categoryMenuItem);
+ this.categoriesBox.addMenuItem(categoryMenuItem);
let iter = root.iter();
let nextType;
while ((nextType = iter.next()) !== GMenu.TreeItemType.INVALID) {
@@ -611,10 +690,13 @@ class ApplicationsButton extends PanelMe
let categoryId = dir.get_menu_id();
this.applicationsByCategory[categoryId] = [];
- this._loadCategory(categoryId, dir);
- if (this.applicationsByCategory[categoryId].length > 0) {
- categoryMenuItem = new CategoryMenuItem(this, dir);
- this.categoriesBox.add_actor(categoryMenuItem);
+ let categoryMenuItem = new ParentCategoryMenuItem(this, dir);
+ this._loadCategory(dir, categoryMenuItem);
+ if (categoryMenuItem.menu.isEmpty())
+ categoryMenuItem = new CategoryMenuItem(this, dir);
+
+ if (this.applicationsByCategory[categoryId].length > 0 || !categoryMenuItem.menu.isEmpty()) {
+ this.categoriesBox.addMenuItem(categoryMenuItem);
}
}
@@ -623,9 +705,10 @@ class ApplicationsButton extends PanelMe
let themeContext = St.ThemeContext.get_for_stage(global.stage);
let scaleFactor = themeContext.scale_factor;
- let categoriesHeight = this.categoriesBox.height / scaleFactor;
+ let categoriesHeight = this.categoriesBox.actor.height / scaleFactor;
let height = Math.round(categoriesHeight) + MENU_HEIGHT_OFFSET;
- this.mainBox.style += `height: ${height}px`;
+ this.mainBox.actor.style += `height: ${height}px`;
+ this.categoriesBox.box.width = 300;
}
selectCategory(dir) {
@@ -640,6 +723,20 @@ class ApplicationsButton extends PanelMe
this._displayButtons(this._listApplications(dir.get_menu_id()));
else
this._displayButtons(this._listApplications(null));
+
+ this.updateOrnament(this.categoriesBox, dir);
+ }
+
+ updateOrnament(menu_item, category_dir) {
+ menu_item._getMenuItems().forEach(sub_menu_item => {
+ if (sub_menu_item.menu != undefined)
+ this.updateOrnament(sub_menu_item.menu, category_dir);
+ if (sub_menu_item._category == category_dir) {
+ sub_menu_item.setOrnament(PopupMenu.Ornament.CHECK);
+ } else {
+ sub_menu_item.setOrnament(PopupMenu.Ornament.NONE);
+ }
+ }, this);
}
_displayButtons(apps) {