Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(428)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui/SoftContextMenu.js

Issue 2790613003: Reland of [DevTools] Migrate SoftContextMenu to use GlassPane (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2011 Google Inc. All Rights Reserved. 2 * Copyright (C) 2011 Google Inc. All Rights Reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 22 matching lines...) Expand all
33 * @param {!UI.SoftContextMenu=} parentMenu 33 * @param {!UI.SoftContextMenu=} parentMenu
34 */ 34 */
35 constructor(items, itemSelectedCallback, parentMenu) { 35 constructor(items, itemSelectedCallback, parentMenu) {
36 this._items = items; 36 this._items = items;
37 this._itemSelectedCallback = itemSelectedCallback; 37 this._itemSelectedCallback = itemSelectedCallback;
38 this._parentMenu = parentMenu; 38 this._parentMenu = parentMenu;
39 } 39 }
40 40
41 /** 41 /**
42 * @param {!Document} document 42 * @param {!Document} document
43 * @param {number} x 43 * @param {!AnchorBox} anchorBox
44 * @param {number} y
45 */ 44 */
46 show(document, x, y) { 45 show(document, anchorBox) {
47 if (!this._items.length) 46 if (!this._items.length)
48 return; 47 return;
49 48
50 this._document = document; 49 this._document = document;
51 this._x = x;
52 this._y = y;
53 this._time = new Date().getTime();
54 50
55 // Create context menu. 51 this._glassPane = new UI.GlassPane();
56 this.element = createElementWithClass('div', 'soft-context-menu'); 52 this._glassPane.setBlockPointerEvents(!this._parentMenu);
57 var root = UI.createShadowRootWithCoreStyles(this.element, 'ui/softContextMe nu.css'); 53 this._glassPane.setSetOutsideClickCallback(event => {
58 this._contextMenuElement = root.createChild('div'); 54 this._discardMenu(true, event);
59 this.element.style.top = y + 'px'; 55 event.consume();
60 var subMenuOverlap = 3; 56 });
61 this.element.style.left = (this._parentMenu ? x - subMenuOverlap : x) + 'px' ; 57 this._glassPane.registerRequiredCSS('ui/softContextMenu.css');
58 this._glassPane.setContentAnchorBox(anchorBox);
59 this._glassPane.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent);
60 this._glassPane.setMarginBehavior(UI.GlassPane.MarginBehavior.NoMargin);
61 this._glassPane.setAnchorBehavior(
62 this._parentMenu ? UI.GlassPane.AnchorBehavior.PreferRight : UI.GlassPan e.AnchorBehavior.PreferBottom);
62 63
64 this._contextMenuElement = this._glassPane.contentElement.createChild('div', 'soft-context-menu');
63 this._contextMenuElement.tabIndex = 0; 65 this._contextMenuElement.tabIndex = 0;
64 this._contextMenuElement.addEventListener('mouseup', e => e.consume(), false ); 66 this._contextMenuElement.addEventListener('mouseup', e => e.consume(), false );
65 this._contextMenuElement.addEventListener('keydown', this._menuKeyDown.bind( this), false); 67 this._contextMenuElement.addEventListener('keydown', this._menuKeyDown.bind( this), false);
66 68
67 for (var i = 0; i < this._items.length; ++i) 69 for (var i = 0; i < this._items.length; ++i)
68 this._contextMenuElement.appendChild(this._createMenuItem(this._items[i])) ; 70 this._contextMenuElement.appendChild(this._createMenuItem(this._items[i])) ;
69 71
70 // Install glass pane capturing events. 72 this._glassPane.show(document);
71 if (!this._parentMenu) {
72 this._glassPaneElement = createElementWithClass('div', 'soft-context-menu- glass-pane fill');
73 this._glassPaneElement.tabIndex = 0;
74 this._glassPaneElement.style.zIndex = '20000';
75 this._glassPaneElement.addEventListener('mouseup', this._glassPaneMouseUp. bind(this), false);
76 this._glassPaneElement.appendChild(this.element);
77 document.body.appendChild(this._glassPaneElement);
78 this._discardMenuOnResizeListener = this._discardMenu.bind(this, true);
79 document.defaultView.addEventListener('resize', this._discardMenuOnResizeL istener, false);
80 } else {
81 this._parentMenu._parentGlassPaneElement().appendChild(this.element);
82 }
83
84 // Re-position menu in case it does not fit.
85 var containerElement = UI.GlassPane.container(document);
86 var hostLeft = containerElement.totalOffsetLeft();
87 var hostRight = hostLeft + containerElement.offsetWidth;
88 if (hostRight < this.element.offsetLeft + this.element.offsetWidth) {
89 var left = this._parentMenu ? this._parentMenu.element.offsetLeft - this.e lement.offsetWidth + subMenuOverlap :
90 hostRight - this.element.offsetWidth;
91 this.element.style.left = Math.max(hostLeft, left) + 'px';
92 }
93
94 // Move submenus upwards if it does not fit.
95 if (this._parentMenu && document.body.offsetHeight < this.element.offsetTop + this.element.offsetHeight) {
96 y = Math.max(containerElement.totalOffsetTop(), document.body.offsetHeight - this.element.offsetHeight);
97 this.element.style.top = y + 'px';
98 }
99
100 var maxHeight = containerElement.offsetHeight;
101 maxHeight -= y - containerElement.totalOffsetTop();
102 this.element.style.maxHeight = maxHeight + 'px';
103
104 this._focus(); 73 this._focus();
105 } 74 }
106 75
107 discard() { 76 discard() {
108 this._discardMenu(true); 77 this._discardMenu(true);
109 } 78 }
110 79
111 _parentGlassPaneElement() {
112 if (this._glassPaneElement)
113 return this._glassPaneElement;
114 if (this._parentMenu)
115 return this._parentMenu._parentGlassPaneElement();
116 return null;
117 }
118
119 _createMenuItem(item) { 80 _createMenuItem(item) {
120 if (item.type === 'separator') 81 if (item.type === 'separator')
121 return this._createSeparator(); 82 return this._createSeparator();
122 83
123 if (item.type === 'subMenu') 84 if (item.type === 'subMenu')
124 return this._createSubMenu(item); 85 return this._createSubMenu(item);
125 86
126 var menuItemElement = createElementWithClass('div', 'soft-context-menu-item' ); 87 var menuItemElement = createElementWithClass('div', 'soft-context-menu-item' );
127 var checkMarkElement = UI.Icon.create('smallicon-checkmark', 'checkmark'); 88 var checkMarkElement = UI.Icon.create('smallicon-checkmark', 'checkmark');
128 menuItemElement.appendChild(checkMarkElement); 89 menuItemElement.appendChild(checkMarkElement);
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 175
215 _showSubMenu(menuItemElement) { 176 _showSubMenu(menuItemElement) {
216 if (menuItemElement._subMenuTimer) { 177 if (menuItemElement._subMenuTimer) {
217 clearTimeout(menuItemElement._subMenuTimer); 178 clearTimeout(menuItemElement._subMenuTimer);
218 delete menuItemElement._subMenuTimer; 179 delete menuItemElement._subMenuTimer;
219 } 180 }
220 if (this._subMenu) 181 if (this._subMenu)
221 return; 182 return;
222 183
223 this._subMenu = new UI.SoftContextMenu(menuItemElement._subItems, this._item SelectedCallback, this); 184 this._subMenu = new UI.SoftContextMenu(menuItemElement._subItems, this._item SelectedCallback, this);
224 var topPadding = 4; 185 var anchorBox = menuItemElement.boxInWindow();
225 this._subMenu.show( 186 // Adjust for padding.
226 this._document, menuItemElement.totalOffsetLeft() + menuItemElement.offs etWidth, 187 anchorBox.y -= 5;
227 menuItemElement.totalOffsetTop() - 1 - topPadding); 188 anchorBox.x += 3;
189 anchorBox.width -= 6;
190 anchorBox.height += 10;
191 this._subMenu.show(this._document, anchorBox);
228 } 192 }
229 193
230 _hideSubMenu() { 194 _hideSubMenu() {
231 if (!this._subMenu) 195 if (!this._subMenu)
232 return; 196 return;
233 this._subMenu._discardSubMenus(); 197 this._subMenu._discardSubMenus();
234 this._focus(); 198 this._focus();
235 } 199 }
236 200
237 _menuItemMouseOver(event) { 201 _menuItemMouseOver(event) {
238 this._highlightMenuItem(event.target, true); 202 this._highlightMenuItem(event.target, true);
239 } 203 }
240 204
241 _menuItemMouseLeave(event) { 205 _menuItemMouseLeave(event) {
242 if (!this._subMenu || !event.relatedTarget) { 206 if (!this._subMenu || !event.relatedTarget) {
243 this._highlightMenuItem(null, true); 207 this._highlightMenuItem(null, true);
244 return; 208 return;
245 } 209 }
246 210
247 var relatedTarget = event.relatedTarget; 211 var relatedTarget = event.relatedTarget;
248 if (relatedTarget.classList.contains('soft-context-menu-glass-pane')) 212 if (relatedTarget === this._contextMenuElement)
249 this._highlightMenuItem(null, true); 213 this._highlightMenuItem(null, true);
250 } 214 }
251 215
252 /** 216 /**
253 * @param {?Element} menuItemElement 217 * @param {?Element} menuItemElement
254 * @param {boolean} scheduleSubMenu 218 * @param {boolean} scheduleSubMenu
255 */ 219 */
256 _highlightMenuItem(menuItemElement, scheduleSubMenu) { 220 _highlightMenuItem(menuItemElement, scheduleSubMenu) {
257 if (this._highlightedMenuItemElement === menuItemElement) 221 if (this._highlightedMenuItemElement === menuItemElement)
258 return; 222 return;
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 this._triggerAction(this._highlightedMenuItemElement, event); 296 this._triggerAction(this._highlightedMenuItemElement, event);
333 if (this._highlightedMenuItemElement._subItems) { 297 if (this._highlightedMenuItemElement._subItems) {
334 this._subMenu._focus(); 298 this._subMenu._focus();
335 this._subMenu._highlightNext(); 299 this._subMenu._highlightNext();
336 } 300 }
337 break; 301 break;
338 } 302 }
339 event.consume(true); 303 event.consume(true);
340 } 304 }
341 305
342 _glassPaneMouseUp(event) {
343 // Return if this is simple 'click', since dispatched on glass pane, can't u se 'click' event.
344 if (new Date().getTime() - this._time < 300)
345 return;
346 if (event.target === this.element)
347 return;
348 this._discardMenu(true, event);
349 event.consume();
350 }
351
352 /** 306 /**
353 * @param {boolean} closeParentMenus 307 * @param {boolean} closeParentMenus
354 * @param {!Event=} event 308 * @param {!Event=} event
355 */ 309 */
356 _discardMenu(closeParentMenus, event) { 310 _discardMenu(closeParentMenus, event) {
357 if (this._subMenu && !closeParentMenus) 311 if (this._subMenu && !closeParentMenus)
358 return; 312 return;
359 if (this._glassPaneElement) {
360 var glassPane = this._glassPaneElement;
361 delete this._glassPaneElement;
362 // This can re-enter discardMenu due to blur.
363 this._document.body.removeChild(glassPane);
364 if (this._parentMenu) {
365 delete this._parentMenu._subMenu;
366 if (closeParentMenus)
367 this._parentMenu._discardMenu(closeParentMenus, event);
368 else
369 this._parentMenu._focus();
370 }
371 313
372 if (event) 314 this._discardSubMenus();
373 event.consume(true); 315
374 } else if (this._parentMenu && this._contextMenuElement.parentElementOrShado wHost()) { 316 if (this._parentMenu) {
375 this._discardSubMenus();
376 if (closeParentMenus) 317 if (closeParentMenus)
377 this._parentMenu._discardMenu(closeParentMenus, event); 318 this._parentMenu._discardMenu(closeParentMenus, event);
378 else 319 else
379 this._parentMenu._focus(); 320 this._parentMenu._focus();
380 if (event)
381 event.consume(true);
382 } 321 }
383 if (this._discardMenuOnResizeListener) { 322
384 this._document.defaultView.removeEventListener('resize', this._discardMenu OnResizeListener, false); 323 if (event)
385 delete this._discardMenuOnResizeListener; 324 event.consume(true);
386 }
387 } 325 }
388 326
389 _discardSubMenus() { 327 _discardSubMenus() {
390 if (this._subMenu) 328 if (this._subMenu)
391 this._subMenu._discardSubMenus(); 329 this._subMenu._discardSubMenus();
392 if (this.element) 330 if (this._glassPane) {
393 this.element.remove(); 331 this._glassPane.hide();
332 delete this._glassPane;
333 }
394 if (this._parentMenu) 334 if (this._parentMenu)
395 delete this._parentMenu._subMenu; 335 delete this._parentMenu._subMenu;
396 } 336 }
397 }; 337 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698