提交 7c13b3cd 编写于 作者: S Sandeep Somavarapu

Introduce resource context actions

上级 8cd0cc21
......@@ -50,7 +50,8 @@ export class MenuId {
static readonly SCMResourceGroupContext = new MenuId('12');
static readonly SCMResourceContext = new MenuId('13');
static readonly CommandPalette = new MenuId('14');
static readonly ViewletTitle = new MenuId('15');
static readonly ViewTitle = new MenuId('15');
static readonly ViewResource = new MenuId('16');
constructor(private _id: string) {
......
......@@ -38,7 +38,8 @@ namespace schema {
case 'scm/title': return MenuId.SCMTitle;
case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext;
case 'scm/resourceState/context': return MenuId.SCMResourceContext;
case 'viewlet/title': return MenuId.ViewletTitle;
case 'view/title': return MenuId.ViewTitle;
case 'view/resource': return MenuId.ViewResource;
}
return void 0;
......
......@@ -22,7 +22,7 @@ class InternalTreeNodeImpl implements InternalTreeNode {
hasChildren: boolean;
clickCommand: string = null;
constructor(node: any, provider: TreeDataProvider<any>) {
constructor(readonly providerId: string, node: any, provider: TreeDataProvider<any>) {
this.id = defaultGenerator.nextId();
this.label = provider.getLabel ? provider.getLabel(node) : node.toString();
this.hasChildren = provider.getHasChildren ? provider.getHasChildren(node) : true;
......@@ -56,6 +56,16 @@ export class ExtHostTreeView extends ExtHostTreeViewShape {
this._extNodeMaps = Object.create(null);
this._mainNodesMap = new Map<string, Map<any, InternalTreeNode>>();
this._childrenNodesMap = new Map<string, Map<any, any[]>>();
commands.registerArgumentProcessor({
processArgument: arg => {
if (arg && arg.providerId && arg.id) {
const extNodeMap = this._extNodeMaps[arg.providerId];
return extNodeMap[arg.id];
}
return arg;
}
});
}
createTreeView<T>(providerId: string, provider: TreeDataProvider<T>): TreeView<T> {
......@@ -90,7 +100,7 @@ export class ExtHostTreeView extends ExtHostTreeViewShape {
return asWinJsPromise(() => provider.provideRootNode()).then(extRootNode => {
const extNodeMap: { [id: string]: InternalTreeNode } = Object.create(null);
const internalRootNode = new InternalTreeNodeImpl(extRootNode, provider);
const internalRootNode = new InternalTreeNodeImpl(providerId, extRootNode, provider);
extNodeMap[internalRootNode.id] = extRootNode;
this._extNodeMaps[providerId] = extNodeMap;
......@@ -123,7 +133,7 @@ export class ExtHostTreeView extends ExtHostTreeViewShape {
return asWinJsPromise(() => provider.resolveChildren(extNode)).then(children => {
return children.map(extChild => {
const internalChild = new InternalTreeNodeImpl(extChild, provider);
const internalChild = new InternalTreeNodeImpl(providerId, extChild, provider);
extNodeMap[internalChild.id] = extChild;
this._mainNodesMap.get(providerId).set(extChild, internalChild);
return internalChild;
......
......@@ -26,29 +26,29 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
registerSingleton(ITreeExplorerService, TreeExplorerService);
const treeViewSchema: IJSONSchema = {
description: localize('vscode.extension.contributes.treeView', 'Contributes custom tree view'),
const viewSchema: IJSONSchema = {
description: localize('vscode.extension.contributes.view', 'Contributes custom view'),
type: 'object',
properties: {
id: {
description: localize('vscode.extension.contributes.treeView.id', 'Unique id used to identify view created through vscode.workspace.createTreeView'),
description: localize('vscode.extension.contributes.view.id', 'Unique id used to identify view created through vscode.workspace.createTreeView'),
type: 'string'
},
label: {
description: localize('vscode.extension.contributes.treeView.label', 'Human readable string used to render the tree view'),
description: localize('vscode.extension.contributes.view.label', 'Human readable string used to render the view'),
type: 'string'
},
icon: {
description: localize('vscode.extension.contributes.treeView.icon', 'Path to the viewlet icon on the activity bar'),
description: localize('vscode.extension.contributes.view.icon', 'Path to the view icon'),
type: 'string'
}
}
};
const treeViewsSchema: IJSONSchema = {
description: localize('vscode.extension.contributes.treeView', 'Contributes custom tree view'),
const viewsSchema: IJSONSchema = {
description: localize('vscode.extension.contributes.views', 'Contributes custom views'),
type: 'object',
items: treeViewSchema
items: viewSchema
};
export class OpenViewletAction extends ToggleViewletAction {
......@@ -70,11 +70,11 @@ export class ExtensionExplorersContribtion implements IWorkbenchContribution {
}
public getId(): string {
return 'vs.extension.treeView';
return 'vs.extension.view';
}
private init() {
ExtensionsRegistry.registerExtensionPoint<ITreeExplorer[]>('treeViews', [], treeViewsSchema).setHandler(extensions => {
ExtensionsRegistry.registerExtensionPoint<ITreeExplorer[]>('views', [], viewsSchema).setHandler(extensions => {
for (let extension of extensions) {
for (const { id, label, icon } of extension.value) {
if (!isValidViewletId(id)) {
......
......@@ -47,7 +47,7 @@ export class TreeExplorerMenus implements IDisposable {
this.activeProviderId = activeProvider;
const titleMenu = this.menuService.createMenu(MenuId.ViewletTitle, this.contextKeyService);
const titleMenu = this.menuService.createMenu(MenuId.ViewTitle, this.contextKeyService);
const updateActions = () => {
this.titleActions = [];
this.titleSecondaryActions = [];
......@@ -74,6 +74,26 @@ export class TreeExplorerMenus implements IDisposable {
return this.titleSecondaryActions;
}
getResourceContextActions(): IAction[] {
return this.getActions(MenuId.ViewResource).secondary;
}
private getActions(menuId: MenuId): { primary: IAction[]; secondary: IAction[]; } {
if (!this.activeProviderId) {
return { primary: [], secondary: [] };
}
const menu = this.menuService.createMenu(menuId, this.contextKeyService);
const primary = [];
const secondary = [];
const result = { primary, secondary };
fillInActions(menu, { shouldForwardArgs: true }, result, g => g === 'inline');
menu.dispose();
return result;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
......
......@@ -31,7 +31,7 @@ export class TreeExplorerService implements ITreeExplorerService {
@IMessageService private messageService: IMessageService
) {
this._treeExplorerNodeProviders = Object.create(null);
this.activeProviderContextKey = this.contextKeyService.createKey<string | undefined>('treeExplorerProvider', void 0);
this.activeProviderContextKey = this.contextKeyService.createKey<string | undefined>('view', void 0);
}
get activeProvider(): string {
......
......@@ -62,7 +62,7 @@ export class TreeExplorerView extends CollapsibleViewletView {
public createViewer(container: Builder): ITree {
const dataSource = this.instantiationService.createInstance(TreeDataSource, this.treeNodeProviderId);
const renderer = this.instantiationService.createInstance(TreeRenderer, this.viewletState, this.actionRunner, container.getHTMLElement());
const controller = this.instantiationService.createInstance(TreeController, this.treeNodeProviderId);
const controller = this.instantiationService.createInstance(TreeController, this.treeNodeProviderId, this.menus);
const tree = new Tree(container.getHTMLElement(), {
dataSource,
......
......@@ -6,14 +6,20 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { $, Builder } from 'vs/base/browser/builder';
import { ITree, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import { ITree, IDataSource, IRenderer, IActionProvider, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
import { InternalTreeNode } from 'vs/workbench/parts/explorers/common/treeExplorerViewModel';
import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IActionRunner } from 'vs/base/common/actions';
import { IActionRunner, IAction, ActionRunner } from 'vs/base/common/actions';
import { ContributableActionProvider } from 'vs/workbench/browser/actionBarRegistry';
import { ITreeExplorerService } from 'vs/workbench/parts/explorers/common/treeExplorerService';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { TreeExplorerMenus } from 'vs/workbench/parts/explorers/browser/treeExplorerMenus';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { MenuItemAction } from 'vs/platform/actions/common/actions';
export class TreeDataSource implements IDataSource {
......@@ -92,7 +98,10 @@ export class TreeController extends DefaultController {
constructor(
private treeNodeProviderId: string,
@ITreeExplorerService private treeExplorerService: ITreeExplorerService
private menus: TreeExplorerMenus,
@IContextMenuService private contextMenuService: IContextMenuService,
@ITreeExplorerService private treeExplorerService: ITreeExplorerService,
@IKeybindingService private _keybindingService: IKeybindingService
) {
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, keyboardSupport: false });
}
......@@ -106,6 +115,50 @@ export class TreeController extends DefaultController {
return true;
}
public onContextMenu(tree: ITree, node: InternalTreeNode, event: ContextMenuEvent): boolean {
tree.setFocus(node);
const actions = this.menus.getResourceContextActions();
if (!actions.length) {
return true;
}
const anchor = { x: event.posx + 1, y: event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => {
return TPromise.as(actions);
},
getActionItem: (action) => {
const keybinding = this._keybindingFor(action);
if (keybinding) {
return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() });
}
return null;
},
getKeyBinding: (action): ResolvedKeybinding => {
return this._keybindingFor(action);
},
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
tree.DOMFocus();
}
},
getActionsContext: () => node,
actionRunner: new MultipleSelectionActionRunner(() => tree.getSelection())
});
return true;
}
private _keybindingFor(action: IAction): ResolvedKeybinding {
return this._keybindingService.lookupKeybinding(action.id);
}
}
export interface ITreeExplorerViewletState {
......@@ -131,3 +184,25 @@ export class TreeExplorerViewletState implements ITreeExplorerViewletState {
public get actionProvider() { return this._actionProvider; }
}
class MultipleSelectionActionRunner extends ActionRunner {
constructor(private getSelectedResources: () => InternalTreeNode[]) {
super();
}
runAction(action: IAction, context: InternalTreeNode): TPromise<any> {
if (action instanceof MenuItemAction) {
const selection = this.getSelectedResources();
const filteredSelection = selection.filter(s => s !== context);
if (selection.length === filteredSelection.length || selection.length === 1) {
return action.run(context);
}
return action.run(context, ...filteredSelection);
}
return super.runAction(action, context);
}
}
......@@ -15,6 +15,7 @@ export interface InternalTreeNodeContent {
export interface InternalTreeNode extends InternalTreeNodeContent {
readonly id: string;
readonly providerId: string;
}
export interface InternalTreeNodeProvider {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册