Codebase list gnome-shell-extensions / 1a98714 debian / patches / apps-menu-with-multiple-levels.patch
1a98714

Tree @1a98714 (Download .tar.gz)

apps-menu-with-multiple-levels.patch @1a98714raw · history · blame

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 | 166 ++++++++++++++++++++++++++++++--------
 1 file changed, 134 insertions(+), 32 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,33 @@ 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(this.categoriesBox.actor, { expand: true,
+                                                           x_fill: true, y_fill: true,
+                                                           y_align: St.Align.START });
+        this.mainBox.addMenuItem(this.leftBox);
+        this.mainBox.actor.add(this._createVertSeparator(), { expand: false,
+                                                              x_fill: false, y_fill: true});
+        this.mainBox.actor.add(this.applicationsScrollBox, { expand: true,
+                                                             x_fill: true, y_fill: true });
+        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 +694,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 +709,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 +727,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) {