diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 29ad2cfa81bbd1e9a3ddc246a739f7f70bc47fd4..fe57d3c015c92e55f364aab28e954aa1c384fdf9 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -11,7 +11,7 @@ import * as DOM from 'vs/base/browser/dom'; import severity from 'vs/base/common/severity'; import paths = require('vs/base/common/paths'); import Event from 'vs/base/common/event'; -import { ActionItem, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionItem, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; @@ -282,7 +282,7 @@ export class DropDownMenuActionItem extends ActionItem { private disposables: IDisposable[] = []; private _extension: IExtension; - constructor(action: IAction, private menuActions: IExtensionAction[], private contextMenuService: IContextMenuService) { + constructor(action: IAction, private menuActions: IAction[], private contextMenuService: IContextMenuService) { super(null, action, { icon: true, label: true }); this.disposables = [...menuActions]; } @@ -292,16 +292,19 @@ export class DropDownMenuActionItem extends ActionItem { set extension(extension: IExtension) { this._extension = extension; for (const menuAction of this.menuActions) { - menuAction.extension = extension; + if (!(menuAction instanceof Separator)) { + (menuAction).extension = extension; + } } } public showMenu(): void { + const actions = this.menuActions.filter(a => a instanceof Separator || a.enabled); let elementPosition = DOM.getDomNodePagePosition(this.builder.getHTMLElement()); const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => TPromise.wrap(this.menuActions), + getActions: () => TPromise.wrap(actions) }); } @@ -311,45 +314,130 @@ export class DropDownMenuActionItem extends ActionItem { } } +export class ManageExtensionActionItem extends DropDownMenuActionItem { + constructor( + action: IAction, + @IInstantiationService instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService) { + super(action, [instantiationService.createInstance(EnableForWorkspaceAction, localize('enableForWorkspaceAction.label', "Enable (Workspace)")), instantiationService.createInstance(EnableGloballyAction, localize('enableAlwaysAction.label', "Enable")), + instantiationService.createInstance(DisableForWorkspaceAction, localize('disableForWorkspaceAction.label', "Disable (Workspace)")), instantiationService.createInstance(DisableGloballyAction, localize('disableAlwaysAction.label', "Disable")), new Separator(), instantiationService.createInstance(UninstallAction)], contextMenuService); + } +} + +export class ManageExtensionAction extends Action { + + static ID = 'extensions.manage'; + + private static Class = 'extension-action manage'; + private static NoExtensionClass = `${ManageExtensionAction.Class} no-extension`; + + private _actionItem: EnableActionItem; + get actionItem(): IActionItem { return this._actionItem; } + + private disposables: IDisposable[] = []; + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); } + + constructor( + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionsRuntimeService private extensionRuntimeService: IExtensionsRuntimeService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(ManageExtensionAction.ID); + + this._actionItem = this.instantiationService.createInstance(ManageExtensionActionItem, this); + this.disposables.push(this._actionItem); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.class = ManageExtensionAction.NoExtensionClass; + this.enabled = false; + if (this.extension) { + this.class = ManageExtensionAction.Class; + this.enabled = ExtensionState.Uninstalled !== this.extension.state; + } + } + + public run(): TPromise { + this._actionItem.showMenu(); + return TPromise.wrap(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + export class EnableForWorkspaceAction extends Action { + static ID = 'extensions.enableForWorkspace'; + static LABEL = localize('enableForWorkspaceAction', "Workspace"); + + private disposables: IDisposable[] = []; + private _extension: IExtension; get extension(): IExtension { return this._extension; } set extension(extension: IExtension) { this._extension = extension; this.update(); } - constructor( + constructor(label: string, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService + @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService, + @IInstantiationService private instantiationService: IInstantiationService ) { - super('extensions.enableForWorkspace', localize('enableForWorkspaceAction', "Workspace"), '', !!workspaceContextService.getWorkspace()); + super(EnableForWorkspaceAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); } private update(): void { + this.enabled = false; if (this.extension && this.workspaceContextService.getWorkspace()) { - this.enabled = !this.extensionsRuntimeService.isDisabledAlways(this.extension.identifier); + this.enabled = ExtensionState.Disabled === this.extension.state && !this.extensionsRuntimeService.isDisabledAlways(this.extension.identifier); } } run(): TPromise { return this.extensionsWorkbenchService.setEnablement(this.extension, true, true); } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } } export class EnableGloballyAction extends Action { + static ID = 'extensions.enableGlobally'; + static LABEL = localize('enableGloballyAction', "Always"); + + private disposables: IDisposable[] = []; + private _extension: IExtension; get extension(): IExtension { return this._extension; } set extension(extension: IExtension) { this._extension = extension; this.update(); } - constructor( + constructor(label: string, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService + @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService, + @IInstantiationService private instantiationService: IInstantiationService ) { - super('extensions.enableGlobally', localize('enableGloballyAction', "Always"), '', false); + super(EnableGloballyAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); } private update(): void { + this.enabled = false; if (this.extension) { this.enabled = this.extensionsRuntimeService.isDisabledAlways(this.extension.identifier); } @@ -358,6 +446,11 @@ export class EnableGloballyAction extends Action { run(): TPromise { return this.extensionsWorkbenchService.setEnablement(this.extension, true, false); } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } } export class EnableAction extends Action { @@ -415,37 +508,79 @@ export class EnableAction extends Action { export class DisableForWorkspaceAction extends Action { + static ID = 'extensions.disableForWorkspace'; + static LABEL = localize('disableForWorkspaceAction', "Workspace"); + + private disposables: IDisposable[] = []; + private _extension: IExtension; get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } - constructor( + constructor(label: string, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService private instantiationService: IInstantiationService ) { - super('extensions.disableForWorkspace', localize('disableForWorkspaceAction', "Workspace"), '', !!workspaceContextService.getWorkspace()); + super(DisableForWorkspaceAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension && this.workspaceContextService.getWorkspace()) { + this.enabled = ExtensionState.Enabled === this.extension.state; + } } run(): TPromise { return this.extensionsWorkbenchService.setEnablement(this.extension, false, true); } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } } export class DisableGloballyAction extends Action { + static ID = 'extensions.disableGlobally'; + static LABEL = localize('disableGloballyAction', "Always"); + + private disposables: IDisposable[] = []; + private _extension: IExtension; get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } - constructor( - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + constructor(label: string, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService private instantiationService: IInstantiationService ) { - super('extensions.disableGlobally', localize('disableGloballyAction', "Always"), '', true); + super(DisableGloballyAction.ID, label); + + this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = false; + if (this.extension) { + this.enabled = ExtensionState.Enabled === this.extension.state; + } } run(): TPromise { return this.extensionsWorkbenchService.setEnablement(this.extension, false, false); } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } } export class DisableAction extends Action { @@ -503,7 +638,7 @@ export class EnableActionItem extends DropDownMenuActionItem { action: IAction, @IInstantiationService instantiationService: IInstantiationService, @IContextMenuService contextMenuService: IContextMenuService) { - super(action, [instantiationService.createInstance(EnableForWorkspaceAction), instantiationService.createInstance(EnableGloballyAction)], contextMenuService); + super(action, [instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL), instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL)], contextMenuService); } } @@ -512,7 +647,7 @@ export class DisableActionItem extends DropDownMenuActionItem { action: IAction, @IInstantiationService instantiationService: IInstantiationService, @IContextMenuService contextMenuService: IContextMenuService) { - super(action, [instantiationService.createInstance(DisableForWorkspaceAction), instantiationService.createInstance(DisableGloballyAction)], contextMenuService); + super(action, [instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL), instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL)], contextMenuService); } } @@ -1118,4 +1253,4 @@ export class EnableAllWorkpsaceAction extends Action { super.dispose(); this.disposables = dispose(this.disposables); } -} +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index 9206c40f3b3c183e50722b88571129d139bd6514..f4ce644eb7fcd846e28f1eeb7cda1fcf9967327f 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -16,9 +16,10 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { once } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, ExtensionState } from '../common/extensions'; -import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction } from './extensionsActions'; +import { InstallAction, UpdateAction, BuiltinStatusLabelAction, ReloadAction, ManageExtensionAction } from './extensionsActions'; import { Label, RatingsWidget, InstallWidget } from './extensionsWidgets'; import { EventType } from 'vs/base/common/events'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; export interface ITemplateData { element: HTMLElement; @@ -44,6 +45,7 @@ export class Renderer implements IPagedRenderer { constructor( @IInstantiationService private instantiationService: IInstantiationService, + @IContextMenuService private contextMenuService: IContextMenuService, @IMessageService private messageService: IMessageService ) { } @@ -65,16 +67,12 @@ export class Renderer implements IPagedRenderer { const actionbar = new ActionBar(footer, { animated: false, actionItemProvider: (action: Action) => { - if (action.id === EnableAction.ID) { - return (action).actionItem; - } - if (action.id === DisableAction.ID) { - return (action).actionItem; + if (action.id === ManageExtensionAction.ID) { + return (action).actionItem; } return null; } }); - actionbar.addListener2(EventType.RUN, ({ error }) => error && this.messageService.show(Severity.Error, error)); const versionWidget = this.instantiationService.createInstance(Label, version, e => e.version); @@ -82,14 +80,13 @@ export class Renderer implements IPagedRenderer { const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratings, { small: true }); const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction); - const combinedInstallAction = this.instantiationService.createInstance(CombinedInstallAction); + const installAction = this.instantiationService.createInstance(InstallAction); const updateAction = this.instantiationService.createInstance(UpdateAction); - const enableAction = this.instantiationService.createInstance(EnableAction); - const disableAction = this.instantiationService.createInstance(DisableAction); const reloadAction = this.instantiationService.createInstance(ReloadAction); + const manageAction = this.instantiationService.createInstance(ManageExtensionAction); - actionbar.push([enableAction, updateAction, disableAction, reloadAction, combinedInstallAction, builtinStatusAction], actionOptions); - const disposables = [versionWidget, installCountWidget, ratingsWidget, combinedInstallAction, builtinStatusAction, updateAction, enableAction, disableAction, reloadAction, actionbar]; + actionbar.push([updateAction, reloadAction, installAction, builtinStatusAction, manageAction], actionOptions); + const disposables = [versionWidget, installCountWidget, ratingsWidget, builtinStatusAction, updateAction, reloadAction, manageAction, actionbar]; return { element, icon, name, installCount, ratings, author, description, disposables, @@ -99,11 +96,10 @@ export class Renderer implements IPagedRenderer { installCountWidget.extension = extension; ratingsWidget.extension = extension; builtinStatusAction.extension = extension; - combinedInstallAction.extension = extension; + installAction.extension = extension; updateAction.extension = extension; - enableAction.extension = extension; - disableAction.extension = extension; reloadAction.extension = extension; + manageAction.extension = extension; } }; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css index d4b29f064517ad28425c3bd7d4331d3b73119cad..fa9e94e525e25fa897482d72d62f15b9c7554284 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css @@ -104,4 +104,19 @@ .extension-editor > .header > .details > .actions > .monaco-action-bar .action-item .action-label.extension-action.built-in-status { font-weight: normal; +} + +.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar .action-item .action-label.extension-action.manage.no-extension { + display: none; +} + +.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar .action-item .action-label.extension-action.manage { + height: 18px; + width: 10px; + border: none; + background: url('manage.svg') center center no-repeat; +} + +.vs-dark .extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar .action-item .action-label.extension-action.manage { + background: url('manage-inverse.svg') center center no-repeat; } \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/manage-inverse.svg b/src/vs/workbench/parts/extensions/electron-browser/media/manage-inverse.svg new file mode 100644 index 0000000000000000000000000000000000000000..61baaea2b8b21ee34356db99c28bb40719643392 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/media/manage-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/manage.svg b/src/vs/workbench/parts/extensions/electron-browser/media/manage.svg new file mode 100644 index 0000000000000000000000000000000000000000..3dec2ba50fd1a3dfdb241d947dc9a4adb2ded64c --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/media/manage.svg @@ -0,0 +1 @@ + \ No newline at end of file