Implementation mechanism of vscode multi-theme color function

The vscode page is divided into two parts, one part is provided by the plug-in, and the other part is the main body. Then vscode needs to consider combining these two parts for management in terms of multi-theme implementation, which is relatively more complicated than simply implementing multi-theme functions on web pages.

The main part is realized

Let's first look at how the style of the main part of vscode is drawn

registerThemingParticipant((theme, collector) => {
    
    
	const activitybarTextForeground = theme.getColor(foreground);
	if (activitybarTextForeground) {
    
    
		/**
		 * color --> vscode原生
		 * background-color --> 插件
		 */
		collector.addRule(`
			.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item .action-label {
				color: ${
      
      activitybarTextForeground} !important;
			}
		`);
		...
	}

This is for the style code of the activitybar part, themeto get the color, collectorcollect the style, and finally insert it into the global style to complete the style setting.

Let's getColorlook inside the method

src/vs/workbench/services/themes/common/colorThemeData.ts

public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color | undefined {
    
    
	let color: Color | undefined = this.customColorMap[colorId];
	if (color) {
    
    
		return color;
	}
	color = this.colorMap[colorId];
	if (useDefault !== false && types.isUndefined(color)) {
    
    
		color = this.getDefault(colorId);
	}
	return color;
}

Where does colorMap come from? Here comes the key point!
The multi-themes of vscode are actually managed through theme plug-ins.
insert image description here
insert image description here
All colors are taken from the json files in the themes directory.

{
    
    
	"type": "dark",
	"colors": {
    
    
		"dropdown.background": "#525252",
		"list.activeSelectionBackground": "#707070",
		"quickInputList.focusBackground": "#707070",
		"list.inactiveSelectionBackground": "#4e4e4e",
		...
		}
	}	

Here are the color variables to prevent.

Color registration is in most of this file
src/vs/platform/theme/common/colorRegistry.ts

...
// 基础颜色
export const secondForeground = registerColor('second.foreground', {
    
     light: '#737373', dark: '#fff', hcDark: '#fff', hcLight: '#737373' }, nls.localize('secondForeground', "Color for text separators."));
...

vscodeThe themes are roughly divided into four categories : dark theme, light theme, dark highlight theme, and light highlight theme .

Therefore, four default values ​​are set here, and if the value cannot be obtained in the plug-in, the default value will be found.

Ok, here is the color setting of the main part, let's take a look at the color setting in the plug-in.

plugin section

.search-warapper .searchInput .search-btn {
    
    
	...
	background-color: var(--vscode-productMain-color);
}

The plugin part uses the var function of css.

CSS var函数is a syntax for custom property values ​​that is used to insert the value of a custom property, but not any part of another property's value. It allows you to change styles without changing the stylesheet, which improves maintainability and reusability. Variable values ​​are inherited from parent classes.

insert image description here
All the styles obtained from the theme plugin are written into the html style.

Now I understand everything~

Theme style switching

insert image description here
wake up color picker
src/vs/workbench/contrib/themes/browser/themes.contribution.ts

override async run(accessor: ServicesAccessor) {
    
    
		const themeService = accessor.get(IWorkbenchThemeService);

		const installMessage = localize('installColorThemes', "Install Additional Color Themes...");
		const browseMessage = '$(plus) ' + localize('browseColorThemes', "Browse Additional Color Themes...");
		const placeholderMessage = localize('themes.selectTheme', "Select Color Theme (Up/Down Keys to Preview)");
		const marketplaceTag = 'category:themes';
		const setTheme = (theme: IWorkbenchTheme | undefined, settingsTarget: ThemeSettingTarget) => themeService.setColorTheme(theme as IWorkbenchColorTheme, settingsTarget);
		const getMarketplaceColorThemes = (publisher: string, name: string, version: string) => themeService.getMarketplaceColorThemes(publisher, name, version);

		const instantiationService = accessor.get(IInstantiationService);
		const picker = instantiationService.createInstance(InstalledThemesPicker, installMessage, browseMessage, placeholderMessage, marketplaceTag, setTheme, getMarketplaceColorThemes);

		const themes = await themeService.getColorThemes();
		const currentTheme = themeService.getColorTheme();

		const picks: QuickPickInput<ThemeItem>[] = [
			...toEntries(themes.filter(t => t.type === ColorScheme.LIGHT), localize('themes.category.light', "light themes")),
			...toEntries(themes.filter(t => t.type === ColorScheme.DARK), localize('themes.category.dark', "dark themes")),
			...toEntries(themes.filter(t => isHighContrast(t.type)), localize('themes.category.hc', "high contrast themes")),
		];
		await picker.openQuickPick(picks, currentTheme);
	}

This event is triggered when selecting up and down

quickpick.onDidChangeActive(themes => selectTheme(themes[0]?.theme, false));

Continue to follow up in
src/vs/workbench/services/themes/browser/workbenchThemeService.ts

public setColorTheme(themeIdOrTheme: string | undefined | IWorkbenchColorTheme, settingsTarget: ThemeSettingTarget): Promise<IWorkbenchColorTheme | null> {
    
    
		return this.colorThemeSequencer.queue(async () => {
    
    
			return this.internalSetColorTheme(themeIdOrTheme, settingsTarget);
		});
	}
private async internalSetColorTheme(themeIdOrTheme: string | undefined | IWorkbenchColorTheme, settingsTarget: ThemeSettingTarget): Promise<IWorkbenchColorTheme | null> {
    
    
		if (!themeIdOrTheme) {
    
    
			return null;
		}
		const themeId = types.isString(themeIdOrTheme) ? validateThemeId(themeIdOrTheme) : themeIdOrTheme.id;
		if (this.currentColorTheme.isLoaded && themeId === this.currentColorTheme.id) {
    
    
			if (settingsTarget !== 'preview') {
    
    
				this.currentColorTheme.toStorage(this.storageService);
			}
			return this.settings.setColorTheme(this.currentColorTheme, settingsTarget);
		}

		let themeData = this.colorThemeRegistry.findThemeById(themeId);
		if (!themeData) {
    
    
			if (themeIdOrTheme instanceof ColorThemeData) {
    
    
				themeData = themeIdOrTheme;
			} else {
    
    
				return null;
			}
		}
		try {
    
    
			await themeData.ensureLoaded(this.extensionResourceLoaderService);
			themeData.setCustomizations(this.settings);
			return this.applyTheme(themeData, settingsTarget);
		} catch (error) {
    
    
			throw new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.location?.toString(), error.message));
		}

	}

Take a look at what is done in the themeData.ensureLoade method

src/vs/workbench/services/themes/common/colorThemeData.ts

public ensureLoaded(extensionResourceLoaderService: IExtensionResourceLoaderService): Promise<void> {
    
    
		return !this.isLoaded ? this.load(extensionResourceLoaderService) : Promise.resolve(undefined);
	}

	public reload(extensionResourceLoaderService: IExtensionResourceLoaderService): Promise<void> {
    
    
		return this.load(extensionResourceLoaderService);
	}

	private load(extensionResourceLoaderService: IExtensionResourceLoaderService): Promise<void> {
    
    
		if (!this.location) {
    
    
			return Promise.resolve(undefined);
		}
		this.themeTokenColors = [];
		this.clearCaches();

		const result = {
    
    
			colors: {
    
    },
			textMateRules: [],
			semanticTokenRules: [],
			semanticHighlighting: false
		};
		return _loadColorTheme(extensionResourceLoaderService, this.location, result).then(_ => {
    
    
			this.isLoaded = true;
			this.semanticTokenRules = result.semanticTokenRules;
			this.colorMap = result.colors; //生成colorMap
			this.themeTokenColors = result.textMateRules;
			this.themeSemanticHighlighting = result.semanticHighlighting;
		});
	}

The colorMap of the main part of vscode above is generated here

then go down

	private updateDynamicCSSRules(themeData: IColorTheme) {
    
    
		...
		themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector, this.environmentService));

		const colorVariables: string[] = [];
		for (const item of getColorRegistry().getColors()) {
    
    
			const color = themeData.getColor(item.id, true);
			if (color) {
    
    
				colorVariables.push(`${
      
      asCssVariableName(item.id)}: ${
      
      color.toString()};`);
			}
		}
		ruleCollector.addRule(`.monaco-workbench { ${
      
      colorVariables.join('\n')} }`);

		_applyRules([...cssRules].join('\n'), colorThemeRulesClassName);
	}

here

themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector, this.environmentService));

Very important, the code of the style collector is re-called here, and it is put back into the collector

registerThemingParticipant((theme, collector) => {
    
    
	const activitybarTextForeground = theme.getColor(foreground);
	if (activitybarTextForeground) {
    
    
		/**
		 * color --> vscode原生
		 * background-color --> 插件
		 */
		collector.addRule(`
			.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item .action-label {
				color: ${
      
      activitybarTextForeground} !important;
			}
		`);
		...
	}

src/vs/workbench/services/themes/browser/workbenchThemeService.ts

function _applyRules(styleSheetContent: string, rulesClassName: string) {
    
    
	const themeStyles = document.head.getElementsByClassName(rulesClassName);
	if (themeStyles.length === 0) {
    
    
		const elStyle = document.createElement('style');
		elStyle.type = 'text/css';
		elStyle.className = rulesClassName;
		elStyle.textContent = styleSheetContent;
		document.head.appendChild(elStyle);
	} else {
    
    
		(<HTMLStyleElement>themeStyles[0]).textContent = styleSheetContent;
	}
}

Here you can see that the style is rewritten in style.
Theme switching is complete!

Brother Meng give attention

Guess you like

Origin blog.csdn.net/woyebuzhidao321/article/details/131495841