提交 9b1013ac 编写于 作者: J Joao Moreno

markers context menus

上级 b8853606
......@@ -44,6 +44,7 @@ export interface IListGestureEvent<T> {
}
export interface IListContextMenuEvent<T> {
browserEvent: UIEvent;
element: T;
index: number;
anchor: HTMLElement | { x: number; y: number; };
......
......@@ -124,7 +124,7 @@ export class PagedList<T> implements IDisposable {
}
get onContextMenu(): Event<IListContextMenuEvent<T>> {
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<T> {
......
......@@ -418,7 +418,13 @@ class MouseController<T> 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<any>;
.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<T> 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<IListContextMenuEvent<T>>(fromKeydown, fromKeyup, fromMouse);
......
......@@ -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<T, TFilterData = void> extends IListOptions<T>, IIndexTreeModelOptions<T, TFilterData> { }
export interface ITreeEvent<T, TFilterData> extends IListEvent<ITreeNode<T, TFilterData>> { }
export interface ITreeContextMenuEvent<T, TFilterData> extends IListContextMenuEvent<ITreeNode<T, TFilterData>> { }
export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable {
......@@ -173,6 +174,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
readonly onDidChangeFocus: Event<ITreeEvent<T, TFilterData>>;
readonly onDidChangeSelection: Event<ITreeEvent<T, TFilterData>>;
readonly onContextMenu: Event<ITreeContextMenuEvent<T, TFilterData>>;
get onDidFocus(): Event<void> { return this.view.onDidFocus; }
get onDidBlur(): Event<void> { return this.view.onDidBlur; }
get onDidDispose(): Event<void> { return this.view.onDidDispose; }
......@@ -192,6 +195,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.view = new List(container, treeDelegate, treeRenderers, createComposedTreeListOptions<T, ITreeNode<T, TFilterData>>(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;
......
......@@ -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<TreeElement>;
private tree: WorkbenchObjectTree<TreeElement, FilterData>;
private rangeHighlightDecorations: RangeHighlightDecorations;
private actions: IAction[];
......@@ -75,7 +80,7 @@ export class MarkersPanel extends Panel {
private panelSettings: any;
private panelFoucusContextKey: IContextKey<boolean>;
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<ITreeNode<any, any>>();
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<TreeElement>;
) as any as WorkbenchObjectTree<TreeElement, FilterData>;
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<TreeElement, FilterData>): 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<IAction[]> {
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];
}
......
/*---------------------------------------------------------------------------------------------
* 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<IAction[]> {
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;
}
}
......@@ -123,7 +123,7 @@ interface RelatedInformationFilterData {
messageMatches: IMatch[];
}
type FilterData = ResourceMarkersFilterData | MarkerFilterData | RelatedInformationFilterData;
export type FilterData = ResourceMarkersFilterData | MarkerFilterData | RelatedInformationFilterData;
export class ResourceMarkersRenderer implements ITreeRenderer<ResourceMarkers, ResourceMarkersFilterData, IResourceMarkersTemplateData> {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册