UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

325 lines (284 loc) 12.1 kB
/* * Copyright (C) 2011 Google Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @unrestricted */ UI.SoftContextMenu = class { /** * @param {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>} items * @param {function(string)} itemSelectedCallback * @param {!UI.SoftContextMenu=} parentMenu */ constructor(items, itemSelectedCallback, parentMenu) { this._items = items; this._itemSelectedCallback = itemSelectedCallback; this._parentMenu = parentMenu; } /** * @param {!Document} document * @param {!AnchorBox} anchorBox */ show(document, anchorBox) { if (!this._items.length) return; this._document = document; this._glassPane = new UI.GlassPane(); this._glassPane.setPointerEventsBehavior( this._parentMenu ? UI.GlassPane.PointerEventsBehavior.PierceGlassPane : UI.GlassPane.PointerEventsBehavior.BlockedByGlassPane); this._glassPane.registerRequiredCSS('ui/softContextMenu.css'); this._glassPane.setContentAnchorBox(anchorBox); this._glassPane.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent); this._glassPane.setMarginBehavior(UI.GlassPane.MarginBehavior.NoMargin); this._glassPane.setAnchorBehavior( this._parentMenu ? UI.GlassPane.AnchorBehavior.PreferRight : UI.GlassPane.AnchorBehavior.PreferBottom); this._contextMenuElement = this._glassPane.contentElement.createChild('div', 'soft-context-menu'); this._contextMenuElement.tabIndex = 0; this._contextMenuElement.addEventListener('mouseup', e => e.consume(), false); this._contextMenuElement.addEventListener('keydown', this._menuKeyDown.bind(this), false); for (let i = 0; i < this._items.length; ++i) this._contextMenuElement.appendChild(this._createMenuItem(this._items[i])); this._glassPane.show(document); this._focusRestorer = new UI.ElementFocusRestorer(this._contextMenuElement); if (!this._parentMenu) { this._hideOnUserGesture = event => { this.discard(); event.consume(true); }; this._document.body.addEventListener('mousedown', this._hideOnUserGesture, false); this._document.defaultView.addEventListener('resize', this._hideOnUserGesture, false); } } discard() { if (this._subMenu) this._subMenu.discard(); if (this._focusRestorer) this._focusRestorer.restore(); if (this._glassPane) { this._glassPane.hide(); delete this._glassPane; if (this._hideOnUserGesture) { this._document.body.removeEventListener('mousedown', this._hideOnUserGesture, false); this._document.defaultView.removeEventListener('resize', this._hideOnUserGesture, false); delete this._hideOnUserGesture; } } if (this._parentMenu) delete this._parentMenu._subMenu; } _createMenuItem(item) { if (item.type === 'separator') return this._createSeparator(); if (item.type === 'subMenu') return this._createSubMenu(item); const menuItemElement = createElementWithClass('div', 'soft-context-menu-item'); const checkMarkElement = UI.Icon.create('smallicon-checkmark', 'checkmark'); menuItemElement.appendChild(checkMarkElement); if (!item.checked) checkMarkElement.style.opacity = '0'; if (item.element) { const wrapper = menuItemElement.createChild('div', 'soft-context-menu-custom-item'); wrapper.appendChild(item.element); menuItemElement._isCustom = true; return menuItemElement; } if (!item.enabled) menuItemElement.classList.add('soft-context-menu-disabled'); menuItemElement.createTextChild(item.label); menuItemElement.createChild('span', 'soft-context-menu-shortcut').textContent = item.shortcut; menuItemElement.addEventListener('mousedown', this._menuItemMouseDown.bind(this), false); menuItemElement.addEventListener('mouseup', this._menuItemMouseUp.bind(this), false); // Manually manage hover highlight since :hover does not work in case of click-and-hold menu invocation. menuItemElement.addEventListener('mouseover', this._menuItemMouseOver.bind(this), false); menuItemElement.addEventListener('mouseleave', this._menuItemMouseLeave.bind(this), false); menuItemElement._actionId = item.id; return menuItemElement; } _createSubMenu(item) { const menuItemElement = createElementWithClass('div', 'soft-context-menu-item'); menuItemElement._subItems = item.subItems; // Occupy the same space on the left in all items. const checkMarkElement = UI.Icon.create('smallicon-checkmark', 'soft-context-menu-item-checkmark'); checkMarkElement.classList.add('checkmark'); menuItemElement.appendChild(checkMarkElement); checkMarkElement.style.opacity = '0'; menuItemElement.createTextChild(item.label); const subMenuArrowElement = menuItemElement.createChild('span', 'soft-context-menu-item-submenu-arrow'); subMenuArrowElement.textContent = '\u25B6'; // BLACK RIGHT-POINTING TRIANGLE menuItemElement.addEventListener('mousedown', this._menuItemMouseDown.bind(this), false); menuItemElement.addEventListener('mouseup', this._menuItemMouseUp.bind(this), false); // Manually manage hover highlight since :hover does not work in case of click-and-hold menu invocation. menuItemElement.addEventListener('mouseover', this._menuItemMouseOver.bind(this), false); menuItemElement.addEventListener('mouseleave', this._menuItemMouseLeave.bind(this), false); return menuItemElement; } _createSeparator() { const separatorElement = createElementWithClass('div', 'soft-context-menu-separator'); separatorElement._isSeparator = true; separatorElement.createChild('div', 'separator-line'); return separatorElement; } _menuItemMouseDown(event) { // Do not let separator's mouse down hit menu's handler - we need to receive mouse up! event.consume(true); } _menuItemMouseUp(event) { this._triggerAction(event.target, event); event.consume(); } /** * @return {!UI.SoftContextMenu} */ _root() { let root = this; while (root._parentMenu) root = root._parentMenu; return root; } _triggerAction(menuItemElement, event) { if (!menuItemElement._subItems) { this._root().discard(); event.consume(true); if (typeof menuItemElement._actionId !== 'undefined') { this._itemSelectedCallback(menuItemElement._actionId); delete menuItemElement._actionId; } return; } this._showSubMenu(menuItemElement); event.consume(); } _showSubMenu(menuItemElement) { if (menuItemElement._subMenuTimer) { clearTimeout(menuItemElement._subMenuTimer); delete menuItemElement._subMenuTimer; } if (this._subMenu) return; this._subMenu = new UI.SoftContextMenu(menuItemElement._subItems, this._itemSelectedCallback, this); const anchorBox = menuItemElement.boxInWindow(); // Adjust for padding. anchorBox.y -= 5; anchorBox.x += 3; anchorBox.width -= 6; anchorBox.height += 10; this._subMenu.show(this._document, anchorBox); } _menuItemMouseOver(event) { this._highlightMenuItem(event.target, true); } _menuItemMouseLeave(event) { if (!this._subMenu || !event.relatedTarget) { this._highlightMenuItem(null, true); return; } const relatedTarget = event.relatedTarget; if (relatedTarget === this._contextMenuElement) this._highlightMenuItem(null, true); } /** * @param {?Element} menuItemElement * @param {boolean} scheduleSubMenu */ _highlightMenuItem(menuItemElement, scheduleSubMenu) { if (this._highlightedMenuItemElement === menuItemElement) return; if (this._subMenu) this._subMenu.discard(); if (this._highlightedMenuItemElement) { this._highlightedMenuItemElement.classList.remove('force-white-icons'); this._highlightedMenuItemElement.classList.remove('soft-context-menu-item-mouse-over'); if (this._highlightedMenuItemElement._subItems && this._highlightedMenuItemElement._subMenuTimer) { clearTimeout(this._highlightedMenuItemElement._subMenuTimer); delete this._highlightedMenuItemElement._subMenuTimer; } } this._highlightedMenuItemElement = menuItemElement; if (this._highlightedMenuItemElement) { this._highlightedMenuItemElement.classList.add('force-white-icons'); this._highlightedMenuItemElement.classList.add('soft-context-menu-item-mouse-over'); this._contextMenuElement.focus(); if (scheduleSubMenu && this._highlightedMenuItemElement._subItems && !this._highlightedMenuItemElement._subMenuTimer) { this._highlightedMenuItemElement._subMenuTimer = setTimeout(this._showSubMenu.bind(this, this._highlightedMenuItemElement), 150); } } } _highlightPrevious() { let menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.previousSibling : this._contextMenuElement.lastChild; while (menuItemElement && (menuItemElement._isSeparator || menuItemElement._isCustom)) menuItemElement = menuItemElement.previousSibling; if (menuItemElement) this._highlightMenuItem(menuItemElement, false); } _highlightNext() { let menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.nextSibling : this._contextMenuElement.firstChild; while (menuItemElement && (menuItemElement._isSeparator || menuItemElement._isCustom)) menuItemElement = menuItemElement.nextSibling; if (menuItemElement) this._highlightMenuItem(menuItemElement, false); } _menuKeyDown(event) { switch (event.key) { case 'ArrowUp': this._highlightPrevious(); break; case 'ArrowDown': this._highlightNext(); break; case 'ArrowLeft': if (this._parentMenu) { this._highlightMenuItem(null, false); this.discard(); } break; case 'ArrowRight': if (!this._highlightedMenuItemElement) break; if (this._highlightedMenuItemElement._subItems) { this._showSubMenu(this._highlightedMenuItemElement); this._subMenu._highlightNext(); } break; case 'Escape': this.discard(); break; case 'Enter': if (!isEnterKey(event)) break; // Fall through case ' ': // Space if (this._highlightedMenuItemElement) this._triggerAction(this._highlightedMenuItemElement, event); if (this._highlightedMenuItemElement._subItems) this._subMenu._highlightNext(); break; } event.consume(true); } };