theme: fix unthemable icons in several areas (#209131)

* theme: fix unthemable icons in several areas

We manually used icon characters in several areas. These are/were entire unthemable. Fixing this required people to manually listen to the theme service and apply rules to their elements when icons change. This PRs adds theme variables that people can use instead.

Fixes #208343

* implement review comments

* fix font-family variable

---------

Co-authored-by: Martin Aeschlimann <martinae@microsoft.com>
This commit is contained in:
Connor Peet 2024-04-11 08:30:49 -07:00 committed by GitHub
parent 49c1a3ee95
commit 6b391eba24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 68 additions and 40 deletions

View file

@ -17,6 +17,7 @@ function getKnownVariableNames() {
}
return knownVariables;
}
const iconVariable = /^--vscode-icon-.+-(content|font-family)$/;
function getVariableNameValidator() {
const allVariables = getKnownVariableNames();
return (value, report) => {
@ -24,7 +25,7 @@ function getVariableNameValidator() {
let match;
while (match = RE_VAR_PROP.exec(value)) {
const variableName = match[1];
if (variableName && !allVariables.has(variableName)) {
if (variableName && !allVariables.has(variableName) && !iconVariable.test(variableName)) {
report(variableName);
}
}

View file

@ -18,6 +18,8 @@ function getKnownVariableNames() {
return knownVariables;
}
const iconVariable = /^--vscode-icon-.+-(content|font-family)$/;
export interface IValidator {
(value: string, report: (message: string) => void): void;
}
@ -29,7 +31,7 @@ export function getVariableNameValidator(): IValidator {
let match;
while (match = RE_VAR_PROP.exec(value)) {
const variableName = match[1];
if (variableName && !allVariables.has(variableName)) {
if (variableName && !allVariables.has(variableName) && !iconVariable.test(variableName)) {
report(variableName);
}
}

View file

@ -9,7 +9,7 @@
src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype");
}
.codicon[class*='codicon-'] {
.codicon {
font: normal normal normal 16px/1 codicon;
display: inline-block;
text-decoration: none;

View file

@ -99,7 +99,8 @@
.menubar:not(.compact) .menubar-menu-button:first-child .toolbar-toggle-more::before,
.menubar.compact .toolbar-toggle-more::before {
content: "\eb94" !important;
content: var(--vscode-icon-menu-content) !important;
font-family: var(--vscode-icon-menu-font-family) !important;
}
/* Match behavior of outline for activity bar icons */

View file

@ -25,5 +25,6 @@
}
.inline-progress-widget:hover .icon::before {
content: "\ea76"; /* codicon-x */
content: var(--vscode-icon-x-content);
font-family: var(--vscode-icon-x-font-family);
}

View file

@ -31,27 +31,31 @@ export function getIconsStyleSheet(themeService: IThemeService | undefined): IIc
getCSS() {
const productIconTheme = themeService ? themeService.getProductIconTheme() : new UnthemedProductIconTheme();
const usedFontIds: { [id: string]: IconFontDefinition } = {};
const formatIconRule = (contribution: IconContribution): string | undefined => {
const rules: string[] = [];
const rootAttribs: string[] = [];
for (const contribution of iconRegistry.getIcons()) {
const definition = productIconTheme.getIcon(contribution);
if (!definition) {
return undefined;
continue;
}
const fontContribution = definition.font;
const fontFamilyVar = `--vscode-icon-${contribution.id}-font-family`;
const contentVar = `--vscode-icon-${contribution.id}-content`;
if (fontContribution) {
usedFontIds[fontContribution.id] = fontContribution.definition;
return `.codicon-${contribution.id}:before { content: '${definition.fontCharacter}'; font-family: ${asCSSPropertyValue(fontContribution.id)}; }`;
}
// default font (codicon)
return `.codicon-${contribution.id}:before { content: '${definition.fontCharacter}'; }`;
};
const rules = [];
for (const contribution of iconRegistry.getIcons()) {
const rule = formatIconRule(contribution);
if (rule) {
rules.push(rule);
rootAttribs.push(
`${fontFamilyVar}: ${asCSSPropertyValue(fontContribution.id)};`,
`${contentVar}: '${definition.fontCharacter}';`,
);
rules.push(`.codicon-${contribution.id}:before { content: '${definition.fontCharacter}'; font-family: ${asCSSPropertyValue(fontContribution.id)}; }`);
} else {
rootAttribs.push(`${contentVar}: '${definition.fontCharacter}'; ${fontFamilyVar}: 'codicon';`);
rules.push(`.codicon-${contribution.id}:before { content: '${definition.fontCharacter}'; }`);
}
}
for (const id in usedFontIds) {
const definition = usedFontIds[id];
const fontWeight = definition.weight ? `font-weight: ${definition.weight};` : '';
@ -59,6 +63,9 @@ export function getIconsStyleSheet(themeService: IThemeService | undefined): IIc
const src = definition.src.map(l => `${asCSSUrl(l.location)} format('${l.format}')`).join(', ');
rules.push(`@font-face { src: ${src}; font-family: ${asCSSPropertyValue(id)};${fontWeight}${fontStyle} font-display: block; }`);
}
rules.push(`:root { ${rootAttribs.join(' ')} }`);
return rules.join('\n');
}
};

View file

@ -4,7 +4,9 @@
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-workspace::before {
content: "\ea76"; /* Close icon flips between black dot and "X" for dirty workspaces */
/* Close icon flips between black dot and "X" for dirty workspaces */
content: var(--vscode-icon-x-content);
font-family: var(--vscode-icon-x-font-family);
}
.monaco-workbench .screencast-mouse {

View file

@ -196,7 +196,8 @@ body.web {
}
.monaco-workbench .select-container:after {
content: "\eab4";
content: var(--vscode-icon-chevron-down-content);
font-family: var(--vscode-icon-chevron-down-font-family);
font-family: codicon;
font-size: 16px;
width: 16px;

View file

@ -4,5 +4,7 @@
*--------------------------------------------------------------------------------------------*/
.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-editor::before {
content: "\ea76"; /* Close icon flips between black dot and "X" for dirty open editors */
/* Close icon flips between black dot and "X" for dirty open editors */
content: var(--vscode-icon-x-content);
font-family: var(--vscode-icon-x-font-family);
}

View file

@ -420,14 +420,16 @@
.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.sticky.dirty > .tab-actions .action-label:not(:hover)::before,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.dirty > .tab-actions .action-label:not(:hover)::before {
content: "\ebb2"; /* use `pinned-dirty` icon unicode for sticky-dirty indication */
font-family: 'codicon';
/* use `pinned-dirty` icon unicode for sticky-dirty indication */
content: var(--vscode-icon-pinned-dirty-content);
font-family: var(--vscode-icon-pinned-dirty-font-family);
}
.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover)::before,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover)::before {
content: "\ea71"; /* use `circle-filled` icon unicode for dirty indication */
font-family: 'codicon';
/* use `circle-filled` icon unicode for dirty indication */
content: var(--vscode-icon-circle-filled-content);
font-family: var(--vscode-icon-circle-filled-font-family);
}
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-actions .action-label,

View file

@ -7,8 +7,8 @@
* Replace with "microphone" icon.
*/
.monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
content: "\ec1c";
font-family: 'codicon';
content: var(--vscode-icon-mic-filled-content);
font-family: var(--vscode-icon-mic-filled-font-family);
}
/*
@ -22,6 +22,6 @@
* Replace with "stop" icon when reduced motion is enabled.
*/
.monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before {
content: "\ead7";
font-family: 'codicon';
content: var(--vscode-icon-debug-stop-content);
font-family: var(--vscode-icon-debug-stop-font-family);
}

View file

@ -530,7 +530,8 @@ div.preview.inline .monaco-editor .comment-range-glyph {
.monaco-editor .margin-view-overlays > div:hover > .comment-range-glyph.comment-diff-added:before,
.monaco-editor .margin-view-overlays .comment-range-glyph.line-hover:before {
content: "\ea60";
content: var(--vscode-icon-plus-content);
font-family: var(--vscode-icon-plus-font-family);
font-family: "codicon";
border-radius: 3px;
width: 18px !important;
@ -557,11 +558,13 @@ div.preview.inline .monaco-editor .comment-range-glyph {
}
.monaco-editor .comment-range-glyph.comment-thread:before {
content: "\ea6b";
content: var(--vscode-icon-comment-add-content);
font-family: var(--vscode-icon-comment-add-font-family);
}
.monaco-editor .comment-range-glyph.comment-thread-unresolved:before {
content: "\ec0a";
content: var(--vscode-icon-comment-unresolved-content);
font-family: var(--vscode-icon-comment-unresolved-font-family);
}
.monaco-editor.inline-comment .margin-view-overlays .codicon-folding-expanded,

View file

@ -36,7 +36,8 @@
.codicon-debug-breakpoint-conditional.codicon-debug-stackframe::after,
.codicon-debug-breakpoint.codicon-debug-stackframe-focused::after,
.codicon-debug-breakpoint.codicon-debug-stackframe::after {
content: '\eb8a';
content: var(--vscode-icon-circle-small-filled-content);
font-family: var(--vscode-icon-circle-small-filled-font-family);
position: absolute;
}

View file

@ -37,13 +37,15 @@
}
.open-editors .monaco-list .monaco-list-row.dirty:not(:hover) > .monaco-action-bar .codicon-pinned::before {
content: "\ebb2"; /* use `pinned-dirty` icon unicode for sticky-dirty indication */
font-family: 'codicon';
/* use `pinned-dirty` icon unicode for sticky-dirty indication */
content: var(--vscode-icon-pinned-dirty-content);
font-family: var(--vscode-icon-pinned-dirty-font-family);
}
.open-editors .monaco-list .monaco-list-row.dirty:not(:hover) > .monaco-action-bar .codicon-close::before {
content: "\ea71"; /* use `circle-filled` icon unicode for dirty indication */
font-family: 'codicon';
/* use `circle-filled` icon unicode for dirty indication */
content: var(--vscode-icon-circle-filled-content);
font-family: var(--vscode-icon-circle-filled-font-family);
}
.open-editors .monaco-list .monaco-list-row > .monaco-action-bar .action-close-all-files,

View file

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-anything::before {
content: "\ea76"; /* Close icon flips between black dot and "X" for dirty open editors */
font-family: 'codicon';
/* Close icon flips between black dot and "X" for dirty open editors */
content: var(--vscode-icon-x-content);
font-family: var(--vscode-icon-x-font-family);
}

View file

@ -4,5 +4,7 @@
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-window::before {
content: "\ea76"; /* Close icon flips between black dot and "X" for dirty windows */
/* Close icon flips between black dot and "X" for dirty open editors */
content: var(--vscode-icon-x-content);
font-family: var(--vscode-icon-x-font-family);
}