diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts index 0e03b0890ceeed89495abaf08483399777b8f652..fcf2068e3ac4b1c9884aa51f0967af8296d60250 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts @@ -28,7 +28,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; +import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -367,6 +367,8 @@ export class ExtensionEditor extends BaseEditor { reloadAction, this.instantiationService.createInstance(StatusLabelAction), this.instantiationService.createInstance(UpdateAction), + this.instantiationService.createInstance(SetColorThemeAction), + this.instantiationService.createInstance(SetFileIconThemeAction), this.instantiationService.createInstance(EnableDropDownAction), this.instantiationService.createInstance(DisableDropDownAction, runningExtensions), this.instantiationService.createInstance(CombinedInstallAction), diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts index e5fc6a2016c630a78b864ea9be8eff711fd7e26f..0025d474609ebd1bc85b2b959eb796386b48cd12 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts @@ -55,7 +55,7 @@ import { clipboard } from 'electron'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { coalesce } from 'vs/base/common/arrays'; -import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, COLOR_THEME_SETTING } from 'vs/workbench/services/themes/common/workbenchThemeService'; function toExtensionDescription(local: ILocalExtension): IExtensionDescription { return { @@ -147,7 +147,6 @@ export class InstallAction extends ExtensionAction { @IInstantiationService private readonly instantiationService: IInstantiationService, @INotificationService private readonly notificationService: INotificationService, @IOpenerService private readonly openerService: IOpenerService, - @IQuickInputService private readonly quickInputService: IQuickInputService, @IExtensionService private readonly runtimeExtensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService ) { @@ -183,8 +182,22 @@ export class InstallAction extends ExtensionAction { const extension = await this.install(this.extension); - if (extension.local && extension.local.manifest.contributes && extension.local.manifest.contributes.themes && extension.local.manifest.contributes.themes.length) { - return this.applyInstalledTheme(extension.local); + if (extension.local) { + const runningExtension = await this.getRunningExtension(extension.local); + if (runningExtension) { + const colorThemes = await this.workbenchThemeService.getColorThemes(runningExtension.identifier); + const fileIconThemes = await this.workbenchThemeService.getFileIconThemes(runningExtension.identifier); + if (colorThemes.length && !fileIconThemes.length) { + const action = this.instantiationService.createInstance(SetColorThemeAction); + action.extension = extension; + return action.run(true); + } + if (!colorThemes.length && fileIconThemes.length) { + const action = this.instantiationService.createInstance(SetFileIconThemeAction); + action.extension = extension; + return action.run(true); + } + } } } @@ -202,25 +215,6 @@ export class InstallAction extends ExtensionAction { }); } - private async applyInstalledTheme(extension: ILocalExtension): Promise { - const runningExtension = await this.getRunningExtension(extension); - if (runningExtension) { - const currentTheme = this.workbenchThemeService.getColorTheme(); - const themes = await this.workbenchThemeService.getColorThemes(runningExtension.identifier); - const delayer = new Delayer(100); - const picks: (IQuickPickItem | IQuickPickSeparator)[] = themes.map(theme => ({ label: theme.label, id: theme.id })); - picks.push({ type: 'separator' }); - picks.push({ label: localize('stay with current theme', "Stay with current theme ({0})", currentTheme.label), id: currentTheme.id }); - const pickedTheme = await this.quickInputService.pick( - picks, - { - placeHolder: localize('apply installed theme', "Apply installed theme or press Escape to cancel."), - onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setColorTheme(item.id, ConfigurationTarget.MEMORY).then(() => undefined)) - }); - this.workbenchThemeService.setColorTheme(pickedTheme ? pickedTheme.id : currentTheme.id, undefined); - } - } - private async getRunningExtension(extension: ILocalExtension): Promise { const runningExtension = await this.runtimeExtensionService.getExtension(extension.identifier.id); if (runningExtension) { @@ -1111,6 +1105,138 @@ export class ReloadAction extends ExtensionAction { } } +export class SetColorThemeAction extends ExtensionAction { + + private static readonly EnabledClass = 'extension-action theme'; + private static readonly DisabledClass = `${SetColorThemeAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + + constructor( + @IExtensionService extensionService: IExtensionService, + @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(`extensions.colorTheme`, localize('color theme', "Set Color Theme"), SetColorThemeAction.DisabledClass, false); + Event.any(extensionService.onDidChangeExtensions, workbenchThemeService.onDidColorThemeChange)(() => this.update(), this, this.disposables); + this.update(); + } + + async update(): Promise { + this.enabled = false; + if (this.extension) { + const isInstalled = this.extension.state === ExtensionState.Installed; + if (isInstalled) { + const colorThemes = await this.workbenchThemeService.getColorThemes(new ExtensionIdentifier(this.extension.identifier.id)); + this.enabled = colorThemes.length > 0; + } + } + this.class = this.enabled ? SetColorThemeAction.EnabledClass : SetColorThemeAction.DisabledClass; + } + + async run(showCurrentTheme: boolean): Promise { + await this.update(); + if (!this.enabled) { + return; + } + let colorThemes = await this.workbenchThemeService.getColorThemes(new ExtensionIdentifier(this.extension.identifier.id)); + const currentTheme = this.workbenchThemeService.getColorTheme(); + showCurrentTheme = showCurrentTheme || colorThemes.some(t => t.id === currentTheme.id); + if (showCurrentTheme) { + colorThemes = colorThemes.filter(t => t.id !== currentTheme.id); + } + + const delayer = new Delayer(100); + const picks: (IQuickPickItem | IQuickPickSeparator)[] = []; + picks.push(...colorThemes.map(theme => ({ label: theme.label, id: theme.id }))); + if (showCurrentTheme) { + picks.push({ type: 'separator', label: localize('current', "Current") }); + picks.push({ label: currentTheme.label, id: currentTheme.id }); + } + const pickedTheme = await this.quickInputService.pick( + picks, + { + placeHolder: localize('select color theme', "Select Color Theme"), + onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setColorTheme(item.id, undefined)) + }); + let confValue = this.configurationService.inspect(COLOR_THEME_SETTING); + const target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; + return this.workbenchThemeService.setColorTheme(pickedTheme ? pickedTheme.id : currentTheme.id, target); + } + + dispose() { + this.disposables = dispose(this.disposables); + super.dispose(); + } +} + +export class SetFileIconThemeAction extends ExtensionAction { + + private static readonly EnabledClass = 'extension-action theme'; + private static readonly DisabledClass = `${SetFileIconThemeAction.EnabledClass} disabled`; + + private disposables: IDisposable[] = []; + + constructor( + @IExtensionService extensionService: IExtensionService, + @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(`extensions.fileIconTheme`, localize('file icon theme', "Set File Icon Theme"), SetFileIconThemeAction.DisabledClass, false); + Event.any(extensionService.onDidChangeExtensions, workbenchThemeService.onDidFileIconThemeChange)(() => this.update(), this, this.disposables); + this.update(); + } + + async update(): Promise { + this.enabled = false; + if (this.extension) { + const isInstalled = this.extension.state === ExtensionState.Installed; + if (isInstalled) { + const fileIconThemes = await this.workbenchThemeService.getFileIconThemes(new ExtensionIdentifier(this.extension.identifier.id)); + this.enabled = fileIconThemes.length > 0; + } + } + this.class = this.enabled ? SetFileIconThemeAction.EnabledClass : SetFileIconThemeAction.DisabledClass; + } + + async run(showCurrentTheme: boolean): Promise { + await this.update(); + if (!this.enabled) { + return; + } + let fileIconThemes = await this.workbenchThemeService.getFileIconThemes(new ExtensionIdentifier(this.extension.identifier.id)); + const currentTheme = this.workbenchThemeService.getFileIconTheme(); + showCurrentTheme = showCurrentTheme || fileIconThemes.some(t => t.id === currentTheme.id); + if (showCurrentTheme) { + fileIconThemes = fileIconThemes.filter(t => t.id !== currentTheme.id); + } + + const delayer = new Delayer(100); + const picks: (IQuickPickItem | IQuickPickSeparator)[] = []; + picks.push(...fileIconThemes.map(theme => ({ label: theme.label, id: theme.id }))); + if (showCurrentTheme) { + picks.push({ type: 'separator', label: localize('current', "Current") }); + picks.push({ label: currentTheme.label, id: currentTheme.id }); + } + const pickedTheme = await this.quickInputService.pick( + picks, + { + placeHolder: localize('select file icon theme', "Select File Icon Theme"), + onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setFileIconTheme(item.id, undefined)) + }); + let confValue = this.configurationService.inspect(COLOR_THEME_SETTING); + const target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; + return this.workbenchThemeService.setFileIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, target); + } + + dispose() { + this.disposables = dispose(this.disposables); + super.dispose(); + } +} + export class OpenExtensionsViewletAction extends ShowViewletAction { static ID = VIEWLET_ID; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/electron-browser/media/extensionActions.css index d2bea0aecb4d771cf65695ab111b162e146c42b2..3bf716270cc8bf019351562758d0f578611751e1 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/media/extensionActions.css +++ b/src/vs/workbench/contrib/extensions/electron-browser/media/extensionActions.css @@ -31,6 +31,7 @@ .monaco-action-bar .action-item.disabled .action-label.extension-action.install:not(.installing), .monaco-action-bar .action-item.disabled .action-label.extension-action.uninstall:not(.uninstalling), .monaco-action-bar .action-item.disabled .action-label.extension-action.update, +.monaco-action-bar .action-item.disabled .action-label.extension-action.theme, .monaco-action-bar .action-item.disabled .action-label.extension-action.extension-editor-dropdown-action, .monaco-action-bar .action-item.disabled .action-label.extension-action.reload, .monaco-action-bar .action-item.disabled .action-label.disable-status.hide, diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 9e386c7960cd4a18fb33d7ee8c9b7e3f1bddd52e..4da012b7901f6430cfe4000b6dc7f768e39b0c60 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -61,7 +61,7 @@ export interface IWorkbenchThemeService extends IThemeService { setFileIconTheme(iconThemeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; getFileIconTheme(): IFileIconTheme; - getFileIconThemes(): Promise; + getFileIconThemes(extensionId?: ExtensionIdentifier): Promise; onDidFileIconThemeChange: Event; } diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index d4b2043c48d41ce87d0c9fb12b3e678aaad9b5bd..099a3ca61be43af2a5eb1038484abc27c95edb83 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -460,8 +460,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } - public getFileIconThemes(): Promise { - return this.iconThemeStore.getFileIconThemes(); + public async getFileIconThemes(extensionId?: ExtensionIdentifier): Promise { + const filIconThemes = await this.iconThemeStore.getFileIconThemes(); + return extensionId ? filIconThemes.filter(c => c.extensionData && ExtensionIdentifier.equals(new ExtensionIdentifier(c.extensionData.extensionId), extensionId)) : filIconThemes; } public getFileIconTheme() {