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) {