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

Tree @b2d26cf (Download .tar.gz)

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

Description: Add support for nested menus
Author: Emilio Pozuelo Monfort <[email protected]>
Bug-Kali: https://bugs.kali.org/view.php?id=2223
Bug: https://bugzilla.gnome.org/show_bug.cgi?id=739480

--- a/extensions/apps-menu/extension.js
+++ b/extensions/apps-menu/extension.js
@@ -114,29 +114,33 @@ class ApplicationMenuItem extends PopupM
     }
 };
 
-class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
-    constructor(button, category) {
-        super();
-        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 */
 
+    constructor(button, category) {
         let name;
-        if (this._category)
-            name = this._category.get_name();
+        if (category)
+            name = category.get_name();
         else
             name = _("Favorites");
 
-        this.actor.add_child(new St.Label({ text: name }));
-        this.actor.connect('motion-event', this._onMotionEvent.bind(this));
+        super(name);
+
+        this._category = category;
+        this._button = button;
     }
 
     activate(event) {
         this._button.selectCategory(this._category, this);
-        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]) {
@@ -222,12 +226,55 @@ class CategoryMenuItem extends PopupMenu
     setActive(active, params) {
         if (active) {
             this._button.selectCategory(this._category, this);
-            this._button.scrollToCatButton(this);
+            //this._button.scrollToCatButton(this);
         }
         super.setActive(active, params);
     }
 };
 
+class ParentCategoryMenuItem extends PopupMenu.PopupSubMenuMenuItem {
+    constructor(button, category) {
+        let name;
+        if (category)
+            name = category.get_name();
+        else
+            name = _("Favorites");
+
+        super(name, false);
+
+        this._category = category;
+        this._button = button;
+    }
+
+    activate(event) {
+        this._button.selectCategory(this._category, this);
+        this._button.scrollToCatButton(this);
+        super.activate(event);
+    }
+
+    setActive(active, params) {
+        if (active) {
+            this._button.selectCategory(this._category, this);
+            //this._button.scrollToCatButton(this);
+        }
+        super.setActive(active, params);
+    }
+};
+
+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;
+    }
+};
+
 class ApplicationsMenu extends PopupMenu.PopupMenu {
     constructor(sourceActor, arrowAlignment, arrowSide, button) {
         super(sourceActor, arrowAlignment, arrowSide);
@@ -462,7 +509,7 @@ class ApplicationsButton extends PanelMe
     _onTreeChanged() {
         if (this.menu.isOpen) {
             this._redisplay();
-            this.mainBox.show();
+            this.mainBox.actor.show();
         } else {
             this.reloadFlag = true;
         }
@@ -536,7 +583,7 @@ class ApplicationsButton extends PanelMe
                this._redisplay();
                this.reloadFlag = false;
            }
-           this.mainBox.show();
+           this.mainBox.actor.show();
        }
        super._onOpenStateChanged(menu, open);
     }
@@ -550,13 +597,14 @@ class ApplicationsButton extends PanelMe
 
     _redisplay() {
         this.applicationsBox.destroy_all_children();
-        this.categoriesBox.destroy_all_children();
+        this.categoriesBox.box.destroy_all_children();
         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();
@@ -575,8 +623,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);
+                    }
+                }
             }
         }
     }
@@ -597,8 +654,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.actor.get_allocation_box();
@@ -612,10 +669,16 @@ class ApplicationsButton extends PanelMe
     }
 
     _createLayout() {
+        // https://mail.gnome.org/archives/gnome-shell-list/2014-January/msg00010.html
+        this.menu._setOpenedSubMenu = (submenu) => {
+            this._openedSubMenu = submenu;
+        };
+
         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({ x_fill: true, y_fill: false,
                                                          y_align: St.Align.START,
                                                          style_class: 'apps-menu vfade' });
@@ -627,6 +690,7 @@ class ApplicationsButton extends PanelMe
         vscroll.connect('scroll-stop', () => {
             this.menu.passEvents = false;
         });
+/*
         this.categoriesScrollBox = new St.ScrollView({ x_fill: true, y_fill: false,
                                                        y_align: St.Align.START,
                                                        style_class: 'vfade' });
@@ -637,14 +701,17 @@ class ApplicationsButton extends PanelMe
         this.leftBox.add(this.categoriesScrollBox, { expand: true,
                                                      x_fill: true, y_fill: true,
                                                      y_align: St.Align.START });
-
+*/
         let activities = new ActivitiesMenuItem(this);
+/*
         this.leftBox.add(activities.actor, { expand: false,
                                              x_fill: true, y_fill: false,
                                              y_align: St.Align.START });
+*/
 
         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);
 
@@ -652,19 +719,39 @@ class ApplicationsButton extends PanelMe
         this.mainBox.add(this._createVertSeparator(), { expand: false, x_fill: false, y_fill: true});
         this.mainBox.add(this.applicationsScrollBox, { expand: true, x_fill: true, y_fill: true });
         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.addMenuItem(this.categoriesBox);
+        // we re-add it to apply the right properties
+        this.leftBox.actor.add(this.categoriesBox.actor, { expand: true,
+                                                           x_fill: true, y_fill: true,
+                                                           y_align: St.Align.START });
+
+        this.leftBox.actor.add(activities.actor, { expand: false,
+                                                   x_fill: true, y_fill: false,
+                                                   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.actor);
+        this.categoriesBox.addMenuItem(categoryMenuItem);
         let iter = root.iter();
         let nextType;
         while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
@@ -673,10 +760,12 @@ class ApplicationsButton extends PanelMe
                 if (!dir.get_is_nodisplay()) {
                     let categoryId = dir.get_menu_id();
                     this.applicationsByCategory[categoryId] = [];
-                    this._loadCategory(categoryId, dir);
-                    if (this.applicationsByCategory[categoryId].length > 0) {
-                        let categoryMenuItem = new CategoryMenuItem(this, dir);
-                        this.categoriesBox.add_actor(categoryMenuItem.actor);
+                    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);
                     }
                 }
             }
@@ -685,8 +774,9 @@ class ApplicationsButton extends PanelMe
         //Load applications
         this._displayButtons(this._listApplications(null));
 
-        let height = this.categoriesBox.height + MENU_HEIGHT_OFFSET + 'px';
-        this.mainBox.style+=('height: ' + height);
+        let height = this.categoriesBox.actor.height + MENU_HEIGHT_OFFSET + 'px';
+        this.mainBox.actor.style+=('height: ' + height);
+        this.categoriesBox.box.width = 300;
     }
 
     selectCategory(dir, categoryMenuItem) {
@@ -701,6 +791,21 @@ class ApplicationsButton extends PanelMe
             this._displayButtons(this._listApplications(dir.get_menu_id()));
         else
             this._displayButtons(this._listApplications(null));
+
+        if (categoryMenuItem)
+            this.updateOrnament(this.categoriesBox, categoryMenuItem);
+    }
+
+    updateOrnament(menu_item, category_menu_item) {
+        menu_item._getMenuItems().forEach(sub_menu_item => {
+            if (sub_menu_item.menu != undefined)
+                this.updateOrnament(sub_menu_item.menu, category_menu_item);
+            if (sub_menu_item == category_menu_item) {
+                sub_menu_item.setOrnament(PopupMenu.Ornament.CHECK);
+            } else {
+                sub_menu_item.setOrnament(PopupMenu.Ornament.NONE);
+            }
+        }, this);
     }
 
     _displayButtons(apps) {