diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index fb1800047c56a49292da65008b41d2497969f447..add0f1682938e0ced1e05a678da5b421c387c66f 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -44,6 +44,7 @@ export interface IListGestureEvent { } export interface IListContextMenuEvent { + browserEvent: UIEvent; element: T; index: number; anchor: HTMLElement | { x: number; y: number; }; diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 780c6396561ceeb11f37affe95333996e9fbaa8b..14bf8b9404c4e55091d270516e28cdb9b328e75d 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -124,7 +124,7 @@ export class PagedList implements IDisposable { } get onContextMenu(): Event> { - return mapEvent(this.list.onContextMenu, ({ element, index, anchor }) => ({ element: this._model.get(element), index, anchor })); + return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => ({ element: this._model.get(element), index, anchor, browserEvent })); } get model(): IPagedModel { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 39ba0119f842ce90497ddb5fd2f4f3724ee93da4..47631a32dac87ee350f77c74a015cd3f6a8e67e3 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -418,7 +418,13 @@ class MouseController implements IDisposable { .map(e => new StandardKeyboardEvent(e)) .filter(e => this.didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) .filter(e => { e.preventDefault(); e.stopPropagation(); return false; }) - .event as Event; + .map(event => { + const index = this.list.getFocus()[0]; + const element = this.view.element(index); + const anchor = this.view.domElement(index); + return { index, element, anchor, browserEvent: event.browserEvent }; + }) + .event; const fromKeyup = chain(domEvent(this.view.domNode, 'keyup')) .filter(() => { @@ -427,18 +433,18 @@ class MouseController implements IDisposable { return didJustPressContextMenuKey; }) .filter(() => this.list.getFocus().length > 0) - .map(() => { + .map(browserEvent => { const index = this.list.getFocus()[0]; const element = this.view.element(index); const anchor = this.view.domElement(index); - return { index, element, anchor }; + return { index, element, anchor, browserEvent }; }) .filter(({ anchor }) => !!anchor) .event; const fromMouse = chain(this.view.onContextMenu) .filter(() => !this.didJustPressContextMenuKey) - .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY } })) + .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY }, browserEvent })) .event; return anyEvent>(fromKeydown, fromKeyup, fromMouse); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index d47a7ac845e5ab6c3ae954e2eb83b5cea17ecead..feaf5c7fd6e66c92b85d34d61e006f20ab46666d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -6,7 +6,7 @@ import 'vs/css!./tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IListOptions, List, IIdentityProvider, IMultipleSelectionController, IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { IVirtualDelegate, IRenderer, IListMouseEvent, IListEvent } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { append, $ } from 'vs/base/browser/dom'; import { Event, Relay, chain } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -161,6 +161,7 @@ function isInputElement(e: HTMLElement): boolean { export interface ITreeOptions extends IListOptions, IIndexTreeModelOptions { } export interface ITreeEvent extends IListEvent> { } +export interface ITreeContextMenuEvent extends IListContextMenuEvent> { } export abstract class AbstractTree implements IDisposable { @@ -173,6 +174,8 @@ export abstract class AbstractTree implements IDisposable readonly onDidChangeFocus: Event>; readonly onDidChangeSelection: Event>; + readonly onContextMenu: Event>; + get onDidFocus(): Event { return this.view.onDidFocus; } get onDidBlur(): Event { return this.view.onDidBlur; } get onDidDispose(): Event { return this.view.onDidDispose; } @@ -192,6 +195,7 @@ export abstract class AbstractTree implements IDisposable this.view = new List(container, treeDelegate, treeRenderers, createComposedTreeListOptions>(options)); this.onDidChangeFocus = this.view.onFocusChange; this.onDidChangeSelection = this.view.onSelectionChange; + this.onContextMenu = this.view.onContextMenu; this.model = this.createModel(this.view, options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 96b1e48f27de74496db4ddf4150f3af702778fe0..5620e3a685f590c32a625ca413f1f8a56ad44665 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -14,7 +14,6 @@ import { Panel } from 'vs/workbench/browser/panel'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/parts/markers/electron-browser/constants'; import { Marker, ResourceMarkers, RelatedInformation, MarkersModel } from 'vs/workbench/parts/markers/electron-browser/markersModel'; -import * as Viewer from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MarkersFilterActionItem, MarkersFilterAction, QuickFixAction, QuickFixActionItem, IMarkersFilterActionChangeEvent } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -36,6 +35,12 @@ import { IExpression, getEmptyExpression } from 'vs/base/common/glob'; import { mixin, deepClone } from 'vs/base/common/objects'; import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isAbsolute, join } from 'vs/base/common/paths'; +import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/abstractTree'; +import { FilterData, FileResourceMarkersRenderer, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { Separator, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; type TreeElement = ResourceMarkers | Marker | RelatedInformation; @@ -61,7 +66,7 @@ export class MarkersPanel extends Panel { private lastSelectedRelativeTop: number = 0; private currentActiveResource: URI = null; - private tree: WorkbenchObjectTree; + private tree: WorkbenchObjectTree; private rangeHighlightDecorations: RangeHighlightDecorations; private actions: IAction[]; @@ -75,7 +80,7 @@ export class MarkersPanel extends Panel { private panelSettings: any; private panelFoucusContextKey: IContextKey; - private filter: Viewer.Filter; + private filter: Filter; private currentResourceGotAddedToMarkersData: boolean = false; @@ -89,6 +94,9 @@ export class MarkersPanel extends Panel { @IStorageService storageService: IStorageService, @IContextKeyService contextKeyService: IContextKeyService, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IMenuService private menuService: IMenuService, + @IKeybindingService private keybindingService: IKeybindingService, ) { super(Constants.MARKERS_PANEL_ID, telemetryService, themeService); this.panelFoucusContextKey = Constants.MarkerPanelFocusContextKey.bindTo(contextKeyService); @@ -279,14 +287,14 @@ export class MarkersPanel extends Panel { const onDidChangeRenderNodeCount = new Relay>(); - const virtualDelegate = new Viewer.VirtualDelegate(); + const virtualDelegate = new VirtualDelegate(); const renderers = [ - this.instantiationService.createInstance(Viewer.FileResourceMarkersRenderer, onDidChangeRenderNodeCount.event), - this.instantiationService.createInstance(Viewer.ResourceMarkersRenderer, onDidChangeRenderNodeCount.event), - this.instantiationService.createInstance(Viewer.MarkerRenderer, a => this.getActionItem(a)), - this.instantiationService.createInstance(Viewer.RelatedInformationRenderer) + this.instantiationService.createInstance(FileResourceMarkersRenderer, onDidChangeRenderNodeCount.event), + this.instantiationService.createInstance(ResourceMarkersRenderer, onDidChangeRenderNodeCount.event), + this.instantiationService.createInstance(MarkerRenderer, a => this.getActionItem(a)), + this.instantiationService.createInstance(RelatedInformationRenderer) ]; - this.filter = new Viewer.Filter(); + this.filter = new Filter(); this.tree = this.instantiationService.createInstance(WorkbenchObjectTree, this.treeContainer, @@ -295,7 +303,7 @@ export class MarkersPanel extends Panel { { filter: this.filter } - ) as any as WorkbenchObjectTree; + ) as any as WorkbenchObjectTree; onDidChangeRenderNodeCount.input = this.tree.onDidChangeRenderNodeCount; @@ -315,6 +323,8 @@ export class MarkersPanel extends Panel { this._register(debounceEvent(markersNavigator.openResource, (last, event) => event, 75, true)(options => { this.openFileAtElement(options.element, options.editorOptions.preserveFocus, options.sideBySide, options.editorOptions.pinned); })); + + this.tree.onContextMenu(this.onContextMenu, this, this._toDispose); } // TODO@joao @@ -525,7 +535,59 @@ export class MarkersPanel extends Panel { this.rangeHighlightDecorations.highlightRange(selection); } - public getFocusElement(): ResourceMarkers | Marker | RelatedInformation { + private onContextMenu(e: ITreeContextMenuEvent): void { + if (!e.element) { + return; + } + + e.browserEvent.preventDefault(); + e.browserEvent.stopPropagation(); + + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => TPromise.wrap(this._getMenuActions(e.element.element)), + getActionItem: (action) => { + const keybinding = this.keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return null; + }, + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this.tree.domFocus(); + } + } + }); + } + + private async _getMenuActions(element: TreeElement): Promise { + const result: IAction[] = []; + + if (element instanceof Marker) { + const quickFixAction = this.instantiationService.createInstance(QuickFixAction, element); + const quickFixActions = await quickFixAction.getQuickFixActions(); + if (quickFixActions.length) { + result.push(...quickFixActions); + result.push(new Separator()); + } + } + + const menu = this.menuService.createMenu(MenuId.ProblemsPanelContext, this.tree.contextKeyService); + const groups = menu.getActions(); + menu.dispose(); + + for (let group of groups) { + const [, actions] = group; + result.push(...actions); + result.push(new Separator()); + } + + result.pop(); // remove last separator + return result; + } + + public getFocusElement(): TreeElement { return this.tree.getFocus()[0]; } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts deleted file mode 100644 index 88accb29a8777cba650d14a95f4b6d9de8f3cb1d..0000000000000000000000000000000000000000 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts +++ /dev/null @@ -1,117 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { TPromise } from 'vs/base/common/winjs.base'; -import * as mouse from 'vs/base/browser/mouseEvent'; -import * as tree from 'vs/base/parts/tree/browser/tree'; -import { MarkersModel, Marker } from 'vs/workbench/parts/markers/electron-browser/markersModel'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { IAction } from 'vs/base/common/actions'; -import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { QuickFixAction } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; - -export class Controller extends WorkbenchTreeController { - - constructor( - private readonly onType: () => any, - @IContextMenuService private contextMenuService: IContextMenuService, - @IMenuService private menuService: IMenuService, - @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IConfigurationService configurationService: IConfigurationService, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super({}, configurationService); - } - - onKeyDown(tree: tree.ITree, event: IKeyboardEvent) { - let handled = super.onKeyDown(tree, event); - if (handled) { - return true; - } - if (this.upKeyBindingDispatcher.has(event.keyCode)) { - return false; - } - if (this._keybindingService.mightProducePrintableCharacter(event)) { - this.onType(); - return true; - } - return false; - } - - protected onLeftClick(tree: tree.ITree, element: any, event: mouse.IMouseEvent): boolean { - let currentFoucssed = tree.getFocus(); - if (super.onLeftClick(tree, element, event)) { - if (element instanceof MarkersModel) { - if (currentFoucssed) { - tree.setFocus(currentFoucssed); - } else { - tree.focusFirst(); - } - } - return true; - } - return false; - } - - // TODO@Joao - public onContextMenu(tree: WorkbenchTree, element: any, event: tree.ContextMenuEvent): boolean { - tree.setFocus(element/* , { preventOpenOnFocus: true } */); - - const anchor = { x: event.posx, y: event.posy }; - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - - getActions: () => TPromise.wrap(this._getMenuActions(tree, element)), - - getActionItem: (action) => { - const keybinding = this._keybindingService.lookupKeybinding(action.id); - if (keybinding) { - return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); - } - return null; - }, - - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - tree.domFocus(); - } - } - }); - - return true; - } - - private async _getMenuActions(tree: WorkbenchTree, element: any): Promise { - const result: IAction[] = []; - - if (element instanceof Marker) { - const quickFixAction = this.instantiationService.createInstance(QuickFixAction, element); - const quickFixActions = await quickFixAction.getQuickFixActions(); - if (quickFixActions.length) { - result.push(...quickFixActions); - result.push(new Separator()); - } - } - - const menu = this.menuService.createMenu(MenuId.ProblemsPanelContext, tree.contextKeyService); - const groups = menu.getActions(); - menu.dispose(); - - for (let group of groups) { - const [, actions] = group; - result.push(...actions); - result.push(new Separator()); - } - - result.pop(); // remove last separator - return result; - } -} diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index 0ed1a62ea488f964898b8d6ee93b6c5e2290bfa4..e21f5a96a638e0761ae0d32bacd8e39a4356833f 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -123,7 +123,7 @@ interface RelatedInformationFilterData { messageMatches: IMatch[]; } -type FilterData = ResourceMarkersFilterData | MarkerFilterData | RelatedInformationFilterData; +export type FilterData = ResourceMarkersFilterData | MarkerFilterData | RelatedInformationFilterData; export class ResourceMarkersRenderer implements ITreeRenderer {