Codebase list gnome-shell-extensions / 17de2bd
Add apps-menu-with-multiple-levels.patch to support nested menus. See https://bugs.kali.org/view.php?id=2223 Raphaël Hertzog 8 years ago
3 changed file(s) with 391 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 gnome-shell-extensions (3.14.2-1kali1) kali-dev; urgency=medium
1
2 * Add apps-menu-with-multiple-levels.patch to support nested menus.
3
4 -- Raphaël Hertzog <[email protected]> Mon, 18 May 2015 17:41:17 +0200
5
06 gnome-shell-extensions (3.14.2-1) unstable; urgency=medium
17
28 * New upstream bugfix release.
0 Description: Add support for nested menus
1 Author: Emilio Pozuelo Monfort <[email protected]>
2 Bug-Kali: https://bugs.kali.org/view.php?id=2223
3 Bug: https://bugzilla.gnome.org/show_bug.cgi?id=739480
4
5 --- a/extensions/apps-menu/extension.js
6 +++ b/extensions/apps-menu/extension.js
7 @@ -97,10 +97,9 @@ const ApplicationMenuItem = new Lang.Cla
8
9 const CategoryMenuItem = new Lang.Class({
10 Name: 'CategoryMenuItem',
11 - Extends: PopupMenu.PopupBaseMenuItem,
12 + Extends: PopupMenu.PopupMenuItem,
13
14 _init: function(button, category) {
15 - this.parent();
16 this._category = category;
17 this._button = button;
18
19 @@ -113,13 +112,126 @@ const CategoryMenuItem = new Lang.Class(
20 else
21 name = _("Favorites");
22
23 - this.actor.add_child(new St.Label({ text: name }));
24 - this.actor.connect('motion-event', Lang.bind(this, this._onMotionEvent));
25 + this.parent(name);
26 +
27 + //this.actor.connect('motion-event', Lang.bind(this, this._onMotionEvent));
28 + },
29 +
30 + activate: function(event) {
31 + this._button.selectCategory(this._category, this);
32 + //this._button.scrollToCatButton(this);
33 + // we don't chain up here so that clicking on a category doesn't
34 + // close the menu
35 + },
36 +
37 + _isNavigatingSubmenu: function([x, y]) {
38 + let [posX, posY] = this.actor.get_transformed_position();
39 +
40 + if (this._oldX == -1) {
41 + this._oldX = x;
42 + this._oldY = y;
43 + return true;
44 + }
45 +
46 + let deltaX = Math.abs(x - this._oldX);
47 + let deltaY = Math.abs(y - this._oldY);
48 +
49 + this._oldX = x;
50 + this._oldY = y;
51 +
52 + // If it lies outside the x-coordinates then it is definitely outside.
53 + if (posX > x || posX + this.actor.width < x)
54 + return false;
55 +
56 + // If it lies inside the menu item then it is definitely inside.
57 + if (posY <= y && posY + this.actor.height >= y)
58 + return true;
59 +
60 + // We want the keep-up triangle only if the movement is more
61 + // horizontal than vertical.
62 + if (deltaX * HORIZ_FACTOR < deltaY)
63 + return false;
64 +
65 + // Check whether the point lies inside triangle ABC, and a similar
66 + // triangle on the other side of the menu item.
67 + //
68 + // +---------------------+
69 + // | menu item |
70 + // A +---------------------+ C
71 + // P |
72 + // B
73 +
74 + // Ensure that the point P always lies below line AC so that we can
75 + // only check for triangle ABC.
76 + if (posY > y) {
77 + let offset = posY - y;
78 + y = posY + this.actor.height + offset;
79 + }
80 +
81 + // Ensure that A is (0, 0).
82 + x -= posX;
83 + y -= posY + this.actor.height;
84 +
85 + // Check which side of line AB the point P lies on by taking the
86 + // cross-product of AB and AP. See:
87 + // http://stackoverflow.com/questions/3461453/determine-which-side-of-a-line-a-point-lies
88 + if (((this.actor.width * y) - (NAVIGATION_REGION_OVERSHOOT * x)) <= 0)
89 + return true;
90 +
91 + return false;
92 + },
93 +
94 + _onMotionEvent: function(actor, event) {
95 + if (!Clutter.get_pointer_grab()) {
96 + this._oldX = -1;
97 + this._oldY = -1;
98 + Clutter.grab_pointer(this.actor);
99 + }
100 + this.actor.hover = true;
101 +
102 + if (this._isNavigatingSubmenu(event.get_coords()))
103 + return true;
104 +
105 + this._oldX = -1;
106 + this._oldY = -1;
107 + this.actor.hover = false;
108 + Clutter.ungrab_pointer();
109 + return false;
110 + },
111 +
112 + setActive: function(active, params) {
113 + if (active) {
114 + this._button.selectCategory(this._category, this);
115 + //this._button.scrollToCatButton(this);
116 + }
117 + this.parent(active, params);
118 + }
119 +});
120 +const ParentCategoryMenuItem = new Lang.Class({
121 + Name: 'ParentCategoryMenuItem',
122 + Extends: PopupMenu.PopupSubMenuMenuItem,
123 +
124 + _init: function(button, category) {
125 + this._category = category;
126 + this._button = button;
127 +
128 + this._oldX = -1;
129 + this._oldY = -1;
130 +
131 + let name;
132 + if (this._category)
133 + name = this._category.get_name();
134 + else
135 + name = _("Favorites");
136 +
137 + this.parent(name, false);
138 +
139 + //this.actor.connect('motion-event', Lang.bind(this, this._onMotionEvent));
140 },
141
142 activate: function(event) {
143 this._button.selectCategory(this._category, this);
144 - this._button.scrollToCatButton(this);
145 + //this._button.scrollToCatButton(this);
146 this.parent(event);
147 },
148
149 @@ -201,12 +313,31 @@ const CategoryMenuItem = new Lang.Class(
150 setActive: function(active, params) {
151 if (active) {
152 this._button.selectCategory(this._category, this);
153 - this._button.scrollToCatButton(this);
154 + //this._button.scrollToCatButton(this);
155 }
156 this.parent(active, params);
157 }
158 });
159
160 +const PopupMenuScrollView = new Lang.Class({
161 + Name: 'PopupMenuScrollView',
162 + Extends: PopupMenu.PopupMenuSection,
163 +
164 + _init: function() {
165 + this.parent();
166 +
167 + this.actor = new St.ScrollView({ style_class: 'vfade',
168 + hscrollbar_policy: Gtk.PolicyType.NEVER,
169 + vscrollbar_policy: Gtk.PolicyType.AUTOMATIC });
170 +
171 + this.container = new Shell.GenericContainer();
172 + this.box.add_actor(this.container);
173 + this.actor.add_actor(this.box);
174 + this.actor._delegate = this;
175 + this.actor.clip_to_allocation = true;
176 + },
177 +});
178 +
179 const HotCorner = new Lang.Class({
180 Name: 'HotCorner',
181 Extends: Layout.HotCorner,
182 @@ -305,7 +436,7 @@ const ApplicationsButton = new Lang.Clas
183 _installedChangedId = appSys.connect('installed-changed', Lang.bind(this, function() {
184 if (this.menu.isOpen) {
185 this._redisplay();
186 - this.mainBox.show();
187 + this.mainBox.actor.show();
188 } else {
189 this.reloadFlag = true;
190 }
191 @@ -370,18 +501,19 @@ const ApplicationsButton = new Lang.Clas
192 this._redisplay();
193 this.reloadFlag = false;
194 }
195 - this.mainBox.show();
196 + //this.categoriesBox.box.width = this._menuContainerGetPreferredWidth(this.categoriesBox.box);
197 + this.mainBox.actor.show();
198 }
199 this.parent(menu, open);
200 },
201
202 _redisplay: function() {
203 this.applicationsBox.destroy_all_children();
204 - this.categoriesBox.destroy_all_children();
205 + this.categoriesBox.actor.destroy_all_children();
206 this._display();
207 },
208
209 - _loadCategory: function(categoryId, dir) {
210 + _loadCategory: function(dir, parentCategory) {
211 let iter = dir.iter();
212 let nextType;
213 while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
214 @@ -391,12 +523,21 @@ const ApplicationsButton = new Lang.Clas
215 let app = appSys.lookup_app(entry.get_desktop_file_id());
216 if (appInfo.should_show()) {
217 let menu_id = dir.get_menu_id();
218 - this.applicationsByCategory[categoryId].push(app);
219 + this.applicationsByCategory[menu_id].push(app);
220 }
221 } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
222 let subdir = iter.get_directory();
223 - if (!subdir.get_is_nodisplay())
224 - this._loadCategory(categoryId, subdir);
225 + if (!subdir.get_is_nodisplay()) {
226 + let menu_id = subdir.get_menu_id();
227 + this.applicationsByCategory[menu_id] = [];
228 + let categoryMenuItem = new ParentCategoryMenuItem(this, subdir);
229 + this._loadCategory(subdir, categoryMenuItem);
230 + if (categoryMenuItem.menu.isEmpty())
231 + categoryMenuItem = new CategoryMenuItem(this, subdir);
232 + if (this.applicationsByCategory[menu_id].length > 0 || !categoryMenuItem.menu.isEmpty()) {
233 + parentCategory.menu.addMenuItem(categoryMenuItem);
234 + }
235 + }
236 }
237 }
238 },
239 @@ -417,8 +558,8 @@ const ApplicationsButton = new Lang.Clas
240 },
241
242 scrollToCatButton: function(button) {
243 - let catsScrollBoxAdj = this.categoriesScrollBox.get_vscroll_bar().get_adjustment();
244 - let catsScrollBoxAlloc = this.categoriesScrollBox.get_allocation_box();
245 + let catsScrollBoxAdj = this.categoriesBox.actor.get_vscroll_bar().get_adjustment();
246 + let catsScrollBoxAlloc = this.categoriesBox.actor.get_allocation_box();
247 let currentScrollValue = catsScrollBoxAdj.get_value();
248 let boxHeight = catsScrollBoxAlloc.y2 - catsScrollBoxAlloc.y1;
249 let buttonAlloc = button.actor.get_allocation_box();
250 @@ -432,10 +573,16 @@ const ApplicationsButton = new Lang.Clas
251 },
252
253 _createLayout: function() {
254 + // https://mail.gnome.org/archives/gnome-shell-list/2014-January/msg00010.html
255 + this.menu._setOpenedSubMenu = Lang.bind(this, function(submenu) {
256 + this._openedSubMenu = submenu;
257 + });
258 +
259 let section = new PopupMenu.PopupMenuSection();
260 this.menu.addMenuItem(section);
261 - this.mainBox = new St.BoxLayout({ vertical: false });
262 - this.leftBox = new St.BoxLayout({ vertical: true });
263 + this.mainBox = new PopupMenu.PopupMenuSection();
264 + this.mainBox.actor.vertical = false;
265 + this.leftBox = new PopupMenu.PopupMenuSection();
266 this.applicationsScrollBox = new St.ScrollView({ x_fill: true, y_fill: false,
267 y_align: St.Align.START,
268 style_class: 'apps-menu vfade' });
269 @@ -447,41 +594,52 @@ const ApplicationsButton = new Lang.Clas
270 vscroll.connect('scroll-stop', Lang.bind(this, function() {
271 this.menu.passEvents = false;
272 }));
273 - this.categoriesScrollBox = new St.ScrollView({ x_fill: true, y_fill: false,
274 - y_align: St.Align.START,
275 - style_class: 'vfade' });
276 - this.categoriesScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
277 - vscroll = this.categoriesScrollBox.get_vscroll_bar();
278 +
279 + let activities = new ActivitiesMenuItem(this);
280 +
281 + this.applicationsBox = new St.BoxLayout({ vertical: true });
282 + this.applicationsScrollBox.add_actor(this.applicationsBox);
283 +
284 + this.categoriesBox = new PopupMenuScrollView();
285 + vscroll = this.categoriesBox.actor.get_vscroll_bar();
286 vscroll.connect('scroll-start', Lang.bind(this, function() {
287 this.menu.passEvents = true;
288 }));
289 vscroll.connect('scroll-stop', Lang.bind(this, function() {
290 this.menu.passEvents = false;
291 }));
292 - this.leftBox.add(this.categoriesScrollBox, { expand: true,
293 - x_fill: true, y_fill: true,
294 - y_align: St.Align.START });
295 -
296 - let activities = new ActivitiesMenuItem(this);
297 - this.leftBox.add(activities.actor, { expand: false,
298 - x_fill: true, y_fill: false,
299 - y_align: St.Align.START });
300 + this.leftBox.addMenuItem(this.categoriesBox);
301 + // FIXME we re-add it to apply the right properties, but re-adding it causes a warning
302 + this.leftBox.actor.add(this.categoriesBox.actor, { expand: true, x_fill: true, y_fill: true, y_align: St.Align.START });
303 +
304 + this.leftBox.actor.add(activities.actor, { expand: false,
305 + x_fill: true, y_fill: false,
306 + y_align: St.Align.START });
307 +
308 + this.mainBox.addMenuItem(this.leftBox);
309 + this.mainBox.actor.add(this._createVertSeparator(), { expand: false, x_fill: false, y_fill: true});
310 + this.mainBox.actor.add(this.applicationsScrollBox, { expand: true, x_fill: true, y_fill: true });
311 + section.addMenuItem(this.mainBox);
312 + },
313 +
314 + _menuContainerGetPreferredWidth: function(container) {
315 + let max_width = 0;
316 + for (let child = container.get_first_child();
317 + child;
318 + child = child.get_next_sibling()) {
319 + // recurse into submenus
320 + if (child._delegate instanceof ParentCategoryMenuItem)
321 + max_width = Math.max(max_width, this._menuContainerGetPreferredWidth(child._delegate.menu.box));
322
323 - this.applicationsBox = new St.BoxLayout({ vertical: true });
324 - this.applicationsScrollBox.add_actor(this.applicationsBox);
325 - this.categoriesBox = new St.BoxLayout({ vertical: true });
326 - this.categoriesScrollBox.add_actor(this.categoriesBox, { expand: true, x_fill: false });
327 -
328 - this.mainBox.add(this.leftBox);
329 - this.mainBox.add(this._createVertSeparator(), { expand: false, x_fill: false, y_fill: true});
330 - this.mainBox.add(this.applicationsScrollBox, { expand: true, x_fill: true, y_fill: true });
331 - section.actor.add_actor(this.mainBox);
332 + max_width = Math.max(max_width, child.width);
333 + }
334 + return max_width;
335 },
336
337 _display: function() {
338 this._applicationsButtons = new Array();
339 - this.mainBox.style=('width: 640px;');
340 - this.mainBox.hide();
341 + this.mainBox.actor.style=('width: 640px;');
342 + this.mainBox.actor.hide();
343
344 //Load categories
345 this.applicationsByCategory = {};
346 @@ -489,7 +647,7 @@ const ApplicationsButton = new Lang.Clas
347 tree.load_sync();
348 let root = tree.get_root_directory();
349 let categoryMenuItem = new CategoryMenuItem(this, null);
350 - this.categoriesBox.add_actor(categoryMenuItem.actor);
351 + this.categoriesBox.addMenuItem(categoryMenuItem);
352 let iter = root.iter();
353 let nextType;
354 while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
355 @@ -498,10 +656,12 @@ const ApplicationsButton = new Lang.Clas
356 if (!dir.get_is_nodisplay()) {
357 let categoryId = dir.get_menu_id();
358 this.applicationsByCategory[categoryId] = [];
359 - this._loadCategory(categoryId, dir);
360 - if (this.applicationsByCategory[categoryId].length > 0) {
361 - let categoryMenuItem = new CategoryMenuItem(this, dir);
362 - this.categoriesBox.add_actor(categoryMenuItem.actor);
363 + let categoryMenuItem = new ParentCategoryMenuItem(this, dir);
364 + this._loadCategory(dir, categoryMenuItem);
365 + if (categoryMenuItem.menu.isEmpty())
366 + categoryMenuItem = new CategoryMenuItem(this, dir);
367 + if (this.applicationsByCategory[categoryId].length > 0 || !categoryMenuItem.menu.isEmpty()) {
368 + this.categoriesBox.addMenuItem(categoryMenuItem);
369 }
370 }
371 }
372 @@ -510,8 +670,9 @@ const ApplicationsButton = new Lang.Clas
373 //Load applications
374 this._displayButtons(this._listApplications(null));
375
376 - let height = this.categoriesBox.height + MENU_HEIGHT_OFFSET + 'px';
377 - this.mainBox.style+=('height: ' + height);
378 + let height = this.categoriesBox.actor.height + MENU_HEIGHT_OFFSET + 'px';
379 + this.mainBox.actor.style+=('height: ' + height);
380 + this.categoriesBox.box.width = 220;
381 },
382
383 _clearApplicationsBox: function(selectedActor) {
22 menu-arrows-icons.patch
33 apps-center-labels.patch
44 window-list-pointerInNotification.patch
5 apps-menu-with-multiple-levels.patch