From 256f2e0965758b9b3b14e78ed6485339ae0c5dec Mon Sep 17 00:00:00 2001 From: Marvin Heilemann Date: Mon, 9 Dec 2019 16:41:29 +0100 Subject: [PATCH 1/8] First working version of native auto switching workspace theme This implements three new options to the workspace scope. One to enable/ disable the auto switch functionality and two others to set the dark and light theme. --- .../themes/browser/workbenchThemeService.ts | 400 ++++++++++++++---- .../themes/common/workbenchThemeService.ts | 36 +- 2 files changed, 344 insertions(+), 92 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index c0172a4cdd808..b567c70281fab 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -6,13 +6,41 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { + IWorkbenchThemeService, + IColorTheme, + ITokenColorCustomizations, + IFileIconTheme, + ExtensionData, + VS_LIGHT_THEME, + VS_DARK_THEME, + VS_HC_THEME, + COLOR_THEME_SETTING, + ICON_THEME_SETTING, + CUSTOM_WORKBENCH_COLORS_SETTING, + CUSTOM_EDITOR_COLORS_SETTING, + DETECT_HC_SETTING, + HC_THEME_ID, + IColorCustomizations, + CUSTOM_EDITOR_TOKENSTYLES_SETTING, + IExperimentalTokenStyleCustomizations, + COLOR_THEME_DARK_SETTING, + COLOR_THEME_LIGHT_SETTING, + DETECT_AS_SETTING, + ColorScheme, + WINDOW_MATCH_PREFERS_COLOR_SCHEME +} from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; import * as errors from 'vs/base/common/errors'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { + IConfigurationRegistry, + Extensions as ConfigurationExtensions, + IConfigurationPropertySchema, + IConfigurationNode +} from 'vs/platform/configuration/common/configurationRegistry'; import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData'; import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; @@ -27,7 +55,11 @@ import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; +import { + textmateColorsSchemaId, + registerColorThemeSchemas, + textmateColorSettingsSchemaId +} from 'vs/workbench/services/themes/common/colorThemeSchema'; import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry'; import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -39,6 +71,9 @@ import { IExtensionResourceLoaderService } from 'vs/workbench/services/extension const DEFAULT_THEME_ID = 'vs-dark vscode-theme-defaults-themes-dark_plus-json'; const DEFAULT_THEME_SETTING_VALUE = 'Default Dark+'; +const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; +const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+'; +const DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE = true; const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData'; const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData'; @@ -58,11 +93,16 @@ const themingRegistry = Registry.as(ThemingExtensions.ThemingC function validateThemeId(theme: string): string { // migrations switch (theme) { - case VS_LIGHT_THEME: return `vs ${defaultThemeExtensionId}-themes-light_vs-json`; - case VS_DARK_THEME: return `vs-dark ${defaultThemeExtensionId}-themes-dark_vs-json`; - case VS_HC_THEME: return `hc-black ${defaultThemeExtensionId}-themes-hc_black-json`; - case `vs ${oldDefaultThemeExtensionId}-themes-light_plus-tmTheme`: return `vs ${defaultThemeExtensionId}-themes-light_plus-json`; - case `vs-dark ${oldDefaultThemeExtensionId}-themes-dark_plus-tmTheme`: return `vs-dark ${defaultThemeExtensionId}-themes-dark_plus-json`; + case VS_LIGHT_THEME: + return `vs ${defaultThemeExtensionId}-themes-light_vs-json`; + case VS_DARK_THEME: + return `vs-dark ${defaultThemeExtensionId}-themes-dark_vs-json`; + case VS_HC_THEME: + return `hc-black ${defaultThemeExtensionId}-themes-hc_black-json`; + case `vs ${oldDefaultThemeExtensionId}-themes-light_plus-tmTheme`: + return `vs ${defaultThemeExtensionId}-themes-light_plus-json`; + case `vs-dark ${oldDefaultThemeExtensionId}-themes-dark_plus-tmTheme`: + return `vs-dark ${defaultThemeExtensionId}-themes-dark_plus-json`; } return theme; } @@ -71,6 +111,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { _serviceBrand: undefined; private colorThemeStore: ColorThemeStore; + private autoSwitchColorTheme = DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE; + private autoSwitchColorThemeListener: MediaQueryList | undefined = undefined; private currentColorTheme: ColorThemeData; private container: HTMLElement; private readonly onColorThemeChange: Emitter; @@ -94,7 +136,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private get tokenStylesCustomizations(): IExperimentalTokenStyleCustomizations { - return this.configurationService.getValue(CUSTOM_EDITOR_TOKENSTYLES_SETTING) || {}; + return ( + this.configurationService.getValue(CUSTOM_EDITOR_TOKENSTYLES_SETTING) || {} + ); } constructor( @@ -107,13 +151,13 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService, @IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService ) { - this.container = layoutService.getWorkbenchContainer(); this.colorThemeStore = new ColorThemeStore(extensionService); this.onFileIconThemeChange = new Emitter(); this.iconThemeStore = new FileIconThemeStore(extensionService); this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); + this.onPreferColorSchemeChange = this.onPreferColorSchemeChange.bind(this); this.currentColorTheme = ColorThemeData.createUnloadedTheme(''); this.currentIconTheme = FileIconThemeData.createUnloadedTheme(''); @@ -146,9 +190,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } - this.initialize().then(undefined, errors.onUnexpectedError).then(_ => { - this.installConfigurationListener(); - }); + this.initialize() + .then(undefined, errors.onUnexpectedError) + .then(_ => { + this.installConfigurationListener(); + this.installColorThemeSwitch(); + }); let prevColorId: string | undefined = undefined; @@ -177,7 +224,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { tokenColorCustomizationSchema.allOf![1] = themeSpecificTokenColors; experimentalTokenStylingCustomizationSchema.allOf![1] = themeSpecificTokenStyling; - configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration); + configurationRegistry.notifyConfigurationSchemaUpdated( + themeSettingsConfiguration, + tokenColorCustomizationConfiguration + ); if (this.currentColorTheme.isLoaded) { const themeData = await this.colorThemeStore.findThemeData(this.currentColorTheme.id); @@ -186,7 +236,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { prevColorId = this.currentColorTheme.id; this.setColorTheme(DEFAULT_THEME_ID, 'auto'); } else { - if (this.currentColorTheme.id === DEFAULT_THEME_ID && !types.isUndefined(prevColorId) && await this.colorThemeStore.findThemeData(prevColorId)) { + if ( + this.currentColorTheme.id === DEFAULT_THEME_ID && + !types.isUndefined(prevColorId) && + (await this.colorThemeStore.findThemeData(prevColorId)) + ) { // restore color this.setColorTheme(prevColorId, 'auto'); prevColorId = undefined; @@ -200,7 +254,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { let prevFileIconId: string | undefined = undefined; this.iconThemeStore.onDidChange(async event => { iconThemeSettingSchema.enum = [null, ...event.themes.map(t => t.settingsId)]; - iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions![0], ...event.themes.map(t => t.description || '')]; + iconThemeSettingSchema.enumDescriptions = [ + iconThemeSettingSchema.enumDescriptions![0], + ...event.themes.map(t => t.description || '') + ]; configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); if (this.currentIconTheme.isLoaded) { @@ -211,7 +268,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.setFileIconTheme(DEFAULT_ICON_THEME_ID, 'auto'); } else { // restore color - if (this.currentIconTheme.id === DEFAULT_ICON_THEME_ID && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) { + if ( + this.currentIconTheme.id === DEFAULT_ICON_THEME_ID && + !types.isUndefined(prevFileIconId) && + (await this.iconThemeStore.findThemeData(prevFileIconId)) + ) { this.setFileIconTheme(prevFileIconId, 'auto'); prevFileIconId = undefined; } else { @@ -222,10 +283,18 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); this.fileService.onFileChanges(async e => { - if (this.watchedColorThemeLocation && this.currentColorTheme && e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED)) { + if ( + this.watchedColorThemeLocation && + this.currentColorTheme && + e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED) + ) { this.reloadCurrentColorTheme(); } - if (this.watchedIconThemeLocation && this.currentIconTheme && e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED)) { + if ( + this.watchedIconThemeLocation && + this.currentIconTheme && + e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED) + ) { this.reloadCurrentFileIconTheme(); } }); @@ -248,17 +317,20 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private initialize(): Promise<[IColorTheme | null, IFileIconTheme | null]> { - let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); + let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); + + let detectThemeAutoSwitch = this.configurationService.getValue(DETECT_AS_SETTING); + this.autoSwitchColorTheme = detectThemeAutoSwitch; + if (detectThemeAutoSwitch) { + colorThemeSetting = this.getPreferredTheme(); + } - let colorThemeSetting: string; + let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); if (this.environmentService.configuration.highContrast && detectHCThemeSetting) { colorThemeSetting = HC_THEME_ID; - } else { - colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); } - let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); - const extDevLocs = this.environmentService.extensionDevelopmentLocationURI; let uri: URI | undefined; if (extDevLocs && extDevLocs.length > 0) { @@ -267,7 +339,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } return Promise.all([ - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + this.getColorThemeData(colorThemeSetting).then(theme => { return this.colorThemeStore.findThemeDataByParentLocation(uri).then(devThemes => { if (devThemes.length) { return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY); @@ -284,20 +356,76 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); } }); - }), + }) ]); } + private installColorThemeSwitch() { + console.log('INSTALL'); + this.autoSwitchColorThemeListener = window.matchMedia(WINDOW_MATCH_PREFERS_COLOR_SCHEME); + this.autoSwitchColorThemeListener.addListener(this.onPreferColorSchemeChange); + console.log('INSTALLED'); + } + + private deinstallColorThemeSwitch() { + console.log('DEINSTALL'); + if (this.autoSwitchColorThemeListener) { + this.autoSwitchColorThemeListener.removeListener(this.onPreferColorSchemeChange); + this.configurationService.updateValue(DETECT_AS_SETTING, false); + console.log('DEINSTALLED'); + } + } + + private onPreferColorSchemeChange({ matches }: MediaQueryListEvent) { + console.log('onPreferColorSchemeChange', matches); + let themeName = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); + if (matches) { + // prefers dark mode + themeName = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); + } + this.setTheme(themeName); + } + + private getPreferredTheme(): string { + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); + const darkMode = this.getPreferredColorScheme() === ColorScheme.DARK; + if (darkMode) { + colorThemeSetting = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); + } + return colorThemeSetting; + } + + private setTheme(themeName: string) { + this.getColorThemeData(themeName).then(theme => { + if (theme) { + this.setColorTheme(theme.id, undefined); + this.configurationService.updateValue(COLOR_THEME_SETTING, themeName); + } + }); + } + private installConfigurationListener() { this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(COLOR_THEME_SETTING)) { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { - if (theme) { - this.setColorTheme(theme.id, undefined); - } - }); + this.setTheme(colorThemeSetting); + this.deinstallColorThemeSwitch(); + } + } + if (e.affectsConfiguration(DETECT_AS_SETTING)) { + let autoSwitchColorTheme = this.configurationService.getValue(DETECT_AS_SETTING); + console.log(autoSwitchColorTheme); + if (this.autoSwitchColorTheme !== autoSwitchColorTheme) { + this.autoSwitchColorTheme = autoSwitchColorTheme; + console.log('HAS CHANGED'); + if (autoSwitchColorTheme) { + this.installColorThemeSwitch(); + let themeName = this.getPreferredTheme(); + this.setTheme(themeName); + } else { + this.deinstallColorThemeSwitch(); + } } } if (e.affectsConfiguration(ICON_THEME_SETTING)) { @@ -338,11 +466,33 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.colorThemeStore.getColorThemes(); } + public getColorThemeData(colorThemeSetting: string): Promise { + return new Promise(resolve => { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + resolve(theme); + }); + }); + } + + public getPreferredColorScheme(): ColorScheme { + const noPreference = window.matchMedia(`(prefers-color-scheme: ${ColorScheme.NO_PREFERENCE})`).matches; + const prefersDark = window.matchMedia(`(prefers-color-scheme: ${ColorScheme.DARK})`).matches; + if (noPreference) { + return ColorScheme.NO_PREFERENCE; + } else if (prefersDark) { + return ColorScheme.DARK; + } + return ColorScheme.LIGHT; + } + public getTheme(): ITheme { return this.getColorTheme(); } - public setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { + public setColorTheme( + themeId: string | undefined, + settingsTarget: ConfigurationTarget | undefined | 'auto' + ): Promise { if (!themeId) { return Promise.resolve(null); } @@ -357,24 +507,40 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return null; } const themeData = data; - return themeData.ensureLoaded(this.extensionResourceLoaderService).then(_ => { - if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { - this.currentColorTheme.clearCaches(); - // the loaded theme is identical to the perisisted theme. Don't need to send an event. - this.currentColorTheme = themeData; + return themeData.ensureLoaded(this.extensionResourceLoaderService).then( + _ => { + if ( + themeId === this.currentColorTheme.id && + !this.currentColorTheme.isLoaded && + this.currentColorTheme.hasEqualData(themeData) + ) { + this.currentColorTheme.clearCaches(); + // the loaded theme is identical to the perisisted theme. Don't need to send an event. + this.currentColorTheme = themeData; + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); + return Promise.resolve(themeData); + } themeData.setCustomColors(this.colorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations); themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); - return Promise.resolve(themeData); + this.updateDynamicCSSRules(themeData); + return this.applyTheme(themeData, settingsTarget); + }, + error => { + return Promise.reject( + new Error( + nls.localize( + 'error.cannotloadtheme', + 'Unable to load {0}: {1}', + themeData.location!.toString(), + error.message + ) + ) + ); } - themeData.setCustomColors(this.colorCustomizations); - themeData.setCustomTokenColors(this.tokenColorCustomizations); - themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); - this.updateDynamicCSSRules(themeData); - return this.applyTheme(themeData, settingsTarget); - }, error => { - return Promise.reject(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.location!.toString(), error.message))); - }); + ); }); } @@ -390,11 +556,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { public restoreColorTheme() { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { - if (theme) { - this.setColorTheme(theme.id, undefined); - } - }); + this.setTheme(colorThemeSetting); } } @@ -411,7 +573,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { _applyRules([...cssRules].join('\n'), colorThemeRulesClassName); } - private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget | undefined | 'auto', silent = false): Promise { + private applyTheme( + newTheme: ColorThemeData, + settingsTarget: ConfigurationTarget | undefined | 'auto', + silent = false + ): Promise { if (this.currentColorTheme.id) { removeClasses(this.container, this.currentColorTheme.id); } else { @@ -422,7 +588,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.currentColorTheme.clearCaches(); this.currentColorTheme = newTheme; if (!this.themingParticipantChangeListener) { - this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme)); + this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => + this.updateDynamicCSSRules(this.currentColorTheme) + ); } if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) { @@ -453,7 +621,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { - return this.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); + return this.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then( + _ => this.currentColorTheme + ); } return Promise.resolve(this.currentColorTheme); } @@ -464,11 +634,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { let key = themeType + themeData.extensionId; if (!this.themeExtensionsActivated.get(key)) { type ActivatePluginClassification = { - id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; - name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; - isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - themeId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + id: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; + name: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; + isBuiltin: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; + publisherDisplayName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; + themeId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; }; type ActivatePluginEvent = { id: string; @@ -501,7 +671,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.currentIconTheme; } - public setFileIconTheme(iconTheme: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { + public setFileIconTheme( + iconTheme: string | undefined, + settingsTarget: ConfigurationTarget | undefined | 'auto' + ): Promise { iconTheme = iconTheme || ''; if (iconTheme === this.currentIconTheme.id && this.currentIconTheme.isLoaded) { return this.writeFileIconConfiguration(settingsTarget); @@ -557,7 +730,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { dispose(this.watchedIconThemeDisposable); this.watchedIconThemeLocation = undefined; - if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { + if ( + iconThemeData.location && + (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI) + ) { this.watchedIconThemeLocation = iconThemeData.location; this.watchedIconThemeDisposable = this.fileService.watch(iconThemeData.location); } @@ -567,12 +743,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'fileIcon'); } this.onFileIconThemeChange.fire(this.currentIconTheme); - } - private writeFileIconConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { + private writeFileIconConfiguration( + settingsTarget: ConfigurationTarget | undefined | 'auto' + ): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { - return this.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme); + return this.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then( + _ => this.currentIconTheme + ); } return Promise.resolve(this.currentIconTheme); } @@ -598,7 +777,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } 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 + ) { if (value === settings.value) { return Promise.resolve(undefined); // nothing to do } @@ -617,7 +799,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } -function _applyIconTheme(data: FileIconThemeData, onApply: (theme: FileIconThemeData) => Promise): Promise { +function _applyIconTheme( + data: FileIconThemeData, + onApply: (theme: FileIconThemeData) => Promise +): Promise { _applyRules(data.styleSheetContent!, iconThemeRulesClassName); return onApply(data); } @@ -643,30 +828,49 @@ const configurationRegistry = Registry.as(ConfigurationE const colorThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', - description: nls.localize('colorTheme', "Specifies the color theme used in the workbench."), + description: nls.localize('colorTheme', 'Specifies the color theme used in the workbench.'), default: DEFAULT_THEME_SETTING_VALUE, - enum: [], - enumDescriptions: [], - errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), + errorMessage: nls.localize('colorThemeError', 'Theme is unknown or not installed.') +}; +const colorThemeDarkSettingSchema: IConfigurationPropertySchema = { + type: 'string', + description: nls.localize('colorThemeDark', 'Specifies the color theme used for a dark OS appearance.'), + default: DEFAULT_THEME_DARK_SETTING_VALUE, + errorMessage: nls.localize('colorThemeError', 'Theme is unknown or not installed.') +}; +const colorThemeLightSettingSchema: IConfigurationPropertySchema = { + type: 'string', + description: nls.localize('colorThemeLight', 'Specifies the color theme used for a light OS appearance.'), + default: DEFAULT_THEME_LIGHT_SETTING_VALUE, + errorMessage: nls.localize('colorThemeError', 'Theme is unknown or not installed.') +}; +const colorThemeAutoSwitchSettingSchema: IConfigurationPropertySchema = { + type: 'boolean', + description: nls.localize('colorThemeAutoSwitch', 'Changes the color theme based on the OS appearance.'), + default: DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE }; const iconThemeSettingSchema: IConfigurationPropertySchema = { type: ['string', 'null'], default: DEFAULT_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 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.") + errorMessage: nls.localize('iconThemeError', 'File icon theme is unknown or not installed.') }; const colorCustomizationsSchema: IConfigurationPropertySchema = { type: 'object', - description: nls.localize('workbenchColors', "Overrides colors from the currently selected color theme."), + description: nls.localize('workbenchColors', 'Overrides colors from the currently selected color theme.'), allOf: [{ $ref: workbenchColorsSchemaId }], default: {}, - defaultSnippets: [{ - body: { + defaultSnippets: [ + { + body: {} } - }] + ] }; const themeSettingsConfiguration: IConfigurationNode = { @@ -675,6 +879,9 @@ const themeSettingsConfiguration: IConfigurationNode = { type: 'object', properties: { [COLOR_THEME_SETTING]: colorThemeSettingSchema, + [COLOR_THEME_DARK_SETTING]: colorThemeDarkSettingSchema, + [COLOR_THEME_LIGHT_SETTING]: colorThemeLightSettingSchema, + [DETECT_AS_SETTING]: colorThemeAutoSwitchSettingSchema, [ICON_THEME_SETTING]: iconThemeSettingSchema, [CUSTOM_WORKBENCH_COLORS_SETTING]: colorCustomizationsSchema } @@ -699,26 +906,45 @@ function tokenGroupSettings(description: string): IJSONSchema { const tokenColorSchema: IJSONSchema = { properties: { - comments: tokenGroupSettings(nls.localize('editorColors.comments', "Sets the colors and styles for comments")), - strings: tokenGroupSettings(nls.localize('editorColors.strings', "Sets the colors and styles for strings literals.")), - keywords: tokenGroupSettings(nls.localize('editorColors.keywords', "Sets the colors and styles for keywords.")), - numbers: tokenGroupSettings(nls.localize('editorColors.numbers', "Sets the colors and styles for number literals.")), - types: tokenGroupSettings(nls.localize('editorColors.types', "Sets the colors and styles for type declarations and references.")), - functions: tokenGroupSettings(nls.localize('editorColors.functions', "Sets the colors and styles for functions declarations and references.")), - variables: tokenGroupSettings(nls.localize('editorColors.variables', "Sets the colors and styles for variables declarations and references.")), + comments: tokenGroupSettings(nls.localize('editorColors.comments', 'Sets the colors and styles for comments')), + strings: tokenGroupSettings( + nls.localize('editorColors.strings', 'Sets the colors and styles for strings literals.') + ), + keywords: tokenGroupSettings(nls.localize('editorColors.keywords', 'Sets the colors and styles for keywords.')), + numbers: tokenGroupSettings( + nls.localize('editorColors.numbers', 'Sets the colors and styles for number literals.') + ), + types: tokenGroupSettings( + nls.localize('editorColors.types', 'Sets the colors and styles for type declarations and references.') + ), + functions: tokenGroupSettings( + nls.localize('editorColors.functions', 'Sets the colors and styles for functions declarations and references.') + ), + variables: tokenGroupSettings( + nls.localize('editorColors.variables', 'Sets the colors and styles for variables declarations and references.') + ), textMateRules: { - description: nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).'), + description: nls.localize( + 'editorColors.textMateRules', + 'Sets colors and styles using textmate theming rules (advanced).' + ), $ref: textmateColorsSchemaId } } }; const tokenColorCustomizationSchema: IConfigurationPropertySchema = { - description: nls.localize('editorColors', "Overrides editor colors and font style from the currently selected color theme."), + description: nls.localize( + 'editorColors', + 'Overrides editor colors and font style from the currently selected color theme.' + ), default: {}, allOf: [tokenColorSchema] }; const experimentalTokenStylingCustomizationSchema: IConfigurationPropertySchema = { - description: nls.localize('editorColorsTokenStyles', "Overrides token color and styles from the currently selected color theme."), + description: nls.localize( + 'editorColorsTokenStyles', + 'Overrides token color and styles from the currently selected color theme.' + ), default: {}, allOf: [{ $ref: tokenStylingSchemaId }] }; diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 9937ca11be921..6577eb4a1d9b9 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -18,12 +18,22 @@ export const VS_HC_THEME = 'hc-black'; export const HC_THEME_ID = 'Default High Contrast'; export const COLOR_THEME_SETTING = 'workbench.colorTheme'; +export const COLOR_THEME_DARK_SETTING = 'workbench.colorThemeDark'; +export const COLOR_THEME_LIGHT_SETTING = 'workbench.colorThemeLight'; +export const DETECT_AS_SETTING = 'workbench.colorThemeAutoSwitch'; +export const WINDOW_MATCH_PREFERS_COLOR_SCHEME = '(prefers-color-scheme: dark)'; export const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; export const ICON_THEME_SETTING = 'workbench.iconTheme'; export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; export const CUSTOM_EDITOR_TOKENSTYLES_SETTING = 'editor.tokenColorCustomizationsExperimental'; +export enum ColorScheme { + LIGHT = 'light', + DARK = 'dark', + NO_PREFERENCE = 'no-preference' +} + export interface IColorTheme extends ITheme { readonly id: string; readonly label: string; @@ -53,13 +63,20 @@ export interface IFileIconTheme extends IIconTheme { export interface IWorkbenchThemeService extends IThemeService { _serviceBrand: undefined; - setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; + setColorTheme( + themeId: string | undefined, + settingsTarget: ConfigurationTarget | undefined + ): Promise; getColorTheme(): IColorTheme; getColorThemes(): Promise; + getColorThemeData(colorThemeSetting: string): Promise; onDidColorThemeChange: Event; restoreColorTheme(): void; - setFileIconTheme(iconThemeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; + setFileIconTheme( + iconThemeId: string | undefined, + settingsTarget: ConfigurationTarget | undefined + ): Promise; getFileIconTheme(): IFileIconTheme; getFileIconThemes(): Promise; onDidFileIconThemeChange: Event; @@ -70,7 +87,12 @@ export interface IColorCustomizations { } export interface ITokenColorCustomizations { - [groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITextMateThemingRule[]; + [groupIdOrThemeSettingsId: string]: + | string + | ITokenColorizationSetting + | ITokenColorCustomizations + | undefined + | ITextMateThemingRule[]; comments?: string | ITokenColorizationSetting; strings?: string | ITokenColorizationSetting; numbers?: string | ITokenColorizationSetting; @@ -82,7 +104,11 @@ export interface ITokenColorCustomizations { } export interface IExperimentalTokenStyleCustomizations { - [styleRuleOrThemeSettingsId: string]: string | ITokenColorizationSetting | IExperimentalTokenStyleCustomizations | undefined; + [styleRuleOrThemeSettingsId: string]: + | string + | ITokenColorizationSetting + | IExperimentalTokenStyleCustomizations + | undefined; } export interface ITextMateThemingRule { @@ -94,7 +120,7 @@ export interface ITextMateThemingRule { export interface ITokenColorizationSetting { foreground?: string; background?: string; - fontStyle?: string; /* [italic|underline|bold] */ + fontStyle?: string /* [italic|underline|bold] */; } export interface ExtensionData { From fc8777550a90e176a72cd895a993581ab53c0e0b Mon Sep 17 00:00:00 2001 From: Marvin Heilemann Date: Tue, 10 Dec 2019 08:44:53 +0100 Subject: [PATCH 2/8] Rolled back changes to better see my changes (thanks to wrong configured prettier) --- .../themes/browser/workbenchThemeService.ts | 402 ++++-------------- .../themes/common/workbenchThemeService.ts | 38 +- 2 files changed, 94 insertions(+), 346 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index b567c70281fab..c6c906e5cb027 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -6,41 +6,13 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { - IWorkbenchThemeService, - IColorTheme, - ITokenColorCustomizations, - IFileIconTheme, - ExtensionData, - VS_LIGHT_THEME, - VS_DARK_THEME, - VS_HC_THEME, - COLOR_THEME_SETTING, - ICON_THEME_SETTING, - CUSTOM_WORKBENCH_COLORS_SETTING, - CUSTOM_EDITOR_COLORS_SETTING, - DETECT_HC_SETTING, - HC_THEME_ID, - IColorCustomizations, - CUSTOM_EDITOR_TOKENSTYLES_SETTING, - IExperimentalTokenStyleCustomizations, - COLOR_THEME_DARK_SETTING, - COLOR_THEME_LIGHT_SETTING, - DETECT_AS_SETTING, - ColorScheme, - WINDOW_MATCH_PREFERS_COLOR_SCHEME -} from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; import * as errors from 'vs/base/common/errors'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { - IConfigurationRegistry, - Extensions as ConfigurationExtensions, - IConfigurationPropertySchema, - IConfigurationNode -} from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData'; import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; @@ -55,11 +27,7 @@ import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { - textmateColorsSchemaId, - registerColorThemeSchemas, - textmateColorSettingsSchemaId -} from 'vs/workbench/services/themes/common/colorThemeSchema'; +import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry'; import { tokenStylingSchemaId } from 'vs/platform/theme/common/tokenClassificationRegistry'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -71,9 +39,6 @@ import { IExtensionResourceLoaderService } from 'vs/workbench/services/extension const DEFAULT_THEME_ID = 'vs-dark vscode-theme-defaults-themes-dark_plus-json'; const DEFAULT_THEME_SETTING_VALUE = 'Default Dark+'; -const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; -const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+'; -const DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE = true; const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData'; const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData'; @@ -93,16 +58,11 @@ const themingRegistry = Registry.as(ThemingExtensions.ThemingC function validateThemeId(theme: string): string { // migrations switch (theme) { - case VS_LIGHT_THEME: - return `vs ${defaultThemeExtensionId}-themes-light_vs-json`; - case VS_DARK_THEME: - return `vs-dark ${defaultThemeExtensionId}-themes-dark_vs-json`; - case VS_HC_THEME: - return `hc-black ${defaultThemeExtensionId}-themes-hc_black-json`; - case `vs ${oldDefaultThemeExtensionId}-themes-light_plus-tmTheme`: - return `vs ${defaultThemeExtensionId}-themes-light_plus-json`; - case `vs-dark ${oldDefaultThemeExtensionId}-themes-dark_plus-tmTheme`: - return `vs-dark ${defaultThemeExtensionId}-themes-dark_plus-json`; + case VS_LIGHT_THEME: return `vs ${defaultThemeExtensionId}-themes-light_vs-json`; + case VS_DARK_THEME: return `vs-dark ${defaultThemeExtensionId}-themes-dark_vs-json`; + case VS_HC_THEME: return `hc-black ${defaultThemeExtensionId}-themes-hc_black-json`; + case `vs ${oldDefaultThemeExtensionId}-themes-light_plus-tmTheme`: return `vs ${defaultThemeExtensionId}-themes-light_plus-json`; + case `vs-dark ${oldDefaultThemeExtensionId}-themes-dark_plus-tmTheme`: return `vs-dark ${defaultThemeExtensionId}-themes-dark_plus-json`; } return theme; } @@ -111,8 +71,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { _serviceBrand: undefined; private colorThemeStore: ColorThemeStore; - private autoSwitchColorTheme = DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE; - private autoSwitchColorThemeListener: MediaQueryList | undefined = undefined; private currentColorTheme: ColorThemeData; private container: HTMLElement; private readonly onColorThemeChange: Emitter; @@ -136,9 +94,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private get tokenStylesCustomizations(): IExperimentalTokenStyleCustomizations { - return ( - this.configurationService.getValue(CUSTOM_EDITOR_TOKENSTYLES_SETTING) || {} - ); + return this.configurationService.getValue(CUSTOM_EDITOR_TOKENSTYLES_SETTING) || {}; } constructor( @@ -151,13 +107,13 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService, @IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService ) { + this.container = layoutService.getWorkbenchContainer(); this.colorThemeStore = new ColorThemeStore(extensionService); this.onFileIconThemeChange = new Emitter(); this.iconThemeStore = new FileIconThemeStore(extensionService); this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); - this.onPreferColorSchemeChange = this.onPreferColorSchemeChange.bind(this); this.currentColorTheme = ColorThemeData.createUnloadedTheme(''); this.currentIconTheme = FileIconThemeData.createUnloadedTheme(''); @@ -190,12 +146,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } - this.initialize() - .then(undefined, errors.onUnexpectedError) - .then(_ => { - this.installConfigurationListener(); - this.installColorThemeSwitch(); - }); + this.initialize().then(undefined, errors.onUnexpectedError).then(_ => { + this.installConfigurationListener(); + }); let prevColorId: string | undefined = undefined; @@ -224,10 +177,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { tokenColorCustomizationSchema.allOf![1] = themeSpecificTokenColors; experimentalTokenStylingCustomizationSchema.allOf![1] = themeSpecificTokenStyling; - configurationRegistry.notifyConfigurationSchemaUpdated( - themeSettingsConfiguration, - tokenColorCustomizationConfiguration - ); + configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration); if (this.currentColorTheme.isLoaded) { const themeData = await this.colorThemeStore.findThemeData(this.currentColorTheme.id); @@ -236,11 +186,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { prevColorId = this.currentColorTheme.id; this.setColorTheme(DEFAULT_THEME_ID, 'auto'); } else { - if ( - this.currentColorTheme.id === DEFAULT_THEME_ID && - !types.isUndefined(prevColorId) && - (await this.colorThemeStore.findThemeData(prevColorId)) - ) { + if (this.currentColorTheme.id === DEFAULT_THEME_ID && !types.isUndefined(prevColorId) && await this.colorThemeStore.findThemeData(prevColorId)) { // restore color this.setColorTheme(prevColorId, 'auto'); prevColorId = undefined; @@ -254,10 +200,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { let prevFileIconId: string | undefined = undefined; this.iconThemeStore.onDidChange(async event => { iconThemeSettingSchema.enum = [null, ...event.themes.map(t => t.settingsId)]; - iconThemeSettingSchema.enumDescriptions = [ - iconThemeSettingSchema.enumDescriptions![0], - ...event.themes.map(t => t.description || '') - ]; + iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions![0], ...event.themes.map(t => t.description || '')]; configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); if (this.currentIconTheme.isLoaded) { @@ -268,11 +211,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.setFileIconTheme(DEFAULT_ICON_THEME_ID, 'auto'); } else { // restore color - if ( - this.currentIconTheme.id === DEFAULT_ICON_THEME_ID && - !types.isUndefined(prevFileIconId) && - (await this.iconThemeStore.findThemeData(prevFileIconId)) - ) { + if (this.currentIconTheme.id === DEFAULT_ICON_THEME_ID && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) { this.setFileIconTheme(prevFileIconId, 'auto'); prevFileIconId = undefined; } else { @@ -283,18 +222,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); this.fileService.onFileChanges(async e => { - if ( - this.watchedColorThemeLocation && - this.currentColorTheme && - e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED) - ) { + if (this.watchedColorThemeLocation && this.currentColorTheme && e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED)) { this.reloadCurrentColorTheme(); } - if ( - this.watchedIconThemeLocation && - this.currentIconTheme && - e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED) - ) { + if (this.watchedIconThemeLocation && this.currentIconTheme && e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED)) { this.reloadCurrentFileIconTheme(); } }); @@ -317,20 +248,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private initialize(): Promise<[IColorTheme | null, IFileIconTheme | null]> { - let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); - let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); - - let detectThemeAutoSwitch = this.configurationService.getValue(DETECT_AS_SETTING); - this.autoSwitchColorTheme = detectThemeAutoSwitch; - if (detectThemeAutoSwitch) { - colorThemeSetting = this.getPreferredTheme(); - } - let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); + + let colorThemeSetting: string; if (this.environmentService.configuration.highContrast && detectHCThemeSetting) { colorThemeSetting = HC_THEME_ID; + } else { + colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); } + let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); + const extDevLocs = this.environmentService.extensionDevelopmentLocationURI; let uri: URI | undefined; if (extDevLocs && extDevLocs.length > 0) { @@ -339,7 +267,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } return Promise.all([ - this.getColorThemeData(colorThemeSetting).then(theme => { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { return this.colorThemeStore.findThemeDataByParentLocation(uri).then(devThemes => { if (devThemes.length) { return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY); @@ -356,76 +284,20 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); } }); - }) + }), ]); } - private installColorThemeSwitch() { - console.log('INSTALL'); - this.autoSwitchColorThemeListener = window.matchMedia(WINDOW_MATCH_PREFERS_COLOR_SCHEME); - this.autoSwitchColorThemeListener.addListener(this.onPreferColorSchemeChange); - console.log('INSTALLED'); - } - - private deinstallColorThemeSwitch() { - console.log('DEINSTALL'); - if (this.autoSwitchColorThemeListener) { - this.autoSwitchColorThemeListener.removeListener(this.onPreferColorSchemeChange); - this.configurationService.updateValue(DETECT_AS_SETTING, false); - console.log('DEINSTALLED'); - } - } - - private onPreferColorSchemeChange({ matches }: MediaQueryListEvent) { - console.log('onPreferColorSchemeChange', matches); - let themeName = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); - if (matches) { - // prefers dark mode - themeName = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); - } - this.setTheme(themeName); - } - - private getPreferredTheme(): string { - let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); - const darkMode = this.getPreferredColorScheme() === ColorScheme.DARK; - if (darkMode) { - colorThemeSetting = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); - } - return colorThemeSetting; - } - - private setTheme(themeName: string) { - this.getColorThemeData(themeName).then(theme => { - if (theme) { - this.setColorTheme(theme.id, undefined); - this.configurationService.updateValue(COLOR_THEME_SETTING, themeName); - } - }); - } - private installConfigurationListener() { this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(COLOR_THEME_SETTING)) { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.setTheme(colorThemeSetting); - this.deinstallColorThemeSwitch(); - } - } - if (e.affectsConfiguration(DETECT_AS_SETTING)) { - let autoSwitchColorTheme = this.configurationService.getValue(DETECT_AS_SETTING); - console.log(autoSwitchColorTheme); - if (this.autoSwitchColorTheme !== autoSwitchColorTheme) { - this.autoSwitchColorTheme = autoSwitchColorTheme; - console.log('HAS CHANGED'); - if (autoSwitchColorTheme) { - this.installColorThemeSwitch(); - let themeName = this.getPreferredTheme(); - this.setTheme(themeName); - } else { - this.deinstallColorThemeSwitch(); - } + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { + if (theme) { + this.setColorTheme(theme.id, undefined); + } + }); } } if (e.affectsConfiguration(ICON_THEME_SETTING)) { @@ -466,33 +338,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.colorThemeStore.getColorThemes(); } - public getColorThemeData(colorThemeSetting: string): Promise { - return new Promise(resolve => { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { - resolve(theme); - }); - }); - } - - public getPreferredColorScheme(): ColorScheme { - const noPreference = window.matchMedia(`(prefers-color-scheme: ${ColorScheme.NO_PREFERENCE})`).matches; - const prefersDark = window.matchMedia(`(prefers-color-scheme: ${ColorScheme.DARK})`).matches; - if (noPreference) { - return ColorScheme.NO_PREFERENCE; - } else if (prefersDark) { - return ColorScheme.DARK; - } - return ColorScheme.LIGHT; - } - public getTheme(): ITheme { return this.getColorTheme(); } - public setColorTheme( - themeId: string | undefined, - settingsTarget: ConfigurationTarget | undefined | 'auto' - ): Promise { + public setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!themeId) { return Promise.resolve(null); } @@ -507,40 +357,24 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return null; } const themeData = data; - return themeData.ensureLoaded(this.extensionResourceLoaderService).then( - _ => { - if ( - themeId === this.currentColorTheme.id && - !this.currentColorTheme.isLoaded && - this.currentColorTheme.hasEqualData(themeData) - ) { - this.currentColorTheme.clearCaches(); - // the loaded theme is identical to the perisisted theme. Don't need to send an event. - this.currentColorTheme = themeData; - themeData.setCustomColors(this.colorCustomizations); - themeData.setCustomTokenColors(this.tokenColorCustomizations); - themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); - return Promise.resolve(themeData); - } + return themeData.ensureLoaded(this.extensionResourceLoaderService).then(_ => { + if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { + this.currentColorTheme.clearCaches(); + // the loaded theme is identical to the perisisted theme. Don't need to send an event. + this.currentColorTheme = themeData; themeData.setCustomColors(this.colorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations); themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); - this.updateDynamicCSSRules(themeData); - return this.applyTheme(themeData, settingsTarget); - }, - error => { - return Promise.reject( - new Error( - nls.localize( - 'error.cannotloadtheme', - 'Unable to load {0}: {1}', - themeData.location!.toString(), - error.message - ) - ) - ); + return Promise.resolve(themeData); } - ); + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + themeData.setCustomTokenStyleRules(this.tokenStylesCustomizations); + this.updateDynamicCSSRules(themeData); + return this.applyTheme(themeData, settingsTarget); + }, error => { + return Promise.reject(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.location!.toString(), error.message))); + }); }); } @@ -556,7 +390,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { public restoreColorTheme() { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.setTheme(colorThemeSetting); + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { + if (theme) { + this.setColorTheme(theme.id, undefined); + } + }); } } @@ -573,11 +411,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { _applyRules([...cssRules].join('\n'), colorThemeRulesClassName); } - private applyTheme( - newTheme: ColorThemeData, - settingsTarget: ConfigurationTarget | undefined | 'auto', - silent = false - ): Promise { + private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget | undefined | 'auto', silent = false): Promise { if (this.currentColorTheme.id) { removeClasses(this.container, this.currentColorTheme.id); } else { @@ -588,9 +422,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.currentColorTheme.clearCaches(); this.currentColorTheme = newTheme; if (!this.themingParticipantChangeListener) { - this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => - this.updateDynamicCSSRules(this.currentColorTheme) - ); + this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme)); } if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) { @@ -621,9 +453,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { - return this.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then( - _ => this.currentColorTheme - ); + return this.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); } return Promise.resolve(this.currentColorTheme); } @@ -634,11 +464,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { let key = themeType + themeData.extensionId; if (!this.themeExtensionsActivated.get(key)) { type ActivatePluginClassification = { - id: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; - name: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; - isBuiltin: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; - publisherDisplayName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; - themeId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; + id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + themeId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; }; type ActivatePluginEvent = { id: string; @@ -671,10 +501,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.currentIconTheme; } - public setFileIconTheme( - iconTheme: string | undefined, - settingsTarget: ConfigurationTarget | undefined | 'auto' - ): Promise { + public setFileIconTheme(iconTheme: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { iconTheme = iconTheme || ''; if (iconTheme === this.currentIconTheme.id && this.currentIconTheme.isLoaded) { return this.writeFileIconConfiguration(settingsTarget); @@ -730,10 +557,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { dispose(this.watchedIconThemeDisposable); this.watchedIconThemeLocation = undefined; - if ( - iconThemeData.location && - (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI) - ) { + if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { this.watchedIconThemeLocation = iconThemeData.location; this.watchedIconThemeDisposable = this.fileService.watch(iconThemeData.location); } @@ -743,15 +567,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'fileIcon'); } this.onFileIconThemeChange.fire(this.currentIconTheme); + } - private writeFileIconConfiguration( - settingsTarget: ConfigurationTarget | undefined | 'auto' - ): Promise { + private writeFileIconConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { - return this.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then( - _ => this.currentIconTheme - ); + return this.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme); } return Promise.resolve(this.currentIconTheme); } @@ -777,10 +598,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } 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) { if (value === settings.value) { return Promise.resolve(undefined); // nothing to do } @@ -799,10 +617,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } -function _applyIconTheme( - data: FileIconThemeData, - onApply: (theme: FileIconThemeData) => Promise -): Promise { +function _applyIconTheme(data: FileIconThemeData, onApply: (theme: FileIconThemeData) => Promise): Promise { _applyRules(data.styleSheetContent!, iconThemeRulesClassName); return onApply(data); } @@ -828,49 +643,30 @@ const configurationRegistry = Registry.as(ConfigurationE const colorThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', - description: nls.localize('colorTheme', 'Specifies the color theme used in the workbench.'), + description: nls.localize('colorTheme', "Specifies the color theme used in the workbench."), default: DEFAULT_THEME_SETTING_VALUE, - errorMessage: nls.localize('colorThemeError', 'Theme is unknown or not installed.') -}; -const colorThemeDarkSettingSchema: IConfigurationPropertySchema = { - type: 'string', - description: nls.localize('colorThemeDark', 'Specifies the color theme used for a dark OS appearance.'), - default: DEFAULT_THEME_DARK_SETTING_VALUE, - errorMessage: nls.localize('colorThemeError', 'Theme is unknown or not installed.') -}; -const colorThemeLightSettingSchema: IConfigurationPropertySchema = { - type: 'string', - description: nls.localize('colorThemeLight', 'Specifies the color theme used for a light OS appearance.'), - default: DEFAULT_THEME_LIGHT_SETTING_VALUE, - errorMessage: nls.localize('colorThemeError', 'Theme is unknown or not installed.') -}; -const colorThemeAutoSwitchSettingSchema: IConfigurationPropertySchema = { - type: 'boolean', - description: nls.localize('colorThemeAutoSwitch', 'Changes the color theme based on the OS appearance.'), - default: DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE + enum: [], + enumDescriptions: [], + errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; const iconThemeSettingSchema: IConfigurationPropertySchema = { type: ['string', 'null'], default: DEFAULT_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 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.') + errorMessage: nls.localize('iconThemeError', "File icon theme is unknown or not installed.") }; const colorCustomizationsSchema: IConfigurationPropertySchema = { type: 'object', - description: nls.localize('workbenchColors', 'Overrides colors from the currently selected color theme.'), + description: nls.localize('workbenchColors', "Overrides colors from the currently selected color theme."), allOf: [{ $ref: workbenchColorsSchemaId }], default: {}, - defaultSnippets: [ - { - body: {} + defaultSnippets: [{ + body: { } - ] + }] }; const themeSettingsConfiguration: IConfigurationNode = { @@ -879,9 +675,6 @@ const themeSettingsConfiguration: IConfigurationNode = { type: 'object', properties: { [COLOR_THEME_SETTING]: colorThemeSettingSchema, - [COLOR_THEME_DARK_SETTING]: colorThemeDarkSettingSchema, - [COLOR_THEME_LIGHT_SETTING]: colorThemeLightSettingSchema, - [DETECT_AS_SETTING]: colorThemeAutoSwitchSettingSchema, [ICON_THEME_SETTING]: iconThemeSettingSchema, [CUSTOM_WORKBENCH_COLORS_SETTING]: colorCustomizationsSchema } @@ -906,45 +699,26 @@ function tokenGroupSettings(description: string): IJSONSchema { const tokenColorSchema: IJSONSchema = { properties: { - comments: tokenGroupSettings(nls.localize('editorColors.comments', 'Sets the colors and styles for comments')), - strings: tokenGroupSettings( - nls.localize('editorColors.strings', 'Sets the colors and styles for strings literals.') - ), - keywords: tokenGroupSettings(nls.localize('editorColors.keywords', 'Sets the colors and styles for keywords.')), - numbers: tokenGroupSettings( - nls.localize('editorColors.numbers', 'Sets the colors and styles for number literals.') - ), - types: tokenGroupSettings( - nls.localize('editorColors.types', 'Sets the colors and styles for type declarations and references.') - ), - functions: tokenGroupSettings( - nls.localize('editorColors.functions', 'Sets the colors and styles for functions declarations and references.') - ), - variables: tokenGroupSettings( - nls.localize('editorColors.variables', 'Sets the colors and styles for variables declarations and references.') - ), + comments: tokenGroupSettings(nls.localize('editorColors.comments', "Sets the colors and styles for comments")), + strings: tokenGroupSettings(nls.localize('editorColors.strings', "Sets the colors and styles for strings literals.")), + keywords: tokenGroupSettings(nls.localize('editorColors.keywords', "Sets the colors and styles for keywords.")), + numbers: tokenGroupSettings(nls.localize('editorColors.numbers', "Sets the colors and styles for number literals.")), + types: tokenGroupSettings(nls.localize('editorColors.types', "Sets the colors and styles for type declarations and references.")), + functions: tokenGroupSettings(nls.localize('editorColors.functions', "Sets the colors and styles for functions declarations and references.")), + variables: tokenGroupSettings(nls.localize('editorColors.variables', "Sets the colors and styles for variables declarations and references.")), textMateRules: { - description: nls.localize( - 'editorColors.textMateRules', - 'Sets colors and styles using textmate theming rules (advanced).' - ), + description: nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).'), $ref: textmateColorsSchemaId } } }; const tokenColorCustomizationSchema: IConfigurationPropertySchema = { - description: nls.localize( - 'editorColors', - 'Overrides editor colors and font style from the currently selected color theme.' - ), + description: nls.localize('editorColors', "Overrides editor colors and font style from the currently selected color theme."), default: {}, allOf: [tokenColorSchema] }; const experimentalTokenStylingCustomizationSchema: IConfigurationPropertySchema = { - description: nls.localize( - 'editorColorsTokenStyles', - 'Overrides token color and styles from the currently selected color theme.' - ), + description: nls.localize('editorColorsTokenStyles', "Overrides token color and styles from the currently selected color theme."), default: {}, allOf: [{ $ref: tokenStylingSchemaId }] }; @@ -959,4 +733,4 @@ const tokenColorCustomizationConfiguration: IConfigurationNode = { }; configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration); -registerSingleton(IWorkbenchThemeService, WorkbenchThemeService); +registerSingleton(IWorkbenchThemeService, WorkbenchThemeService); \ No newline at end of file diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 6577eb4a1d9b9..9fc33ebab8270 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -18,22 +18,12 @@ export const VS_HC_THEME = 'hc-black'; export const HC_THEME_ID = 'Default High Contrast'; export const COLOR_THEME_SETTING = 'workbench.colorTheme'; -export const COLOR_THEME_DARK_SETTING = 'workbench.colorThemeDark'; -export const COLOR_THEME_LIGHT_SETTING = 'workbench.colorThemeLight'; -export const DETECT_AS_SETTING = 'workbench.colorThemeAutoSwitch'; -export const WINDOW_MATCH_PREFERS_COLOR_SCHEME = '(prefers-color-scheme: dark)'; export const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; export const ICON_THEME_SETTING = 'workbench.iconTheme'; export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; export const CUSTOM_EDITOR_TOKENSTYLES_SETTING = 'editor.tokenColorCustomizationsExperimental'; -export enum ColorScheme { - LIGHT = 'light', - DARK = 'dark', - NO_PREFERENCE = 'no-preference' -} - export interface IColorTheme extends ITheme { readonly id: string; readonly label: string; @@ -63,20 +53,13 @@ export interface IFileIconTheme extends IIconTheme { export interface IWorkbenchThemeService extends IThemeService { _serviceBrand: undefined; - setColorTheme( - themeId: string | undefined, - settingsTarget: ConfigurationTarget | undefined - ): Promise; + setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; getColorTheme(): IColorTheme; getColorThemes(): Promise; - getColorThemeData(colorThemeSetting: string): Promise; onDidColorThemeChange: Event; restoreColorTheme(): void; - setFileIconTheme( - iconThemeId: string | undefined, - settingsTarget: ConfigurationTarget | undefined - ): Promise; + setFileIconTheme(iconThemeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; getFileIconTheme(): IFileIconTheme; getFileIconThemes(): Promise; onDidFileIconThemeChange: Event; @@ -87,12 +70,7 @@ export interface IColorCustomizations { } export interface ITokenColorCustomizations { - [groupIdOrThemeSettingsId: string]: - | string - | ITokenColorizationSetting - | ITokenColorCustomizations - | undefined - | ITextMateThemingRule[]; + [groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITextMateThemingRule[]; comments?: string | ITokenColorizationSetting; strings?: string | ITokenColorizationSetting; numbers?: string | ITokenColorizationSetting; @@ -104,11 +82,7 @@ export interface ITokenColorCustomizations { } export interface IExperimentalTokenStyleCustomizations { - [styleRuleOrThemeSettingsId: string]: - | string - | ITokenColorizationSetting - | IExperimentalTokenStyleCustomizations - | undefined; + [styleRuleOrThemeSettingsId: string]: string | ITokenColorizationSetting | IExperimentalTokenStyleCustomizations | undefined; } export interface ITextMateThemingRule { @@ -120,7 +94,7 @@ export interface ITextMateThemingRule { export interface ITokenColorizationSetting { foreground?: string; background?: string; - fontStyle?: string /* [italic|underline|bold] */; + fontStyle?: string; /* [italic|underline|bold] */ } export interface ExtensionData { @@ -137,4 +111,4 @@ export interface IThemeExtensionPoint { path: string; uiTheme?: typeof VS_LIGHT_THEME | typeof VS_DARK_THEME | typeof VS_HC_THEME; _watch: boolean; // unsupported options to watch location -} +} \ No newline at end of file From 6099ca39676d4ca225b035d62f3426568d4a70a1 Mon Sep 17 00:00:00 2001 From: Marvin Heilemann Date: Tue, 10 Dec 2019 09:02:16 +0100 Subject: [PATCH 3/8] Applied changes again --- .../themes/browser/workbenchThemeService.ts | 137 +++++++++++++++--- .../themes/common/workbenchThemeService.ts | 11 ++ 2 files changed, 130 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index c6c906e5cb027..59d2b9df7919e 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations, DETECT_AS_SETTING, COLOR_THEME_DARK_SETTING, WINDOW_MATCH_PREFERS_COLOR_SCHEME, COLOR_THEME_LIGHT_SETTING, ColorScheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -39,6 +39,9 @@ import { IExtensionResourceLoaderService } from 'vs/workbench/services/extension const DEFAULT_THEME_ID = 'vs-dark vscode-theme-defaults-themes-dark_plus-json'; const DEFAULT_THEME_SETTING_VALUE = 'Default Dark+'; +const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; +const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+'; +const DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE = true; const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData'; const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData'; @@ -72,6 +75,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private colorThemeStore: ColorThemeStore; private currentColorTheme: ColorThemeData; + private autoSwitchColorTheme = DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE; + private autoSwitchColorThemeListener: MediaQueryList | undefined = undefined; private container: HTMLElement; private readonly onColorThemeChange: Emitter; private watchedColorThemeLocation: URI | undefined; @@ -114,6 +119,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.iconThemeStore = new FileIconThemeStore(extensionService); this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); + this.onPreferColorSchemeChange = this.onPreferColorSchemeChange.bind(this); this.currentColorTheme = ColorThemeData.createUnloadedTheme(''); this.currentIconTheme = FileIconThemeData.createUnloadedTheme(''); @@ -148,6 +154,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.initialize().then(undefined, errors.onUnexpectedError).then(_ => { this.installConfigurationListener(); + this.installColorThemeSwitch(); }); let prevColorId: string | undefined = undefined; @@ -248,17 +255,20 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private initialize(): Promise<[IColorTheme | null, IFileIconTheme | null]> { - let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); + let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); - let colorThemeSetting: string; + let detectThemeAutoSwitch = this.configurationService.getValue(DETECT_AS_SETTING); + this.autoSwitchColorTheme = detectThemeAutoSwitch; + if (detectThemeAutoSwitch) { + colorThemeSetting = this.getPreferredTheme(); + } + + let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); if (this.environmentService.configuration.highContrast && detectHCThemeSetting) { colorThemeSetting = HC_THEME_ID; - } else { - colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); } - let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); - const extDevLocs = this.environmentService.extensionDevelopmentLocationURI; let uri: URI | undefined; if (extDevLocs && extDevLocs.length > 0) { @@ -267,7 +277,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } return Promise.all([ - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + this.getColorThemeData(colorThemeSetting).then(theme => { return this.colorThemeStore.findThemeDataByParentLocation(uri).then(devThemes => { if (devThemes.length) { return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY); @@ -288,16 +298,54 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { ]); } + private installColorThemeSwitch() { + console.log('INSTALL'); + this.autoSwitchColorThemeListener = window.matchMedia(WINDOW_MATCH_PREFERS_COLOR_SCHEME); + this.autoSwitchColorThemeListener.addListener(this.onPreferColorSchemeChange); + console.log('INSTALLED'); + } + + private deinstallColorThemeSwitch() { + console.log('DEINSTALL'); + if (this.autoSwitchColorThemeListener) { + this.autoSwitchColorThemeListener.removeListener(this.onPreferColorSchemeChange); + this.configurationService.updateValue(DETECT_AS_SETTING, false); + console.log('DEINSTALLED'); + } + } + + private onPreferColorSchemeChange({ matches }: MediaQueryListEvent) { + console.log('onPreferColorSchemeChange', matches); + let themeName = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); + if (matches) { + // prefers dark mode + themeName = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); + } + this.setTheme(themeName); + } + private installConfigurationListener() { this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(COLOR_THEME_SETTING)) { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { - if (theme) { - this.setColorTheme(theme.id, undefined); - } - }); + this.setTheme(colorThemeSetting); + this.deinstallColorThemeSwitch(); + } + } + if (e.affectsConfiguration(DETECT_AS_SETTING)) { + let autoSwitchColorTheme = this.configurationService.getValue(DETECT_AS_SETTING); + console.log(autoSwitchColorTheme); + if (this.autoSwitchColorTheme !== autoSwitchColorTheme) { + this.autoSwitchColorTheme = autoSwitchColorTheme; + console.log('HAS CHANGED'); + if (autoSwitchColorTheme) { + this.installColorThemeSwitch(); + let themeName = this.getPreferredTheme(); + this.setTheme(themeName); + } else { + this.deinstallColorThemeSwitch(); + } } } if (e.affectsConfiguration(ICON_THEME_SETTING)) { @@ -330,6 +378,34 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } + private getPreferredTheme(): string { + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); + const darkMode = this.getPreferredColorScheme() === ColorScheme.DARK; + if (darkMode) { + colorThemeSetting = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); + } + return colorThemeSetting; + } + + public getPreferredColorScheme(): ColorScheme { + const noPreference = window.matchMedia(`(prefers-color-scheme: ${ColorScheme.NO_PREFERENCE})`).matches; + const prefersDark = window.matchMedia(`(prefers-color-scheme: ${ColorScheme.DARK})`).matches; + if (noPreference) { + return ColorScheme.NO_PREFERENCE; + } else if (prefersDark) { + return ColorScheme.DARK; + } + return ColorScheme.LIGHT; + } + + public getColorThemeData(colorThemeSetting: string): Promise { + return new Promise(resolve => { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + resolve(theme); + }); + }); + } + public getColorTheme(): IColorTheme { return this.currentColorTheme; } @@ -342,6 +418,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.getColorTheme(); } + public setTheme(themeName: string) { + this.getColorThemeData(themeName).then(theme => { + if (theme) { + this.setColorTheme(theme.id, undefined); + this.configurationService.updateValue(COLOR_THEME_SETTING, themeName); + } + }); + } + public setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!themeId) { return Promise.resolve(null); @@ -390,11 +475,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { public restoreColorTheme() { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { - if (theme) { - this.setColorTheme(theme.id, undefined); - } - }); + this.setTheme(colorThemeSetting); } } @@ -649,6 +730,23 @@ const colorThemeSettingSchema: IConfigurationPropertySchema = { enumDescriptions: [], errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; +const colorThemeDarkSettingSchema: IConfigurationPropertySchema = { + type: 'string', + description: nls.localize('colorThemeDark', 'Specifies the color theme used for a dark OS appearance.'), + default: DEFAULT_THEME_DARK_SETTING_VALUE, + errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), +}; +const colorThemeLightSettingSchema: IConfigurationPropertySchema = { + type: 'string', + description: nls.localize('colorThemeLight', 'Specifies the color theme used for a light OS appearance.'), + default: DEFAULT_THEME_LIGHT_SETTING_VALUE, + errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), +}; +const colorThemeAutoSwitchSettingSchema: IConfigurationPropertySchema = { + type: 'boolean', + description: nls.localize('colorThemeAutoSwitch', 'Changes the color theme based on the OS appearance.'), + default: DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE +}; const iconThemeSettingSchema: IConfigurationPropertySchema = { type: ['string', 'null'], @@ -675,6 +773,9 @@ const themeSettingsConfiguration: IConfigurationNode = { type: 'object', properties: { [COLOR_THEME_SETTING]: colorThemeSettingSchema, + [COLOR_THEME_DARK_SETTING]: colorThemeDarkSettingSchema, + [COLOR_THEME_LIGHT_SETTING]: colorThemeLightSettingSchema, + [DETECT_AS_SETTING]: colorThemeAutoSwitchSettingSchema, [ICON_THEME_SETTING]: iconThemeSettingSchema, [CUSTOM_WORKBENCH_COLORS_SETTING]: colorCustomizationsSchema } diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 9fc33ebab8270..faa27f7966d8a 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -18,12 +18,22 @@ export const VS_HC_THEME = 'hc-black'; export const HC_THEME_ID = 'Default High Contrast'; export const COLOR_THEME_SETTING = 'workbench.colorTheme'; +export const COLOR_THEME_DARK_SETTING = 'workbench.colorThemeDark'; +export const COLOR_THEME_LIGHT_SETTING = 'workbench.colorThemeLight'; +export const DETECT_AS_SETTING = 'workbench.colorThemeAutoSwitch'; +export const WINDOW_MATCH_PREFERS_COLOR_SCHEME = '(prefers-color-scheme: dark)'; export const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; export const ICON_THEME_SETTING = 'workbench.iconTheme'; export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; export const CUSTOM_EDITOR_TOKENSTYLES_SETTING = 'editor.tokenColorCustomizationsExperimental'; +export enum ColorScheme { + LIGHT = 'light', + DARK = 'dark', + NO_PREFERENCE = 'no-preference' +} + export interface IColorTheme extends ITheme { readonly id: string; readonly label: string; @@ -56,6 +66,7 @@ export interface IWorkbenchThemeService extends IThemeService { setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; getColorTheme(): IColorTheme; getColorThemes(): Promise; + getColorThemeData(colorThemeSetting: string): Promise; onDidColorThemeChange: Event; restoreColorTheme(): void; From 9146c53f3fb784bb67a32e36fa9369c3f392f7a5 Mon Sep 17 00:00:00 2001 From: Marvin Heilemann Date: Tue, 17 Dec 2019 15:00:27 +0100 Subject: [PATCH 4/8] Resolved some requested changes --- .../themes/browser/workbenchThemeService.ts | 99 ++++++++++--------- .../themes/common/workbenchThemeService.ts | 8 -- 2 files changed, 52 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 59d2b9df7919e..40adde676cf5f 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations, DETECT_AS_SETTING, COLOR_THEME_DARK_SETTING, WINDOW_MATCH_PREFERS_COLOR_SCHEME, COLOR_THEME_LIGHT_SETTING, ColorScheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations, DETECT_AS_SETTING, COLOR_THEME_DARK_SETTING, COLOR_THEME_LIGHT_SETTING } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -14,7 +14,7 @@ import * as errors from 'vs/base/common/errors'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData'; -import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; +import { ITheme, Extensions as ThemingExtensions, IThemingRegistry, DARK, LIGHT } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -43,6 +43,12 @@ const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+'; const DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE = true; +enum ColorScheme { + LIGHT = 'light', + DARK = 'dark', + NO_PREFERENCE = 'no-preference' +} + const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData'; const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData'; @@ -75,8 +81,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private colorThemeStore: ColorThemeStore; private currentColorTheme: ColorThemeData; - private autoSwitchColorTheme = DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE; - private autoSwitchColorThemeListener: MediaQueryList | undefined = undefined; + private autoSwitchColorTheme: MediaQueryList | undefined; private container: HTMLElement; private readonly onColorThemeChange: Emitter; private watchedColorThemeLocation: URI | undefined; @@ -259,7 +264,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); let detectThemeAutoSwitch = this.configurationService.getValue(DETECT_AS_SETTING); - this.autoSwitchColorTheme = detectThemeAutoSwitch; if (detectThemeAutoSwitch) { colorThemeSetting = this.getPreferredTheme(); } @@ -277,7 +281,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } return Promise.all([ - this.getColorThemeData(colorThemeSetting).then(theme => { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { return this.colorThemeStore.findThemeDataByParentLocation(uri).then(devThemes => { if (devThemes.length) { return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY); @@ -300,28 +304,32 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private installColorThemeSwitch() { console.log('INSTALL'); - this.autoSwitchColorThemeListener = window.matchMedia(WINDOW_MATCH_PREFERS_COLOR_SCHEME); - this.autoSwitchColorThemeListener.addListener(this.onPreferColorSchemeChange); + this.autoSwitchColorTheme = window.matchMedia('(prefers-color-scheme: dark)'); + this.autoSwitchColorTheme.addListener(this.onPreferColorSchemeChange); console.log('INSTALLED'); } private deinstallColorThemeSwitch() { console.log('DEINSTALL'); - if (this.autoSwitchColorThemeListener) { - this.autoSwitchColorThemeListener.removeListener(this.onPreferColorSchemeChange); + if (this.autoSwitchColorTheme) { + this.autoSwitchColorTheme.removeListener(this.onPreferColorSchemeChange); this.configurationService.updateValue(DETECT_AS_SETTING, false); console.log('DEINSTALLED'); } } - private onPreferColorSchemeChange({ matches }: MediaQueryListEvent) { + private onPreferColorSchemeChange({ matches }: MediaQueryListEvent) { console.log('onPreferColorSchemeChange', matches); - let themeName = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); if (matches) { // prefers dark mode - themeName = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); + colorThemeSetting = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); } - this.setTheme(themeName); + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + if (theme) { + this.setColorTheme(theme.id, undefined); + } + }); } private installConfigurationListener() { @@ -329,23 +337,33 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (e.affectsConfiguration(COLOR_THEME_SETTING)) { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.setTheme(colorThemeSetting); - this.deinstallColorThemeSwitch(); + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + if (theme) { + if (this.autoSwitchColorTheme && theme.type === DARK) { + this.configurationService.updateValue(COLOR_THEME_DARK_SETTING, theme.settingsId); + } + if (this.autoSwitchColorTheme && theme.type === LIGHT) { + this.configurationService.updateValue(COLOR_THEME_LIGHT_SETTING, theme.settingsId); + } + this.setColorTheme(theme.id, undefined); + } + }); } } if (e.affectsConfiguration(DETECT_AS_SETTING)) { let autoSwitchColorTheme = this.configurationService.getValue(DETECT_AS_SETTING); - console.log(autoSwitchColorTheme); - if (this.autoSwitchColorTheme !== autoSwitchColorTheme) { - this.autoSwitchColorTheme = autoSwitchColorTheme; - console.log('HAS CHANGED'); - if (autoSwitchColorTheme) { - this.installColorThemeSwitch(); - let themeName = this.getPreferredTheme(); - this.setTheme(themeName); - } else { - this.deinstallColorThemeSwitch(); + if (autoSwitchColorTheme) { + this.installColorThemeSwitch(); + let colorThemeSetting = this.getPreferredTheme(); + if (colorThemeSetting !== this.currentColorTheme.settingsId) { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + if (theme) { + this.setColorTheme(theme.id, undefined); + } + }); } + } else { + this.deinstallColorThemeSwitch(); } } if (e.affectsConfiguration(ICON_THEME_SETTING)) { @@ -398,14 +416,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return ColorScheme.LIGHT; } - public getColorThemeData(colorThemeSetting: string): Promise { - return new Promise(resolve => { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { - resolve(theme); - }); - }); - } - public getColorTheme(): IColorTheme { return this.currentColorTheme; } @@ -418,15 +428,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.getColorTheme(); } - public setTheme(themeName: string) { - this.getColorThemeData(themeName).then(theme => { - if (theme) { - this.setColorTheme(theme.id, undefined); - this.configurationService.updateValue(COLOR_THEME_SETTING, themeName); - } - }); - } - public setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!themeId) { return Promise.resolve(null); @@ -437,11 +438,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { themeId = validateThemeId(themeId); // migrate theme ids - return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(data => { - if (!data) { + return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => { + if (!themeData) { return null; } - const themeData = data; + this.configurationService.updateValue(COLOR_THEME_SETTING, themeData.settingsId); return themeData.ensureLoaded(this.extensionResourceLoaderService).then(_ => { if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { this.currentColorTheme.clearCaches(); @@ -475,7 +476,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { public restoreColorTheme() { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.setTheme(colorThemeSetting); + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + if (theme) { + this.setColorTheme(theme.id, undefined); + } + }); } } diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index faa27f7966d8a..c498b7d1ac214 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -21,19 +21,12 @@ export const COLOR_THEME_SETTING = 'workbench.colorTheme'; export const COLOR_THEME_DARK_SETTING = 'workbench.colorThemeDark'; export const COLOR_THEME_LIGHT_SETTING = 'workbench.colorThemeLight'; export const DETECT_AS_SETTING = 'workbench.colorThemeAutoSwitch'; -export const WINDOW_MATCH_PREFERS_COLOR_SCHEME = '(prefers-color-scheme: dark)'; export const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; export const ICON_THEME_SETTING = 'workbench.iconTheme'; export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; export const CUSTOM_EDITOR_TOKENSTYLES_SETTING = 'editor.tokenColorCustomizationsExperimental'; -export enum ColorScheme { - LIGHT = 'light', - DARK = 'dark', - NO_PREFERENCE = 'no-preference' -} - export interface IColorTheme extends ITheme { readonly id: string; readonly label: string; @@ -66,7 +59,6 @@ export interface IWorkbenchThemeService extends IThemeService { setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; getColorTheme(): IColorTheme; getColorThemes(): Promise; - getColorThemeData(colorThemeSetting: string): Promise; onDidColorThemeChange: Event; restoreColorTheme(): void; From 494a6bcfb91e8f118813b84daa06c30bb9023e0c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 Dec 2019 11:50:44 +0100 Subject: [PATCH 5/8] polish --- .../themes/browser/workbenchThemeService.ts | 222 ++++++++---------- .../themes/common/workbenchThemeService.ts | 6 +- 2 files changed, 100 insertions(+), 128 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 40adde676cf5f..a40550eec3b24 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations, DETECT_AS_SETTING, COLOR_THEME_DARK_SETTING, COLOR_THEME_LIGHT_SETTING } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, IColorCustomizations, CUSTOM_EDITOR_TOKENSTYLES_SETTING, IExperimentalTokenStyleCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -14,7 +14,7 @@ import * as errors from 'vs/base/common/errors'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData'; -import { ITheme, Extensions as ThemingExtensions, IThemingRegistry, DARK, LIGHT } from 'vs/platform/theme/common/themeService'; +import { ITheme, Extensions as ThemingExtensions, IThemingRegistry, ThemeType, LIGHT, DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -35,19 +35,21 @@ import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; +// settings + +const PREFERRED_DARK_THEME_SETTING = 'workbench.preferredDarkColorTheme'; +const PREFERRED_LIGHT_THEME_SETTING = 'workbench.preferredLightColorTheme'; +const PREFERRED_HC_THEME_SETTING = 'workbench.preferredHighContrastColorTheme'; +const DETECT_COLOR_SCHEME_SETTING = 'workbench.autoDetectColorScheme'; +const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; + // implementation const DEFAULT_THEME_ID = 'vs-dark vscode-theme-defaults-themes-dark_plus-json'; const DEFAULT_THEME_SETTING_VALUE = 'Default Dark+'; const DEFAULT_THEME_DARK_SETTING_VALUE = 'Default Dark+'; const DEFAULT_THEME_LIGHT_SETTING_VALUE = 'Default Light+'; -const DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE = true; - -enum ColorScheme { - LIGHT = 'light', - DARK = 'dark', - NO_PREFERENCE = 'no-preference' -} +const DEFAULT_THEME_HC_SETTING_VALUE = 'Default High Contrast'; const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData'; const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData'; @@ -81,7 +83,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private colorThemeStore: ColorThemeStore; private currentColorTheme: ColorThemeData; - private autoSwitchColorTheme: MediaQueryList | undefined; private container: HTMLElement; private readonly onColorThemeChange: Emitter; private watchedColorThemeLocation: URI | undefined; @@ -124,7 +125,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.iconThemeStore = new FileIconThemeStore(extensionService); this.onColorThemeChange = new Emitter({ leakWarningThreshold: 400 }); - this.onPreferColorSchemeChange = this.onPreferColorSchemeChange.bind(this); this.currentColorTheme = ColorThemeData.createUnloadedTheme(''); this.currentIconTheme = FileIconThemeData.createUnloadedTheme(''); @@ -159,7 +159,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.initialize().then(undefined, errors.onUnexpectedError).then(_ => { this.installConfigurationListener(); - this.installColorThemeSwitch(); + this.installPreferredSchemeListener(); }); let prevColorId: string | undefined = undefined; @@ -260,76 +260,43 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private initialize(): Promise<[IColorTheme | null, IFileIconTheme | null]> { - let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); - let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); - - let detectThemeAutoSwitch = this.configurationService.getValue(DETECT_AS_SETTING); - if (detectThemeAutoSwitch) { - colorThemeSetting = this.getPreferredTheme(); - } - - let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); - if (this.environmentService.configuration.highContrast && detectHCThemeSetting) { - colorThemeSetting = HC_THEME_ID; - } + const colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); + const iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); const extDevLocs = this.environmentService.extensionDevelopmentLocationURI; - let uri: URI | undefined; - if (extDevLocs && extDevLocs.length > 0) { - // if there are more than one ext dev paths, use first - uri = extDevLocs[0]; - } - - return Promise.all([ - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { - return this.colorThemeStore.findThemeDataByParentLocation(uri).then(devThemes => { - if (devThemes.length) { - return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY); - } else { - return this.setColorTheme(theme && theme.id, undefined); - } - }); - }), - this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => { - return this.iconThemeStore.findThemeDataByParentLocation(uri).then(devThemes => { - if (devThemes.length) { - return this.setFileIconTheme(devThemes[0].id, ConfigurationTarget.MEMORY); - } else { - return this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); - } - }); - }), - ]); - } - - private installColorThemeSwitch() { - console.log('INSTALL'); - this.autoSwitchColorTheme = window.matchMedia('(prefers-color-scheme: dark)'); - this.autoSwitchColorTheme.addListener(this.onPreferColorSchemeChange); - console.log('INSTALLED'); - } - private deinstallColorThemeSwitch() { - console.log('DEINSTALL'); - if (this.autoSwitchColorTheme) { - this.autoSwitchColorTheme.removeListener(this.onPreferColorSchemeChange); - this.configurationService.updateValue(DETECT_AS_SETTING, false); - console.log('DEINSTALLED'); - } - } + const initializeColorTheme = async () => { + if (extDevLocs && extDevLocs.length > 0) { // in dev mode, switch to a theme provided by the extension under dev. + const devThemes = await this.colorThemeStore.findThemeDataByParentLocation(extDevLocs[0]); + if (devThemes.length) { + return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY); + } + } + let theme = await this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID); + const preferredType = this.getPreferredColorScheme(); + let settingsTarget: undefined | 'auto' = undefined; + if (preferredType && theme && theme.type !== preferredType) { + const preferedTheme = await this.getPreferredColorTheme(preferredType); + if (preferedTheme) { + theme = preferedTheme; + settingsTarget = 'auto'; + } + } + return this.setColorTheme(theme && theme.id, settingsTarget); + }; - private onPreferColorSchemeChange({ matches }: MediaQueryListEvent) { - console.log('onPreferColorSchemeChange', matches); - let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); - if (matches) { - // prefers dark mode - colorThemeSetting = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); - } - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { - if (theme) { - this.setColorTheme(theme.id, undefined); + const initializeIconTheme = async () => { + if (extDevLocs && extDevLocs.length > 0) { // in dev mode, switch to a theme provided by the extension under dev. + const devThemes = await this.iconThemeStore.findThemeDataByParentLocation(extDevLocs[0]); + if (devThemes.length) { + return this.setFileIconTheme(devThemes[0].id, ConfigurationTarget.MEMORY); + } } - }); + const theme = await this.iconThemeStore.findThemeBySettingsId(iconThemeSetting); + return this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); + }; + + return Promise.all([initializeColorTheme(), initializeIconTheme()]); } private installConfigurationListener() { @@ -339,31 +306,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (colorThemeSetting !== this.currentColorTheme.settingsId) { this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { if (theme) { - if (this.autoSwitchColorTheme && theme.type === DARK) { - this.configurationService.updateValue(COLOR_THEME_DARK_SETTING, theme.settingsId); - } - if (this.autoSwitchColorTheme && theme.type === LIGHT) { - this.configurationService.updateValue(COLOR_THEME_LIGHT_SETTING, theme.settingsId); - } this.setColorTheme(theme.id, undefined); } }); } } - if (e.affectsConfiguration(DETECT_AS_SETTING)) { - let autoSwitchColorTheme = this.configurationService.getValue(DETECT_AS_SETTING); + if (e.affectsConfiguration(DETECT_COLOR_SCHEME_SETTING)) { + let autoSwitchColorTheme = this.configurationService.getValue(DETECT_COLOR_SCHEME_SETTING); if (autoSwitchColorTheme) { - this.installColorThemeSwitch(); - let colorThemeSetting = this.getPreferredTheme(); - if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { - if (theme) { - this.setColorTheme(theme.id, undefined); - } - }); - } - } else { - this.deinstallColorThemeSwitch(); + this.preferredSchemeUpdated(); } } if (e.affectsConfiguration(ICON_THEME_SETTING)) { @@ -396,24 +347,45 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } - private getPreferredTheme(): string { - let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_LIGHT_SETTING); - const darkMode = this.getPreferredColorScheme() === ColorScheme.DARK; - if (darkMode) { - colorThemeSetting = this.configurationService.getValue(COLOR_THEME_DARK_SETTING); + // preferred scheme handling + + private installPreferredSchemeListener() { + window.matchMedia('(prefers-color-scheme: dark)').addListener(async () => this.preferredSchemeUpdated()); + } + + private async preferredSchemeUpdated() { + const scheme = this.getPreferredColorScheme(); + if (scheme && this.currentColorTheme.type !== scheme) { + const preferedTheme = await this.getPreferredColorTheme(scheme); + if (preferedTheme) { + return this.setColorTheme(preferedTheme.id, 'auto'); + } } - return colorThemeSetting; + return undefined; } - public getPreferredColorScheme(): ColorScheme { - const noPreference = window.matchMedia(`(prefers-color-scheme: ${ColorScheme.NO_PREFERENCE})`).matches; - const prefersDark = window.matchMedia(`(prefers-color-scheme: ${ColorScheme.DARK})`).matches; - if (noPreference) { - return ColorScheme.NO_PREFERENCE; - } else if (prefersDark) { - return ColorScheme.DARK; + private getPreferredColorScheme(): ThemeType | undefined { + let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); + if (this.environmentService.configuration.highContrast && detectHCThemeSetting) { + return HIGH_CONTRAST; } - return ColorScheme.LIGHT; + if (this.configurationService.getValue(DETECT_COLOR_SCHEME_SETTING)) { + if (window.matchMedia(`(prefers-color-scheme: light)`).matches) { + return LIGHT; + } else if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) { + return DARK; + } + } + return undefined; + } + + private getPreferredColorTheme(type: ThemeType): Promise { + const settingId = type === DARK ? PREFERRED_DARK_THEME_SETTING : type === LIGHT ? PREFERRED_LIGHT_THEME_SETTING : PREFERRED_HC_THEME_SETTING; + const themeSettingId = this.configurationService.getValue(settingId); + if (themeSettingId) { + return this.colorThemeStore.findThemeDataBySettingsId(themeSettingId, undefined); + } + return Promise.resolve(undefined); } public getColorTheme(): IColorTheme { @@ -735,22 +707,25 @@ const colorThemeSettingSchema: IConfigurationPropertySchema = { enumDescriptions: [], errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; -const colorThemeDarkSettingSchema: IConfigurationPropertySchema = { +const preferredDarkThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', - description: nls.localize('colorThemeDark', 'Specifies the color theme used for a dark OS appearance.'), + description: nls.localize('preferredDarkColorTheme', 'Specifies the preferred color theme for dark OS appearance when \'{0}\' is enabled.', DETECT_COLOR_SCHEME_SETTING), default: DEFAULT_THEME_DARK_SETTING_VALUE, - errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; -const colorThemeLightSettingSchema: IConfigurationPropertySchema = { +const preferredLightThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', - description: nls.localize('colorThemeLight', 'Specifies the color theme used for a light OS appearance.'), + description: nls.localize('preferredLightColorTheme', 'Specifies the preferred color theme for light OS appearance when \'{0}\' is enabled.', DETECT_COLOR_SCHEME_SETTING), default: DEFAULT_THEME_LIGHT_SETTING_VALUE, - errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; -const colorThemeAutoSwitchSettingSchema: IConfigurationPropertySchema = { +const preferredHCThemeSettingSchema: IConfigurationPropertySchema = { + type: 'string', + description: nls.localize('preferredHCColorTheme', 'Specifies the preferred color theme used in high contrast mode when \'{0}\' is enabled.', DETECT_HC_SETTING), + default: DEFAULT_THEME_HC_SETTING_VALUE, +}; +const detectColorSchemeSettingSchema: IConfigurationPropertySchema = { type: 'boolean', - description: nls.localize('colorThemeAutoSwitch', 'Changes the color theme based on the OS appearance.'), - default: DEFAULT_THEME_AUTO_SWITCH_SETTING_VALUE + description: nls.localize('detectColorScheme', 'If set, use the prefered color theme based on the OS appearance.'), + default: true }; const iconThemeSettingSchema: IConfigurationPropertySchema = { @@ -778,9 +753,10 @@ const themeSettingsConfiguration: IConfigurationNode = { type: 'object', properties: { [COLOR_THEME_SETTING]: colorThemeSettingSchema, - [COLOR_THEME_DARK_SETTING]: colorThemeDarkSettingSchema, - [COLOR_THEME_LIGHT_SETTING]: colorThemeLightSettingSchema, - [DETECT_AS_SETTING]: colorThemeAutoSwitchSettingSchema, + [PREFERRED_DARK_THEME_SETTING]: preferredDarkThemeSettingSchema, + [PREFERRED_LIGHT_THEME_SETTING]: preferredLightThemeSettingSchema, + [PREFERRED_HC_THEME_SETTING]: preferredHCThemeSettingSchema, + [DETECT_COLOR_SCHEME_SETTING]: detectColorSchemeSettingSchema, [ICON_THEME_SETTING]: iconThemeSettingSchema, [CUSTOM_WORKBENCH_COLORS_SETTING]: colorCustomizationsSchema } @@ -839,4 +815,4 @@ const tokenColorCustomizationConfiguration: IConfigurationNode = { }; configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration); -registerSingleton(IWorkbenchThemeService, WorkbenchThemeService); \ No newline at end of file +registerSingleton(IWorkbenchThemeService, WorkbenchThemeService); diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index c498b7d1ac214..4756204b5954f 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -18,10 +18,6 @@ export const VS_HC_THEME = 'hc-black'; export const HC_THEME_ID = 'Default High Contrast'; export const COLOR_THEME_SETTING = 'workbench.colorTheme'; -export const COLOR_THEME_DARK_SETTING = 'workbench.colorThemeDark'; -export const COLOR_THEME_LIGHT_SETTING = 'workbench.colorThemeLight'; -export const DETECT_AS_SETTING = 'workbench.colorThemeAutoSwitch'; -export const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; export const ICON_THEME_SETTING = 'workbench.iconTheme'; export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; @@ -114,4 +110,4 @@ export interface IThemeExtensionPoint { path: string; uiTheme?: typeof VS_LIGHT_THEME | typeof VS_DARK_THEME | typeof VS_HC_THEME; _watch: boolean; // unsupported options to watch location -} \ No newline at end of file +} From 5a94ca0bde2bf7ad7bee9310f81edbea90ace547 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 Dec 2019 12:31:44 +0100 Subject: [PATCH 6/8] enums for preferred theme settings --- .../themes/browser/workbenchThemeService.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 840c370623692..70bda875facec 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -167,8 +167,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { // update settings schema setting for theme specific settings this.colorThemeStore.onDidChange(async event => { // updates enum for the 'workbench.colorTheme` setting - colorThemeSettingSchema.enum = event.themes.map(t => t.settingsId); - colorThemeSettingSchema.enumDescriptions = event.themes.map(t => t.description || ''); + colorThemeSettingEnum.splice(0, colorThemeSettingEnum.length, ...event.themes.map(t => t.settingsId)); + colorThemeSettingEnumDescriptions.splice(0, colorThemeSettingEnumDescriptions.length, ...event.themes.map(t => t.description || '')); const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} }; const themeSpecificTokenColors: IJSONSchema = { properties: {} }; @@ -699,28 +699,40 @@ registerFileIconThemeSchemas(); // Configuration: Themes const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); +const colorThemeSettingEnum: string[] = []; +const colorThemeSettingEnumDescriptions: string[] = []; + const colorThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', description: nls.localize('colorTheme', "Specifies the color theme used in the workbench."), default: DEFAULT_THEME_SETTING_VALUE, - enum: [], - enumDescriptions: [], + enum: colorThemeSettingEnum, + enumDescriptions: colorThemeSettingEnumDescriptions, errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; const preferredDarkThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', description: nls.localize('preferredDarkColorTheme', 'Specifies the preferred color theme for dark OS appearance when \'{0}\' is enabled.', DETECT_COLOR_SCHEME_SETTING), default: DEFAULT_THEME_DARK_SETTING_VALUE, + enum: colorThemeSettingEnum, + enumDescriptions: colorThemeSettingEnumDescriptions, + errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; const preferredLightThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', description: nls.localize('preferredLightColorTheme', 'Specifies the preferred color theme for light OS appearance when \'{0}\' is enabled.', DETECT_COLOR_SCHEME_SETTING), default: DEFAULT_THEME_LIGHT_SETTING_VALUE, + enum: colorThemeSettingEnum, + enumDescriptions: colorThemeSettingEnumDescriptions, + errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; const preferredHCThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', description: nls.localize('preferredHCColorTheme', 'Specifies the preferred color theme used in high contrast mode when \'{0}\' is enabled.', DETECT_HC_SETTING), default: DEFAULT_THEME_HC_SETTING_VALUE, + enum: colorThemeSettingEnum, + enumDescriptions: colorThemeSettingEnumDescriptions, + errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; const detectColorSchemeSettingSchema: IConfigurationPropertySchema = { type: 'boolean', From a4f610ec282c421eb43688857af53bd2775acc1b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 Dec 2019 15:47:01 +0100 Subject: [PATCH 7/8] update --- .../themes/browser/workbenchThemeService.ts | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 70bda875facec..b9af024b8abfa 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -53,6 +53,7 @@ const DEFAULT_THEME_HC_SETTING_VALUE = 'Default High Contrast'; const PERSISTED_THEME_STORAGE_KEY = 'colorThemeData'; const PERSISTED_ICON_THEME_STORAGE_KEY = 'iconThemeData'; +const PERSISTED_OS_COLOR_SCHEME = 'osColorScheme'; const defaultThemeExtensionId = 'vscode-theme-defaults'; const oldDefaultThemeExtensionId = 'vscode-theme-colorful-defaults'; @@ -273,16 +274,13 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } let theme = await this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID); - const preferredType = this.getPreferredColorScheme(); - let settingsTarget: undefined | 'auto' = undefined; - if (preferredType && theme && theme.type !== preferredType) { - const preferedTheme = await this.getPreferredColorTheme(preferredType); - if (preferedTheme) { - theme = preferedTheme; - settingsTarget = 'auto'; - } + + const persistedColorScheme = this.storageService.get(PERSISTED_OS_COLOR_SCHEME, StorageScope.GLOBAL); + const preferredColorScheme = this.getPreferredColorScheme(); + if (persistedColorScheme && preferredColorScheme && persistedColorScheme !== preferredColorScheme) { + return this.applyPreferredColorTheme(preferredColorScheme); } - return this.setColorTheme(theme && theme.id, settingsTarget); + return this.setColorTheme(theme && theme.id, undefined); }; const initializeIconTheme = async () => { @@ -312,10 +310,16 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } if (e.affectsConfiguration(DETECT_COLOR_SCHEME_SETTING)) { - let autoSwitchColorTheme = this.configurationService.getValue(DETECT_COLOR_SCHEME_SETTING); - if (autoSwitchColorTheme) { - this.preferredSchemeUpdated(); - } + this.preferredSchemeUpdated(); + } + if (e.affectsConfiguration(PREFERRED_DARK_THEME_SETTING) && this.getPreferredColorScheme() === DARK) { + this.applyPreferredColorTheme(DARK); + } + if (e.affectsConfiguration(PREFERRED_LIGHT_THEME_SETTING) && this.getPreferredColorScheme() === LIGHT) { + this.applyPreferredColorTheme(LIGHT); + } + if (e.affectsConfiguration(PREFERRED_HC_THEME_SETTING) && this.getPreferredColorScheme() === HIGH_CONTRAST) { + this.applyPreferredColorTheme(HIGH_CONTRAST); } if (e.affectsConfiguration(ICON_THEME_SETTING)) { let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); @@ -355,11 +359,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private async preferredSchemeUpdated() { const scheme = this.getPreferredColorScheme(); - if (scheme && this.currentColorTheme.type !== scheme) { - const preferedTheme = await this.getPreferredColorTheme(scheme); - if (preferedTheme) { - return this.setColorTheme(preferedTheme.id, 'auto'); - } + this.storageService.store(PERSISTED_OS_COLOR_SCHEME, scheme, StorageScope.GLOBAL); + if (scheme) { + return this.applyPreferredColorTheme(scheme); } return undefined; } @@ -379,13 +381,16 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return undefined; } - private getPreferredColorTheme(type: ThemeType): Promise { + private async applyPreferredColorTheme(type: ThemeType): Promise { const settingId = type === DARK ? PREFERRED_DARK_THEME_SETTING : type === LIGHT ? PREFERRED_LIGHT_THEME_SETTING : PREFERRED_HC_THEME_SETTING; const themeSettingId = this.configurationService.getValue(settingId); if (themeSettingId) { - return this.colorThemeStore.findThemeDataBySettingsId(themeSettingId, undefined); + const theme = await this.colorThemeStore.findThemeDataBySettingsId(themeSettingId, undefined); + if (theme) { + return this.setColorTheme(theme.id, 'auto'); + } } - return Promise.resolve(undefined); + return Promise.resolve(null); } public getColorTheme(): IColorTheme { @@ -736,7 +741,7 @@ const preferredHCThemeSettingSchema: IConfigurationPropertySchema = { }; const detectColorSchemeSettingSchema: IConfigurationPropertySchema = { type: 'boolean', - description: nls.localize('detectColorScheme', 'If set, use the prefered color theme based on the OS appearance.'), + description: nls.localize('detectColorScheme', 'If set, automatically switch to the preferred color theme based on the OS appearance.'), default: true }; From 10091e7300581b075ba8c2773187c38ffeeac266 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 Dec 2019 16:22:21 +0100 Subject: [PATCH 8/8] polish --- .../themes/browser/workbenchThemeService.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index b9af024b8abfa..ed8db767922af 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -302,7 +302,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (e.affectsConfiguration(COLOR_THEME_SETTING)) { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { if (theme) { this.setColorTheme(theme.id, undefined); } @@ -310,7 +310,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } } if (e.affectsConfiguration(DETECT_COLOR_SCHEME_SETTING)) { - this.preferredSchemeUpdated(); + this.handlePreferredSchemeUpdated(); } if (e.affectsConfiguration(PREFERRED_DARK_THEME_SETTING) && this.getPreferredColorScheme() === DARK) { this.applyPreferredColorTheme(DARK); @@ -354,10 +354,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { // preferred scheme handling private installPreferredSchemeListener() { - window.matchMedia('(prefers-color-scheme: dark)').addListener(async () => this.preferredSchemeUpdated()); + window.matchMedia('(prefers-color-scheme: dark)').addListener(async () => this.handlePreferredSchemeUpdated()); } - private async preferredSchemeUpdated() { + private async handlePreferredSchemeUpdated() { const scheme = this.getPreferredColorScheme(); this.storageService.store(PERSISTED_OS_COLOR_SCHEME, scheme, StorageScope.GLOBAL); if (scheme) { @@ -390,7 +390,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.setColorTheme(theme.id, 'auto'); } } - return Promise.resolve(null); + return null; } public getColorTheme(): IColorTheme { @@ -419,7 +419,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (!themeData) { return null; } - this.configurationService.updateValue(COLOR_THEME_SETTING, themeData.settingsId); return themeData.ensureLoaded(this.extensionResourceLoaderService).then(_ => { if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { this.currentColorTheme.clearCaches(); @@ -453,7 +452,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { public restoreColorTheme() { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { if (theme) { this.setColorTheme(theme.id, undefined); }