| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { $, addDisposableListener, EventType, isHTMLElement } from '../../../base/browser/dom.js';
- import { StandardMouseEvent } from '../../../base/browser/mouseEvent.js';
- import { Menu } from '../../../base/browser/ui/menu/menu.js';
- import { ActionRunner } from '../../../base/common/actions.js';
- import { isCancellationError } from '../../../base/common/errors.js';
- import { combinedDisposable, DisposableStore } from '../../../base/common/lifecycle.js';
- import { attachMenuStyler } from '../../theme/common/styler.js';
- export class ContextMenuHandler {
- constructor(contextViewService, telemetryService, notificationService, keybindingService, themeService) {
- this.contextViewService = contextViewService;
- this.telemetryService = telemetryService;
- this.notificationService = notificationService;
- this.keybindingService = keybindingService;
- this.themeService = themeService;
- this.focusToReturn = null;
- this.block = null;
- this.options = { blockMouse: true };
- }
- configure(options) {
- this.options = options;
- }
- showContextMenu(delegate) {
- const actions = delegate.getActions();
- if (!actions.length) {
- return; // Don't render an empty context menu
- }
- this.focusToReturn = document.activeElement;
- let menu;
- const shadowRootElement = isHTMLElement(delegate.domForShadowRoot) ? delegate.domForShadowRoot : undefined;
- this.contextViewService.showContextView({
- getAnchor: () => delegate.getAnchor(),
- canRelayout: false,
- anchorAlignment: delegate.anchorAlignment,
- anchorAxisAlignment: delegate.anchorAxisAlignment,
- render: (container) => {
- const className = delegate.getMenuClassName ? delegate.getMenuClassName() : '';
- if (className) {
- container.className += ' ' + className;
- }
- // Render invisible div to block mouse interaction in the rest of the UI
- if (this.options.blockMouse) {
- this.block = container.appendChild($('.context-view-block'));
- this.block.style.position = 'fixed';
- this.block.style.cursor = 'initial';
- this.block.style.left = '0';
- this.block.style.top = '0';
- this.block.style.width = '100%';
- this.block.style.height = '100%';
- this.block.style.zIndex = '-1';
- // TODO@Steven: this is never getting disposed
- addDisposableListener(this.block, EventType.MOUSE_DOWN, e => e.stopPropagation());
- }
- const menuDisposables = new DisposableStore();
- const actionRunner = delegate.actionRunner || new ActionRunner();
- actionRunner.onBeforeRun(this.onActionRun, this, menuDisposables);
- actionRunner.onDidRun(this.onDidActionRun, this, menuDisposables);
- menu = new Menu(container, actions, {
- actionViewItemProvider: delegate.getActionViewItem,
- context: delegate.getActionsContext ? delegate.getActionsContext() : null,
- actionRunner,
- getKeyBinding: delegate.getKeyBinding ? delegate.getKeyBinding : action => this.keybindingService.lookupKeybinding(action.id)
- });
- menuDisposables.add(attachMenuStyler(menu, this.themeService));
- menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables);
- menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables);
- menuDisposables.add(addDisposableListener(window, EventType.BLUR, () => this.contextViewService.hideContextView(true)));
- menuDisposables.add(addDisposableListener(window, EventType.MOUSE_DOWN, (e) => {
- if (e.defaultPrevented) {
- return;
- }
- const event = new StandardMouseEvent(e);
- let element = event.target;
- // Don't do anything as we are likely creating a context menu
- if (event.rightButton) {
- return;
- }
- while (element) {
- if (element === container) {
- return;
- }
- element = element.parentElement;
- }
- this.contextViewService.hideContextView(true);
- }));
- return combinedDisposable(menuDisposables, menu);
- },
- focus: () => {
- menu === null || menu === void 0 ? void 0 : menu.focus(!!delegate.autoSelectFirstItem);
- },
- onHide: (didCancel) => {
- var _a;
- (_a = delegate.onHide) === null || _a === void 0 ? void 0 : _a.call(delegate, !!didCancel);
- if (this.block) {
- this.block.remove();
- this.block = null;
- }
- if (this.focusToReturn) {
- this.focusToReturn.focus();
- }
- }
- }, shadowRootElement, !!shadowRootElement);
- }
- onActionRun(e) {
- this.telemetryService.publicLog2('workbenchActionExecuted', { id: e.action.id, from: 'contextMenu' });
- this.contextViewService.hideContextView(false);
- // Restore focus here
- if (this.focusToReturn) {
- this.focusToReturn.focus();
- }
- }
- onDidActionRun(e) {
- if (e.error && !isCancellationError(e.error)) {
- this.notificationService.error(e.error);
- }
- }
- }
|