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 | 152 +++++++++++++++++++++++++++++---------
1 file changed, 119 insertions(+), 33 deletions(-)
--- a/extensions/apps-menu/extension.js
+++ b/extensions/apps-menu/extension.js
@@ -98,34 +98,40 @@
}
}
-class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
+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 */
+
static {
GObject.registerClass(this);
}
constructor(button, category) {
- super();
- this._category = category;
- this._button = button;
-
- this._oldX = -1;
- this._oldY = -1;
-
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(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]) {
@@ -218,10 +224,63 @@
return;
this._button.selectCategory(this._category);
+ //this._button.scrollToCatButton(this);
+ }
+}
+
+class ParentCategoryMenuItem extends PopupMenu.PopupSubMenuMenuItem {
+ static {
+ GObject.registerClass(this);
+ }
+
+ constructor(button, category) {
+ let name;
+ if (category)
+ name = category.get_name();
+ else
+ name = _("Favorites");
+
+ super(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);
@@ -424,7 +483,7 @@
_onTreeChanged() {
if (this.menu.isOpen) {
this._redisplay();
- this.mainBox.show();
+ this.mainBox.actor.show();
} else {
this.reloadFlag = true;
}
@@ -485,20 +544,21 @@
this._redisplay();
this.reloadFlag = false;
}
- this.mainBox.show();
+ this.mainBox.actor.show();
}
super._onOpenStateChanged(menu, open);
}
_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();
@@ -517,8 +577,17 @@
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);
+ }
+ }
}
}
}
@@ -539,8 +608,8 @@
}
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();
@@ -556,8 +625,9 @@
_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,
@@ -570,6 +640,7 @@
vscroll.connect('scroll-stop', () => {
this.menu.passEvents = false;
});
+/*
this.categoriesScrollBox = new St.ScrollView({
style_class: 'vfade',
});
@@ -578,9 +649,10 @@
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);
@@ -588,19 +660,29 @@
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.applicationsScrollBox.actor.style = 'width: 22em;';
+ 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) {
@@ -613,10 +695,13 @@
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);
}
}
@@ -625,9 +710,10 @@
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) {