Skip to content

Commit 950ad4f

Browse files
committed
colors: add support for exporting colors
Make color definitions introspectable, where previously there was data hidden in closures. This allows colors to be exported and used in other tools.
1 parent 2c50328 commit 950ad4f

8 files changed

Lines changed: 97 additions & 66 deletions

File tree

scripts/test.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ set NAMESHORT=%NAMESHORT:"=%.exe
1212
set CODE=".build\electron\%NAMESHORT%"
1313

1414
:: Download Electron if needed
15-
node build\lib\electron.js
15+
call node build\lib\electron.js
1616
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
1717

1818
:: Run tests

src/vs/platform/theme/common/colorRegistry.ts

Lines changed: 66 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Event, Emitter } from 'vs/base/common/event';
1111
import * as nls from 'vs/nls';
1212
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
1313
import { RunOnceScheduler } from 'vs/base/common/async';
14+
import { assertNever } from 'vs/base/common/types';
1415

1516
// ------ API types
1617

@@ -24,11 +25,21 @@ export interface ColorContribution {
2425
readonly deprecationMessage: string | undefined;
2526
}
2627

27-
28-
export interface ColorFunction {
29-
(theme: IColorTheme): Color | undefined;
28+
export const enum ColorTransformType {
29+
Darken,
30+
Lighten,
31+
Transparent,
32+
OneOf,
33+
LessProminent,
3034
}
3135

36+
export type ColorTransform =
37+
| { op: ColorTransformType.Darken; value: ColorValue; factor: number }
38+
| { op: ColorTransformType.Lighten; value: ColorValue; factor: number }
39+
| { op: ColorTransformType.Transparent; value: ColorValue; factor: number }
40+
| { op: ColorTransformType.OneOf; values: readonly ColorValue[] }
41+
| { op: ColorTransformType.LessProminent; value: ColorValue; background: ColorValue; factor: number; transparency: number };
42+
3243
export interface ColorDefaults {
3344
light: ColorValue | null;
3445
dark: ColorValue | null;
@@ -38,7 +49,7 @@ export interface ColorDefaults {
3849
/**
3950
* A Color Value is either a color literal, a reference to an other color or a derived color
4051
*/
41-
export type ColorValue = Color | string | ColorIdentifier | ColorFunction;
52+
export type ColorValue = Color | string | ColorIdentifier | ColorTransform;
4253

4354
// color registry
4455
export const Extensions = {
@@ -495,63 +506,63 @@ export const chartsPurple = registerColor('charts.purple', { dark: '#B180D7', li
495506

496507
// ----- color functions
497508

498-
export function darken(colorValue: ColorValue, factor: number): ColorFunction {
499-
return (theme) => {
500-
let color = resolveColorValue(colorValue, theme);
501-
if (color) {
502-
return color.darken(factor);
503-
}
504-
return undefined;
505-
};
509+
export function executeTransform(transform: ColorTransform, theme: IColorTheme) {
510+
switch (transform.op) {
511+
case ColorTransformType.Darken:
512+
return resolveColorValue(transform.value, theme)?.darken(transform.factor);
513+
514+
case ColorTransformType.Lighten:
515+
return resolveColorValue(transform.value, theme)?.lighten(transform.factor);
516+
517+
case ColorTransformType.Transparent:
518+
return resolveColorValue(transform.value, theme)?.transparent(transform.factor);
519+
520+
case ColorTransformType.OneOf:
521+
for (const candidate of transform.values) {
522+
const color = resolveColorValue(candidate, theme);
523+
if (color) {
524+
return color;
525+
}
526+
}
527+
return undefined;
528+
529+
case ColorTransformType.LessProminent:
530+
const from = resolveColorValue(transform.value, theme);
531+
if (!from) {
532+
return undefined;
533+
}
534+
535+
const backgroundColor = resolveColorValue(transform.background, theme);
536+
if (!backgroundColor) {
537+
return from.transparent(transform.factor * transform.transparency);
538+
}
539+
540+
return from.isDarkerThan(backgroundColor)
541+
? Color.getLighterColor(from, backgroundColor, transform.factor).transparent(transform.transparency)
542+
: Color.getDarkerColor(from, backgroundColor, transform.factor).transparent(transform.transparency);
543+
default:
544+
throw assertNever(transform);
545+
}
506546
}
507547

508-
export function lighten(colorValue: ColorValue, factor: number): ColorFunction {
509-
return (theme) => {
510-
let color = resolveColorValue(colorValue, theme);
511-
if (color) {
512-
return color.lighten(factor);
513-
}
514-
return undefined;
515-
};
548+
export function darken(colorValue: ColorValue, factor: number): ColorTransform {
549+
return { op: ColorTransformType.Darken, value: colorValue, factor };
516550
}
517551

518-
export function transparent(colorValue: ColorValue, factor: number): ColorFunction {
519-
return (theme) => {
520-
let color = resolveColorValue(colorValue, theme);
521-
if (color) {
522-
return color.transparent(factor);
523-
}
524-
return undefined;
525-
};
552+
export function lighten(colorValue: ColorValue, factor: number): ColorTransform {
553+
return { op: ColorTransformType.Lighten, value: colorValue, factor };
526554
}
527555

528-
export function oneOf(...colorValues: ColorValue[]): ColorFunction {
529-
return (theme) => {
530-
for (let colorValue of colorValues) {
531-
let color = resolveColorValue(colorValue, theme);
532-
if (color) {
533-
return color;
534-
}
535-
}
536-
return undefined;
537-
};
556+
export function transparent(colorValue: ColorValue, factor: number): ColorTransform {
557+
return { op: ColorTransformType.Transparent, value: colorValue, factor };
538558
}
539559

540-
function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, factor: number, transparency: number): ColorFunction {
541-
return (theme) => {
542-
let from = resolveColorValue(colorValue, theme);
543-
if (from) {
544-
let backgroundColor = resolveColorValue(backgroundColorValue, theme);
545-
if (backgroundColor) {
546-
if (from.isDarkerThan(backgroundColor)) {
547-
return Color.getLighterColor(from, backgroundColor, factor).transparent(transparency);
548-
}
549-
return Color.getDarkerColor(from, backgroundColor, factor).transparent(transparency);
550-
}
551-
return from.transparent(factor * transparency);
552-
}
553-
return undefined;
554-
};
560+
export function oneOf(...colorValues: ColorValue[]): ColorTransform {
561+
return { op: ColorTransformType.OneOf, values: colorValues };
562+
}
563+
564+
function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, factor: number, transparency: number): ColorTransform {
565+
return { op: ColorTransformType.LessProminent, value: colorValue, background: backgroundColorValue, factor, transparency };
555566
}
556567

557568
// ----- implementation
@@ -569,8 +580,8 @@ export function resolveColorValue(colorValue: ColorValue | null, theme: IColorTh
569580
return theme.getColor(colorValue);
570581
} else if (colorValue instanceof Color) {
571582
return colorValue;
572-
} else if (typeof colorValue === 'function') {
573-
return colorValue(theme);
583+
} else if (typeof colorValue === 'object') {
584+
return executeTransform(colorValue, theme);
574585
}
575586
return undefined;
576587
}

src/vs/platform/theme/common/styler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
7-
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, listFocusOutline, listInactiveFocusOutline, tableColumnsBorder, quickInputListFocusBackground, buttonBorder, keybindingLabelForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, quickInputListFocusForeground } from 'vs/platform/theme/common/colorRegistry';
7+
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, listFocusOutline, listInactiveFocusOutline, tableColumnsBorder, quickInputListFocusBackground, buttonBorder, keybindingLabelForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, quickInputListFocusForeground, ColorTransform } from 'vs/platform/theme/common/colorRegistry';
88
import { IDisposable } from 'vs/base/common/lifecycle';
99
import { Color } from 'vs/base/common/color';
1010
import { IThemable, styleFn } from 'vs/base/common/styler';
@@ -269,7 +269,7 @@ export function attachStylerCallback(themeService: IThemeService, colors: { [nam
269269
}
270270

271271
export interface IBreadcrumbsWidgetStyleOverrides extends IColorMapping {
272-
breadcrumbsBackground?: ColorIdentifier | ColorFunction;
272+
breadcrumbsBackground?: ColorIdentifier | ColorTransform;
273273
breadcrumbsForeground?: ColorIdentifier;
274274
breadcrumbsHoverForeground?: ColorIdentifier;
275275
breadcrumbsFocusForeground?: ColorIdentifier;

src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
2424
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
2525
import { IListService, WorkbenchDataTree, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService';
2626
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
27-
import { ColorIdentifier, ColorFunction } from 'vs/platform/theme/common/colorRegistry';
27+
import { ColorIdentifier, ColorTransform } from 'vs/platform/theme/common/colorRegistry';
2828
import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler';
2929
import { IThemeService } from 'vs/platform/theme/common/themeService';
3030
import { ResourceLabel } from 'vs/workbench/browser/labels';
@@ -146,7 +146,7 @@ export interface IBreadcrumbsControlOptions {
146146
showFileIcons: boolean;
147147
showSymbolIcons: boolean;
148148
showDecorationColors: boolean;
149-
breadcrumbsBackground: ColorIdentifier | ColorFunction;
149+
breadcrumbsBackground: ColorIdentifier | ColorTransform;
150150
showPlaceholder: boolean;
151151
}
152152

src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { IMenu, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/co
3030
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
3131
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
3232
import { IOpenerService } from 'vs/platform/opener/common/opener';
33-
import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, transparent } from 'vs/platform/theme/common/colorRegistry';
33+
import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, resolveColorValue, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
3434
import { IColorTheme, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
3535
import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions';
3636
import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget';
@@ -805,7 +805,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
805805
renderOptions: {
806806
after: {
807807
contentText: placeholder,
808-
color: `${transparent(editorForeground, 0.4)(this.themeService.getColorTheme())}`
808+
color: `${resolveColorValue(editorForeground, this.themeService.getColorTheme())?.transparent(0.4)}`
809809
}
810810
}
811811
}];

src/vs/workbench/contrib/debug/browser/repl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/
3333
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
3434
import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
3535
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
36-
import { transparent, editorForeground } from 'vs/platform/theme/common/colorRegistry';
36+
import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
3737
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
3838
import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
3939
import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes';
@@ -657,7 +657,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
657657

658658
const decorations: IDecorationOptions[] = [];
659659
if (this.isReadonly && this.replInput.hasTextFocus() && !this.replInput.getValue()) {
660-
const transparentForeground = transparent(editorForeground, 0.4)(this.themeService.getColorTheme());
660+
const transparentForeground = resolveColorValue(editorForeground, this.themeService.getColorTheme())?.transparent(0.4);
661661
decorations.push({
662662
range: {
663663
startLineNumber: 0,

src/vs/workbench/contrib/feedback/browser/feedback.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
1313
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
1414
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
1515
import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
16-
import { editorWidgetBackground, editorWidgetForeground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, textLinkForeground, contrastBorder, darken } from 'vs/platform/theme/common/colorRegistry';
16+
import { editorWidgetBackground, editorWidgetForeground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, textLinkForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
1717
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
1818
import { Button } from 'vs/base/browser/ui/button/button';
1919
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -142,7 +142,7 @@ export class FeedbackWidget extends Dropdown {
142142
if (darkenFactor) {
143143
const backgroundBaseColor = theme.getColor(editorWidgetBackground);
144144
if (backgroundBaseColor) {
145-
const backgroundColor = darken(backgroundBaseColor, darkenFactor)(theme);
145+
const backgroundColor = backgroundBaseColor.darken(darkenFactor);
146146
if (backgroundColor) {
147147
closeBtn.style.backgroundColor = backgroundColor.toString();
148148
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { Color } from 'vs/base/common/color';
7+
import { Registry } from 'vs/platform/registry/common/platform';
8+
import { Extensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry';
9+
10+
suite('ColorRegistry', () => {
11+
if (process.env.VSCODE_COLOR_REGISTRY_EXPORT) {
12+
test('exports', () => {
13+
const themingRegistry = Registry.as<IColorRegistry>(Extensions.ColorContribution);
14+
const colors = themingRegistry.getColors();
15+
const replacer = (_key: string, value: unknown) =>
16+
value instanceof Color ? Color.Format.CSS.formatHexA(value) : value;
17+
console.log(`#colors:${JSON.stringify(colors, replacer)}\n`);
18+
});
19+
}
20+
});

0 commit comments

Comments
 (0)