From 8d7ef6e898522f126aaf6180aafc831b308d4fcd Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 11 Mar 2020 08:06:28 +0100 Subject: [PATCH] adopt product icon theme in extension actions & picker --- .../extensions/browser/extensionEditor.ts | 10 +- .../extensions/browser/extensionsActions.ts | 214 ++++++++++------- .../extensions/browser/extensionsViews.ts | 9 +- .../themes/browser/themes.contribution.ts | 223 +++++++++++------- .../themes/browser/productIconThemeData.ts | 2 +- .../themes/browser/workbenchThemeService.ts | 2 +- .../themes/common/themeConfiguration.ts | 14 +- .../themes/common/workbenchThemeService.ts | 42 ++-- 8 files changed, 298 insertions(+), 218 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 0d029dcbb27..f33d2839862 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -27,7 +27,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/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, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, SyncIgnoredIconAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction, SyncIgnoredIconAction, SetProductIconThemeAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; @@ -317,8 +317,6 @@ export class ExtensionEditor extends BaseEditor { private async updateTemplate(input: ExtensionsInput, template: IExtensionEditorTemplate, preserveFocus: boolean): Promise { const runningExtensions = await this.extensionService.getExtensions(); - const colorThemes = await this.workbenchThemeService.getColorThemes(); - const fileIconThemes = await this.workbenchThemeService.getFileIconThemes(); this.activeElement = null; this.editorLoadComplete = false; @@ -402,8 +400,10 @@ export class ExtensionEditor extends BaseEditor { this.instantiationService.createInstance(SyncIgnoredIconAction), this.instantiationService.createInstance(StatusLabelAction), this.instantiationService.createInstance(UpdateAction), - this.instantiationService.createInstance(SetColorThemeAction, colorThemes), - this.instantiationService.createInstance(SetFileIconThemeAction, fileIconThemes), + this.instantiationService.createInstance(SetColorThemeAction, await this.workbenchThemeService.getColorThemes()), + this.instantiationService.createInstance(SetFileIconThemeAction, await this.workbenchThemeService.getFileIconThemes()), + this.instantiationService.createInstance(SetProductIconThemeAction, await this.workbenchThemeService.getProductIconThemes()), + this.instantiationService.createInstance(EnableDropDownAction), this.instantiationService.createInstance(DisableDropDownAction, runningExtensions), this.instantiationService.createInstance(RemoteInstallAction), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index c29ffa2de33..d53d8aba530 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -29,7 +29,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, registerColor, foreground } from 'vs/platform/theme/common/colorRegistry'; import { Color } from 'vs/base/common/color'; @@ -52,7 +52,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { coalesce } from 'vs/base/common/arrays'; -import { IWorkbenchThemeService, ThemeSettings, IWorkbenchFileIconTheme, IWorkbenchColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IWorkbenchTheme, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ILabelService } from 'vs/platform/label/common/label'; import { prefersExecuteOnUI, prefersExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -237,17 +237,15 @@ export class InstallAction extends ExtensionAction { if (extension && extension.local) { const runningExtension = await this.getRunningExtension(extension.local); if (runningExtension) { - const colorThemes = await this.workbenchThemeService.getColorThemes(); - const fileIconThemes = await this.workbenchThemeService.getFileIconThemes(); - if (SetColorThemeAction.getColorThemes(colorThemes, this.extension).length) { - const action = this.instantiationService.createInstance(SetColorThemeAction, colorThemes); - action.extension = extension; - return action.run({ showCurrentTheme: true, ignoreFocusLost: true }); - } - if (SetFileIconThemeAction.getFileIconThemes(fileIconThemes, this.extension).length) { - const action = this.instantiationService.createInstance(SetFileIconThemeAction, fileIconThemes); - action.extension = extension; - return action.run({ showCurrentTheme: true, ignoreFocusLost: true }); + let action = await SetColorThemeAction.create(this.workbenchThemeService, this.instantiationService, extension) + || await SetFileIconThemeAction.create(this.workbenchThemeService, this.instantiationService, extension) + || await SetProductIconThemeAction.create(this.workbenchThemeService, this.instantiationService, extension); + if (action) { + try { + return action.run({ showCurrentTheme: true, ignoreFocusLost: true }); + } finally { + action.dispose(); + } } } } @@ -704,19 +702,22 @@ export class ManageExtensionAction extends ExtensionDropDownAction { this.update(); } - getActionGroups(runningExtensions: IExtensionDescription[], colorThemes: IWorkbenchColorTheme[], fileIconThemes: IWorkbenchFileIconTheme[]): IAction[][] { + async getActionGroups(runningExtensions: IExtensionDescription[]): Promise { const groups: ExtensionAction[][] = []; if (this.extension) { - const extensionColorThemes = SetColorThemeAction.getColorThemes(colorThemes, this.extension); - const extensionFileIconThemes = SetFileIconThemeAction.getFileIconThemes(fileIconThemes, this.extension); - if (extensionColorThemes.length || extensionFileIconThemes.length) { - const themesGroup: ExtensionAction[] = []; - if (extensionColorThemes.length) { - themesGroup.push(this.instantiationService.createInstance(SetColorThemeAction, colorThemes)); - } - if (extensionFileIconThemes.length) { - themesGroup.push(this.instantiationService.createInstance(SetFileIconThemeAction, fileIconThemes)); + const actions = await Promise.all([ + SetColorThemeAction.create(this.workbenchThemeService, this.instantiationService, this.extension), + SetFileIconThemeAction.create(this.workbenchThemeService, this.instantiationService, this.extension), + SetProductIconThemeAction.create(this.workbenchThemeService, this.instantiationService, this.extension) + ]); + + const themesGroup: ExtensionAction[] = []; + for (let action of actions) { + if (action) { + themesGroup.push(action); } + } + if (themesGroup.length) { groups.push(themesGroup); } } @@ -740,9 +741,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction { async run(): Promise { const runtimeExtensions = await this.extensionService.getExtensions(); - const colorThemes = await this.workbenchThemeService.getColorThemes(); - const fileIconThemes = await this.workbenchThemeService.getFileIconThemes(); - return super.run({ actionGroups: this.getActionGroups(runtimeExtensions, colorThemes, fileIconThemes), disposeActionsOnHide: true }); + return super.run({ actionGroups: await this.getActionGroups(runtimeExtensions), disposeActionsOnHide: true }); } update(): void { @@ -1301,21 +1300,45 @@ export class ReloadAction extends ExtensionAction { } } -export class SetColorThemeAction extends ExtensionAction { +function isThemeFromExtension(theme: IWorkbenchTheme, extension: IExtension | undefined | null): boolean { + return !!(extension && theme.extensionData && ExtensionIdentifier.equals(theme.extensionData.extensionId, extension.identifier.id)); +} - static getColorThemes(colorThemes: IWorkbenchColorTheme[], extension: IExtension): IWorkbenchColorTheme[] { - return colorThemes.filter(c => c.extensionData && ExtensionIdentifier.equals(c.extensionData.extensionId, extension.identifier.id)); +function getQuickPickEntries(themes: IWorkbenchTheme[], currentTheme: IWorkbenchTheme, extension: IExtension | null | undefined, showCurrentTheme: boolean): (IQuickPickItem | IQuickPickSeparator)[] { + const picks: (IQuickPickItem | IQuickPickSeparator)[] = []; + for (const theme of themes) { + if (isThemeFromExtension(theme, extension) && !(showCurrentTheme && theme === currentTheme)) { + picks.push({ label: theme.label, id: theme.id }); + } } + if (showCurrentTheme) { + picks.push({ type: 'separator', label: localize('current', "Current") }); + picks.push({ label: currentTheme.label, id: currentTheme.id }); + } + return picks; +} + + +export class SetColorThemeAction extends ExtensionAction { private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`; private static readonly DisabledClass = `${SetColorThemeAction.EnabledClass} disabled`; + static async create(workbenchThemeService: IWorkbenchThemeService, instantiationService: IInstantiationService, extension: IExtension): Promise { + const themes = await workbenchThemeService.getColorThemes(); + if (themes.some(th => isThemeFromExtension(th, extension))) { + const action = instantiationService.createInstance(SetColorThemeAction, themes); + action.extension = extension; + return action; + } + return undefined; + } + constructor( - private readonly colorThemes: IWorkbenchColorTheme[], + private colorThemes: IWorkbenchColorTheme[], @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); this._register(Event.any(extensionService.onDidChangeExtensions, workbenchThemeService.onDidColorThemeChange)(() => this.update(), this)); @@ -1323,36 +1346,21 @@ export class SetColorThemeAction extends ExtensionAction { } update(): void { - this.enabled = false; - if (this.extension) { - const isInstalled = this.extension.state === ExtensionState.Installed; - if (isInstalled) { - const extensionThemes = SetColorThemeAction.getColorThemes(this.colorThemes, this.extension); - this.enabled = extensionThemes.length > 0; - } - } + this.enabled = !!this.extension && (this.extension.state === ExtensionState.Installed) && this.colorThemes.some(th => isThemeFromExtension(th, this.extension)); this.class = this.enabled ? SetColorThemeAction.EnabledClass : SetColorThemeAction.DisabledClass; } async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean, ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise { + this.colorThemes = await this.workbenchThemeService.getColorThemes(); + this.update(); if (!this.enabled) { return; } - let extensionThemes = SetColorThemeAction.getColorThemes(this.colorThemes, this.extension!); - const currentTheme = this.colorThemes.filter(t => t.settingsId === this.configurationService.getValue(ThemeSettings.COLOR_THEME))[0] || this.workbenchThemeService.getColorTheme(); - showCurrentTheme = showCurrentTheme || extensionThemes.some(t => t.id === currentTheme.id); - if (showCurrentTheme) { - extensionThemes = extensionThemes.filter(t => t.id !== currentTheme.id); - } + const currentTheme = this.workbenchThemeService.getColorTheme(); const delayer = new Delayer(100); - const picks: (IQuickPickItem | IQuickPickSeparator)[] = []; - picks.push(...extensionThemes.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 picks = getQuickPickEntries(this.colorThemes, currentTheme, this.extension, showCurrentTheme); const pickedTheme = await this.quickInputService.pick( picks, { @@ -1360,9 +1368,7 @@ export class SetColorThemeAction extends ExtensionAction { onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setColorTheme(item.id, undefined)), ignoreFocusLost }); - let confValue = this.configurationService.inspect(ThemeSettings.COLOR_THEME); - const target = typeof confValue.workspaceValue !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; - return this.workbenchThemeService.setColorTheme(pickedTheme ? pickedTheme.id : currentTheme.id, target); + return this.workbenchThemeService.setColorTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto'); } } @@ -1371,16 +1377,21 @@ export class SetFileIconThemeAction extends ExtensionAction { private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`; private static readonly DisabledClass = `${SetFileIconThemeAction.EnabledClass} disabled`; - static getFileIconThemes(fileIconThemes: IWorkbenchFileIconTheme[], extension: IExtension): IWorkbenchFileIconTheme[] { - return fileIconThemes.filter(c => c.extensionData && ExtensionIdentifier.equals(c.extensionData.extensionId, extension.identifier.id)); + static async create(workbenchThemeService: IWorkbenchThemeService, instantiationService: IInstantiationService, extension: IExtension): Promise { + const themes = await workbenchThemeService.getFileIconThemes(); + if (themes.some(th => isThemeFromExtension(th, extension))) { + const action = instantiationService.createInstance(SetFileIconThemeAction, themes); + action.extension = extension; + return action; + } + return undefined; } constructor( - private readonly fileIconThemes: IWorkbenchFileIconTheme[], + private fileIconThemes: IWorkbenchFileIconTheme[], @IExtensionService extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IQuickInputService private readonly quickInputService: IQuickInputService ) { super(`extensions.fileIconTheme`, localize('file icon theme', "Set File Icon Theme"), SetFileIconThemeAction.DisabledClass, false); this._register(Event.any(extensionService.onDidChangeExtensions, workbenchThemeService.onDidFileIconThemeChange)(() => this.update(), this)); @@ -1388,36 +1399,20 @@ export class SetFileIconThemeAction extends ExtensionAction { } update(): void { - this.enabled = false; - if (this.extension) { - const isInstalled = this.extension.state === ExtensionState.Installed; - if (isInstalled) { - const extensionThemes = SetFileIconThemeAction.getFileIconThemes(this.fileIconThemes, this.extension); - this.enabled = extensionThemes.length > 0; - } - } + this.enabled = !!this.extension && (this.extension.state === ExtensionState.Installed) && this.fileIconThemes.some(th => isThemeFromExtension(th, this.extension)); this.class = this.enabled ? SetFileIconThemeAction.EnabledClass : SetFileIconThemeAction.DisabledClass; } async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean, ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise { - await this.update(); + this.fileIconThemes = await this.workbenchThemeService.getFileIconThemes(); + this.update(); if (!this.enabled) { return; } - let extensionThemes = SetFileIconThemeAction.getFileIconThemes(this.fileIconThemes, this.extension!); - const currentTheme = this.fileIconThemes.filter(t => t.settingsId === this.configurationService.getValue(ThemeSettings.ICON_THEME))[0] || this.workbenchThemeService.getFileIconTheme(); - showCurrentTheme = showCurrentTheme || extensionThemes.some(t => t.id === currentTheme.id); - if (showCurrentTheme) { - extensionThemes = extensionThemes.filter(t => t.id !== currentTheme.id); - } + const currentTheme = this.workbenchThemeService.getFileIconTheme(); const delayer = new Delayer(100); - const picks: (IQuickPickItem | IQuickPickSeparator)[] = []; - picks.push(...extensionThemes.map(theme => ({ label: theme.label, id: theme.id }))); - if (showCurrentTheme && currentTheme.label) { - picks.push({ type: 'separator', label: localize('current', "Current") }); - picks.push({ label: currentTheme.label, id: currentTheme.id }); - } + const picks = getQuickPickEntries(this.fileIconThemes, currentTheme, this.extension, showCurrentTheme); const pickedTheme = await this.quickInputService.pick( picks, { @@ -1425,9 +1420,62 @@ export class SetFileIconThemeAction extends ExtensionAction { onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setFileIconTheme(item.id, undefined)), ignoreFocusLost }); - let confValue = this.configurationService.inspect(ThemeSettings.ICON_THEME); - const target = typeof confValue.workspaceValue !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; - return this.workbenchThemeService.setFileIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, target); + return this.workbenchThemeService.setFileIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto'); + } +} + +export class SetProductIconThemeAction extends ExtensionAction { + + private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`; + private static readonly DisabledClass = `${SetProductIconThemeAction.EnabledClass} disabled`; + + static async create(workbenchThemeService: IWorkbenchThemeService, instantiationService: IInstantiationService, extension: IExtension): Promise { + const themes = await workbenchThemeService.getProductIconThemes(); + if (themes.some(th => isThemeFromExtension(th, extension))) { + const action = instantiationService.createInstance(SetProductIconThemeAction, themes); + action.extension = extension; + return action; + } + return undefined; + } + + constructor( + private productIconThemes: IWorkbenchProductIconTheme[], + @IExtensionService extensionService: IExtensionService, + @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, + @IQuickInputService private readonly quickInputService: IQuickInputService + ) { + super(`extensions.productIconTheme`, localize('product icon theme', "Set Product Icon Theme"), SetProductIconThemeAction.DisabledClass, false); + this._register(Event.any(extensionService.onDidChangeExtensions, workbenchThemeService.onDidProductIconThemeChange)(() => this.update(), this)); + this.enabled = true; // enabled by default + this.class = SetProductIconThemeAction.EnabledClass; + // this.update(); + } + + update(): void { + this.enabled = !!this.extension && (this.extension.state === ExtensionState.Installed) && this.productIconThemes.some(th => isThemeFromExtension(th, this.extension)); + this.class = this.enabled ? SetProductIconThemeAction.EnabledClass : SetProductIconThemeAction.DisabledClass; + } + + async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean, ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise { + this.productIconThemes = await this.workbenchThemeService.getProductIconThemes(); + this.update(); + if (!this.enabled) { + return; + } + + const currentTheme = this.workbenchThemeService.getProductIconTheme(); + + const delayer = new Delayer(100); + const picks = getQuickPickEntries(this.productIconThemes, currentTheme, this.extension, showCurrentTheme); + const pickedTheme = await this.quickInputService.pick( + picks, + { + placeHolder: localize('select product icon theme', "Select Product Icon Theme"), + onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setProductIconTheme(item.id, undefined)), + ignoreFocusLost + }); + return this.workbenchThemeService.setProductIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto'); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 98afce8fb5c..e3cb96713c6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -41,7 +41,6 @@ import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IAction, Action } from 'vs/base/common/actions'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; -import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IProductService } from 'vs/platform/product/common/productService'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; @@ -106,7 +105,6 @@ export class ExtensionsListView extends ViewPane { @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService protected contextService: IWorkspaceContextService, @IExperimentService private readonly experimentService: IExperimentService, - @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService, @IProductService protected readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, @@ -233,12 +231,10 @@ export class ExtensionsListView extends ViewPane { private async onContextMenu(e: IListContextMenuEvent): Promise { if (e.element) { const runningExtensions = await this.extensionService.getExtensions(); - const colorThemes = await this.workbenchThemeService.getColorThemes(); - const fileIconThemes = await this.workbenchThemeService.getFileIconThemes(); const manageExtensionAction = this.instantiationService.createInstance(ManageExtensionAction); manageExtensionAction.extension = e.element; if (manageExtensionAction.enabled) { - const groups = manageExtensionAction.getActionGroups(runningExtensions, colorThemes, fileIconThemes); + const groups = await manageExtensionAction.getActionGroups(runningExtensions); let actions: IAction[] = []; for (const menuActions of groups) { actions = [...actions, ...menuActions, new Separator()]; @@ -882,7 +878,6 @@ export class ServerExtensionsView extends ExtensionsListView { @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IExperimentService experimentService: IExperimentService, - @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, @IProductService productService: IProductService, @@ -893,7 +888,7 @@ export class ServerExtensionsView extends ExtensionsListView { @IPreferencesService preferencesService: IPreferencesService, ) { options.server = server; - super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService, productService, contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); + super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, telemetryService, configurationService, contextService, experimentService, extensionManagementServerService, productService, contextKeyService, viewDescriptorService, menuService, openerService, preferencesService); this._register(onDidChangeTitle(title => this.updateTitle(title))); } diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 6b76ac761fa..587408c3248 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -10,18 +10,19 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IWorkbenchThemeService, ThemeSettings, IWorkbenchColorTheme, IWorkbenchFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IWorkbenchTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Color } from 'vs/base/common/color'; -import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { LIGHT, DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { colorThemeSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { DEFAULT_PRODUCT_ICON_THEME_ID } from 'vs/workbench/services/themes/browser/productIconThemeData'; export class SelectColorThemeAction extends Action { @@ -34,8 +35,7 @@ export class SelectColorThemeAction extends Action { @IQuickInputService private readonly quickInputService: IQuickInputService, @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, - @IViewletService private readonly viewletService: IViewletService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IViewletService private readonly viewletService: IViewletService ) { super(id, label); } @@ -60,13 +60,8 @@ export class SelectColorThemeAction extends Action { selectThemeTimeout = window.setTimeout(() => { selectThemeTimeout = undefined; const themeId = theme && theme.id !== undefined ? theme.id : currentTheme.id; - let target: ConfigurationTarget | undefined = undefined; - if (applyTheme) { - const confValue = this.configurationService.inspect(ThemeSettings.COLOR_THEME); - target = typeof confValue.workspaceValue !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; - } - this.themeService.setColorTheme(themeId, target).then(undefined, + this.themeService.setColorTheme(themeId, applyTheme ? 'auto' : undefined).then(undefined, err => { onUnexpectedError(err); this.themeService.setColorTheme(currentTheme.id, undefined); @@ -108,7 +103,83 @@ export class SelectColorThemeAction extends Action { } } -class SelectIconThemeAction extends Action { +abstract class AbstractIconThemeAction extends Action { + constructor( + id: string, + label: string, + private readonly quickInputService: IQuickInputService, + private readonly extensionGalleryService: IExtensionGalleryService, + private readonly viewletService: IViewletService + + ) { + super(id, label); + } + + protected abstract get builtInEntry(): QuickPickInput; + protected abstract get installMessage(): string | undefined; + protected abstract get placeholderMessage(): string; + protected abstract get marketplaceTag(): string; + + protected abstract setTheme(id: string, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise; + + protected pick(themes: IWorkbenchTheme[], currentTheme: IWorkbenchTheme) { + let picks: QuickPickInput[] = [this.builtInEntry]; + picks = picks.concat( + toEntries(themes), + configurationEntries(this.extensionGalleryService, this.installMessage) + ); + + let selectThemeTimeout: number | undefined; + + const selectTheme = (theme: ThemeItem, applyTheme: boolean) => { + if (selectThemeTimeout) { + clearTimeout(selectThemeTimeout); + } + selectThemeTimeout = window.setTimeout(() => { + selectThemeTimeout = undefined; + const themeId = theme && theme.id !== undefined ? theme.id : currentTheme.id; + this.setTheme(themeId, applyTheme ? 'auto' : undefined).then(undefined, + err => { + onUnexpectedError(err); + this.setTheme(currentTheme.id, undefined); + } + ); + }, applyTheme ? 0 : 200); + }; + + return new Promise((s, _) => { + let isCompleted = false; + + const autoFocusIndex = firstIndex(picks, p => isItem(p) && p.id === currentTheme.id); + const quickpick = this.quickInputService.createQuickPick(); + quickpick.items = picks; + quickpick.placeholder = this.placeholderMessage; + quickpick.activeItems = [picks[autoFocusIndex] as ThemeItem]; + quickpick.canSelectMany = false; + quickpick.onDidAccept(_ => { + const theme = quickpick.activeItems[0]; + if (!theme || typeof theme.id === 'undefined') { // 'pick in marketplace' entry + openExtensionViewlet(this.viewletService, `${this.marketplaceTag} ${quickpick.value}`); + } else { + selectTheme(theme, true); + } + isCompleted = true; + quickpick.hide(); + s(); + }); + quickpick.onDidChangeActive(themes => selectTheme(themes[0], false)); + quickpick.onDidHide(() => { + if (!isCompleted) { + selectTheme(currentTheme, true); + s(); + } + }); + quickpick.show(); + }); + } +} + +class SelectFileIconThemeAction extends AbstractIconThemeAction { static readonly ID = 'workbench.action.selectIconTheme'; static readonly LABEL = localize('selectIconTheme.label', "File Icon Theme"); @@ -116,84 +187,61 @@ class SelectIconThemeAction extends Action { constructor( id: string, label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService, + @IQuickInputService quickInputService: IQuickInputService, @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, - @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, - @IViewletService private readonly viewletService: IViewletService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, + @IViewletService viewletService: IViewletService ) { - super(id, label); + super(id, label, quickInputService, extensionGalleryService, viewletService); } - run(): Promise { - return this.themeService.getFileIconThemes().then(themes => { - const currentTheme = this.themeService.getFileIconTheme(); + protected builtInEntry: QuickPickInput = { id: '', label: localize('noIconThemeLabel', 'None'), description: localize('noIconThemeDesc', 'Disable file icons') }; + protected installMessage = localize('installIconThemes', "Install Additional File Icon Themes..."); + protected placeholderMessage = localize('themes.selectIconTheme', "Select File Icon Theme"); + protected marketplaceTag = 'tag:icon-theme'; + protected setTheme(id: string, settingsTarget: ConfigurationTarget | undefined | 'auto') { + return this.themeService.setFileIconTheme(id, settingsTarget); + } - let picks: QuickPickInput[] = [{ id: '', label: localize('noIconThemeLabel', 'None'), description: localize('noIconThemeDesc', 'Disable file icons') }]; - picks = picks.concat( - toEntries(themes), - configurationEntries(this.extensionGalleryService, localize('installIconThemes', "Install Additional File Icon Themes...")) - ); - - let selectThemeTimeout: number | undefined; - - const selectTheme = (theme: ThemeItem, applyTheme: boolean) => { - if (selectThemeTimeout) { - clearTimeout(selectThemeTimeout); - } - selectThemeTimeout = window.setTimeout(() => { - selectThemeTimeout = undefined; - const themeId = theme && theme.id !== undefined ? theme.id : currentTheme.id; - let target: ConfigurationTarget | undefined = undefined; - if (applyTheme) { - const confValue = this.configurationService.inspect(ThemeSettings.ICON_THEME); - target = typeof confValue.workspaceValue !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; - } - this.themeService.setFileIconTheme(themeId, target).then(undefined, - err => { - onUnexpectedError(err); - this.themeService.setFileIconTheme(currentTheme.id, undefined); - } - ); - }, applyTheme ? 0 : 200); - }; - - return new Promise((s, _) => { - let isCompleted = false; - - const autoFocusIndex = firstIndex(picks, p => isItem(p) && p.id === currentTheme.id); - const quickpick = this.quickInputService.createQuickPick(); - quickpick.items = picks; - quickpick.placeholder = localize('themes.selectIconTheme', "Select File Icon Theme"); - quickpick.activeItems = [picks[autoFocusIndex] as ThemeItem]; - quickpick.canSelectMany = false; - quickpick.onDidAccept(_ => { - const theme = quickpick.activeItems[0]; - if (!theme || typeof theme.id === 'undefined') { // 'pick in marketplace' entry - openExtensionViewlet(this.viewletService, `tag:icon-theme ${quickpick.value}`); - } else { - selectTheme(theme, true); - } - isCompleted = true; - quickpick.hide(); - s(); - }); - quickpick.onDidChangeActive(themes => selectTheme(themes[0], false)); - quickpick.onDidHide(() => { - if (!isCompleted) { - selectTheme(currentTheme, true); - s(); - } - }); - quickpick.show(); - }); - }); + async run(): Promise { + this.pick(await this.themeService.getFileIconThemes(), this.themeService.getFileIconTheme()); } } -function configurationEntries(extensionGalleryService: IExtensionGalleryService, label: string): QuickPickInput[] { - if (extensionGalleryService.isEnabled()) { + +class SelectProductIconThemeAction extends AbstractIconThemeAction { + + static readonly ID = 'workbench.action.selectProductIconTheme'; + static readonly LABEL = localize('selectProductIconTheme.label', "Product Icon Theme"); + + constructor( + id: string, + label: string, + @IQuickInputService quickInputService: IQuickInputService, + @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, + @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, + @IViewletService viewletService: IViewletService + + ) { + super(id, label, quickInputService, extensionGalleryService, viewletService); + } + + protected builtInEntry: QuickPickInput = { id: DEFAULT_PRODUCT_ICON_THEME_ID, label: localize('defaultProductIconThemeLabel', 'Default') }; + protected installMessage = undefined; //localize('installProductIconThemes', "Install Additional Product Icon Themes..."); + protected placeholderMessage = localize('themes.selectProductIconTheme', "Select Product Icon Theme"); + protected marketplaceTag = 'tag:product-icon-theme'; + protected setTheme(id: string, settingsTarget: ConfigurationTarget | undefined | 'auto') { + return this.themeService.setProductIconTheme(id, settingsTarget); + } + + async run(): Promise { + this.pick(await this.themeService.getProductIconThemes(), this.themeService.getProductIconTheme()); + } +} + +function configurationEntries(extensionGalleryService: IExtensionGalleryService, label: string | undefined): QuickPickInput[] { + if (extensionGalleryService.isEnabled() && label !== undefined) { return [ { type: 'separator' @@ -227,8 +275,8 @@ function isItem(i: QuickPickInput): i is ThemeItem { return (i)['type'] !== 'separator'; } -function toEntries(themes: Array, label?: string): QuickPickInput[] { - const toEntry = (theme: IWorkbenchColorTheme | IWorkbenchFileIconTheme): ThemeItem => ({ id: theme.id, label: theme.label, description: theme.description }); +function toEntries(themes: Array, label?: string): QuickPickInput[] { + const toEntry = (theme: IWorkbenchTheme): ThemeItem => ({ id: theme.id, label: theme.label, description: theme.description }); const sorter = (t1: ThemeItem, t2: ThemeItem) => t1.label.localeCompare(t2.label); let entries: QuickPickInput[] = themes.map(toEntry).sort(sorter); if (entries.length > 0 && label) { @@ -288,8 +336,11 @@ const category = localize('preferences', "Preferences"); const colorThemeDescriptor = SyncActionDescriptor.create(SelectColorThemeAction, SelectColorThemeAction.ID, SelectColorThemeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_T) }); Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(colorThemeDescriptor, 'Preferences: Color Theme', category); -const iconThemeDescriptor = SyncActionDescriptor.create(SelectIconThemeAction, SelectIconThemeAction.ID, SelectIconThemeAction.LABEL); -Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(iconThemeDescriptor, 'Preferences: File Icon Theme', category); +const fileIconThemeDescriptor = SyncActionDescriptor.create(SelectFileIconThemeAction, SelectFileIconThemeAction.ID, SelectFileIconThemeAction.LABEL); +Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(fileIconThemeDescriptor, 'Preferences: File Icon Theme', category); + +const productIconThemeDescriptor = SyncActionDescriptor.create(SelectProductIconThemeAction, SelectProductIconThemeAction.ID, SelectProductIconThemeAction.LABEL); +Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(productIconThemeDescriptor, 'Preferences: Product Icon Theme', category); const developerCategory = localize('developer', "Developer"); @@ -309,7 +360,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { group: '4_themes', command: { - id: SelectIconThemeAction.ID, + id: SelectFileIconThemeAction.ID, title: localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme") }, order: 2 @@ -327,7 +378,7 @@ MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '4_themes', command: { - id: SelectIconThemeAction.ID, + id: SelectFileIconThemeAction.ID, title: localize('themes.selectIconTheme.label', "File Icon Theme") }, order: 2 diff --git a/src/vs/workbench/services/themes/browser/productIconThemeData.ts b/src/vs/workbench/services/themes/browser/productIconThemeData.ts index a189eb6823f..092732fc660 100644 --- a/src/vs/workbench/services/themes/browser/productIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/productIconThemeData.ts @@ -86,7 +86,7 @@ export class ProductIconThemeData implements IWorkbenchProductIconTheme { static get defaultTheme(): ProductIconThemeData { let themeData = ProductIconThemeData._defaultProductIconTheme; if (!themeData) { - themeData = ProductIconThemeData._defaultProductIconTheme = new ProductIconThemeData(DEFAULT_PRODUCT_ICON_THEME_ID, nls.localize('defaultTheme', 'Default theme'), DEFAULT_PRODUCT_ICON_THEME_SETTING_VALUE); + themeData = ProductIconThemeData._defaultProductIconTheme = new ProductIconThemeData(DEFAULT_PRODUCT_ICON_THEME_ID, nls.localize('defaultTheme', 'Default'), DEFAULT_PRODUCT_ICON_THEME_SETTING_VALUE); themeData.isLoaded = true; themeData.extensionData = undefined; themeData.watch = false; diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 43b5c261630..49764e33760 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -263,7 +263,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (e.affectsConfiguration(ThemeSettings.PREFERRED_HC_THEME) && this.getPreferredColorScheme() === HIGH_CONTRAST) { this.applyPreferredColorTheme(HIGH_CONTRAST); } - if (e.affectsConfiguration(ThemeSettings.ICON_THEME)) { + if (e.affectsConfiguration(ThemeSettings.FILE_ICON_THEME)) { this.restoreFileIconTheme(); } if (e.affectsConfiguration(ThemeSettings.PRODUCT_ICON_THEME)) { diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index a5aac2386f4..c71c74cc6e1 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -78,11 +78,10 @@ const colorCustomizationsSchema: IConfigurationPropertySchema = { } }] }; - const fileIconThemeSettingSchema: IConfigurationPropertySchema = { type: ['string', 'null'], default: DEFAULT_FILE_ICON_THEME_SETTING_VALUE, - description: nls.localize('iconTheme', "Specifies the icon theme used in the workbench or 'null' to not show any file icons."), + description: nls.localize('iconTheme', "Specifies the file icon theme used in the workbench or 'null' to not show any file icons."), enum: [null], enumDescriptions: [nls.localize('noIconThemeDesc', 'No file icons')], errorMessage: nls.localize('iconThemeError', "File icon theme is unknown or not installed.") @@ -106,7 +105,7 @@ const themeSettingsConfiguration: IConfigurationNode = { [ThemeSettings.PREFERRED_LIGHT_THEME]: preferredLightThemeSettingSchema, [ThemeSettings.PREFERRED_HC_THEME]: preferredHCThemeSettingSchema, [ThemeSettings.DETECT_COLOR_SCHEME]: detectColorSchemeSettingSchema, - [ThemeSettings.ICON_THEME]: fileIconThemeSettingSchema, + [ThemeSettings.FILE_ICON_THEME]: fileIconThemeSettingSchema, [ThemeSettings.COLOR_CUSTOMIZATIONS]: colorCustomizationsSchema, [ThemeSettings.PRODUCT_ICON_THEME]: productIconThemeSettingSchema } @@ -212,7 +211,7 @@ export class ThemeConfiguration { } public get fileIconTheme(): string | null { - return this.configurationService.getValue(ThemeSettings.ICON_THEME); + return this.configurationService.getValue(ThemeSettings.FILE_ICON_THEME); } public get productIconTheme(): string { @@ -237,7 +236,7 @@ export class ThemeConfiguration { } public async setFileIconTheme(theme: IWorkbenchFileIconTheme, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { - await this.writeConfiguration(ThemeSettings.ICON_THEME, theme.settingsId, settingsTarget); + await this.writeConfiguration(ThemeSettings.FILE_ICON_THEME, theme.settingsId, settingsTarget); return theme; } @@ -257,6 +256,8 @@ export class ThemeConfiguration { settingsTarget = ConfigurationTarget.WORKSPACE_FOLDER; } else if (!types.isUndefined(settings.workspaceValue)) { settingsTarget = ConfigurationTarget.WORKSPACE; + } else if (!types.isUndefined(settings.userRemote)) { + settingsTarget = ConfigurationTarget.USER_REMOTE; } else { settingsTarget = ConfigurationTarget.USER; } @@ -271,12 +272,11 @@ export class ThemeConfiguration { } value = undefined; // remove configuration from user settings } - } else if (settingsTarget === ConfigurationTarget.WORKSPACE || settingsTarget === ConfigurationTarget.WORKSPACE_FOLDER) { + } else if (settingsTarget === ConfigurationTarget.WORKSPACE || settingsTarget === ConfigurationTarget.WORKSPACE_FOLDER || settingsTarget === ConfigurationTarget.USER_REMOTE) { if (value === settings.value) { return Promise.resolve(undefined); // nothing to do } } return this.configurationService.updateValue(key, value, settingsTarget); } - } diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index cf95aeb4451..e6459c58672 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -20,7 +20,8 @@ export const HC_THEME_ID = 'Default High Contrast'; export enum ThemeSettings { COLOR_THEME = 'workbench.colorTheme', - ICON_THEME = 'workbench.iconTheme', + FILE_ICON_THEME = 'workbench.iconTheme', + PRODUCT_ICON_THEME = 'workbench.productIconTheme', COLOR_CUSTOMIZATIONS = 'workbench.colorCustomizations', TOKEN_COLOR_CUSTOMIZATIONS = 'editor.tokenColorCustomizations', TOKEN_COLOR_CUSTOMIZATIONS_EXPERIMENTAL = 'editor.tokenColorCustomizationsExperimental', @@ -29,18 +30,19 @@ export enum ThemeSettings { PREFERRED_LIGHT_THEME = 'workbench.preferredLightColorTheme', PREFERRED_HC_THEME = 'workbench.preferredHighContrastColorTheme', DETECT_COLOR_SCHEME = 'window.autoDetectColorScheme', - DETECT_HC = 'window.autoDetectHighContrast', - - PRODUCT_ICON_THEME = 'workbench.productIconTheme' + DETECT_HC = 'window.autoDetectHighContrast' } -export interface IWorkbenchColorTheme extends IColorTheme { +export interface IWorkbenchTheme { readonly id: string; readonly label: string; - readonly settingsId: string; readonly extensionData?: ExtensionData; readonly description?: string; - readonly isLoaded: boolean; + readonly settingsId: string | null; +} + +export interface IWorkbenchColorTheme extends IWorkbenchTheme, IColorTheme { + readonly settingsId: string; readonly tokenColors: ITextMateThemingRule[]; } @@ -48,44 +50,28 @@ export interface IColorMap { [id: string]: Color; } -export interface IWorkbenchFileIconTheme extends IFileIconTheme { - readonly id: string; - readonly label: string; - readonly settingsId: string | null; - readonly description?: string; - readonly extensionData?: ExtensionData; - - readonly isLoaded: boolean; - readonly hasFileIcons: boolean; - readonly hasFolderIcons: boolean; - readonly hidesExplorerArrows: boolean; +export interface IWorkbenchFileIconTheme extends IWorkbenchTheme, IFileIconTheme { } -export interface IWorkbenchProductIconTheme { - readonly id: string; - readonly label: string; +export interface IWorkbenchProductIconTheme extends IWorkbenchTheme { readonly settingsId: string; - readonly description?: string; - readonly extensionData?: ExtensionData; - - readonly isLoaded: boolean; } export interface IWorkbenchThemeService extends IThemeService { _serviceBrand: undefined; - setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; + setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise; getColorTheme(): IWorkbenchColorTheme; getColorThemes(): Promise; onDidColorThemeChange: Event; restoreColorTheme(): void; - setFileIconTheme(iconThemeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; + setFileIconTheme(iconThemeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise; getFileIconTheme(): IWorkbenchFileIconTheme; getFileIconThemes(): Promise; onDidFileIconThemeChange: Event; - setProductIconTheme(iconThemeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; + setProductIconTheme(iconThemeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise; getProductIconTheme(): IWorkbenchProductIconTheme; getProductIconThemes(): Promise; onDidProductIconThemeChange: Event;