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.
This commit is contained in:
Connor Peet 2021-05-28 14:15:17 -07:00
parent 2c50328192
commit 950ad4ffec
No known key found for this signature in database
GPG key ID: CF8FD2EA0DBC61BD
8 changed files with 100 additions and 69 deletions

View file

@ -12,7 +12,7 @@ set NAMESHORT=%NAMESHORT:"=%.exe
set CODE=".build\electron\%NAMESHORT%"
:: Download Electron if needed
node build\lib\electron.js
call node build\lib\electron.js
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
:: Run tests

View file

@ -11,6 +11,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import * as nls from 'vs/nls';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { RunOnceScheduler } from 'vs/base/common/async';
import { assertNever } from 'vs/base/common/types';
// ------ API types
@ -24,11 +25,21 @@ export interface ColorContribution {
readonly deprecationMessage: string | undefined;
}
export interface ColorFunction {
(theme: IColorTheme): Color | undefined;
export const enum ColorTransformType {
Darken,
Lighten,
Transparent,
OneOf,
LessProminent,
}
export type ColorTransform =
| { op: ColorTransformType.Darken; value: ColorValue; factor: number }
| { op: ColorTransformType.Lighten; value: ColorValue; factor: number }
| { op: ColorTransformType.Transparent; value: ColorValue; factor: number }
| { op: ColorTransformType.OneOf; values: readonly ColorValue[] }
| { op: ColorTransformType.LessProminent; value: ColorValue; background: ColorValue; factor: number; transparency: number };
export interface ColorDefaults {
light: ColorValue | null;
dark: ColorValue | null;
@ -38,7 +49,7 @@ export interface ColorDefaults {
/**
* A Color Value is either a color literal, a reference to an other color or a derived color
*/
export type ColorValue = Color | string | ColorIdentifier | ColorFunction;
export type ColorValue = Color | string | ColorIdentifier | ColorTransform;
// color registry
export const Extensions = {
@ -495,63 +506,63 @@ export const chartsPurple = registerColor('charts.purple', { dark: '#B180D7', li
// ----- color functions
export function darken(colorValue: ColorValue, factor: number): ColorFunction {
return (theme) => {
let color = resolveColorValue(colorValue, theme);
if (color) {
return color.darken(factor);
}
return undefined;
};
}
export function executeTransform(transform: ColorTransform, theme: IColorTheme) {
switch (transform.op) {
case ColorTransformType.Darken:
return resolveColorValue(transform.value, theme)?.darken(transform.factor);
export function lighten(colorValue: ColorValue, factor: number): ColorFunction {
return (theme) => {
let color = resolveColorValue(colorValue, theme);
if (color) {
return color.lighten(factor);
}
return undefined;
};
}
case ColorTransformType.Lighten:
return resolveColorValue(transform.value, theme)?.lighten(transform.factor);
export function transparent(colorValue: ColorValue, factor: number): ColorFunction {
return (theme) => {
let color = resolveColorValue(colorValue, theme);
if (color) {
return color.transparent(factor);
}
return undefined;
};
}
case ColorTransformType.Transparent:
return resolveColorValue(transform.value, theme)?.transparent(transform.factor);
export function oneOf(...colorValues: ColorValue[]): ColorFunction {
return (theme) => {
for (let colorValue of colorValues) {
let color = resolveColorValue(colorValue, theme);
if (color) {
return color;
}
}
return undefined;
};
}
function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, factor: number, transparency: number): ColorFunction {
return (theme) => {
let from = resolveColorValue(colorValue, theme);
if (from) {
let backgroundColor = resolveColorValue(backgroundColorValue, theme);
if (backgroundColor) {
if (from.isDarkerThan(backgroundColor)) {
return Color.getLighterColor(from, backgroundColor, factor).transparent(transparency);
case ColorTransformType.OneOf:
for (const candidate of transform.values) {
const color = resolveColorValue(candidate, theme);
if (color) {
return color;
}
return Color.getDarkerColor(from, backgroundColor, factor).transparent(transparency);
}
return from.transparent(factor * transparency);
}
return undefined;
};
return undefined;
case ColorTransformType.LessProminent:
const from = resolveColorValue(transform.value, theme);
if (!from) {
return undefined;
}
const backgroundColor = resolveColorValue(transform.background, theme);
if (!backgroundColor) {
return from.transparent(transform.factor * transform.transparency);
}
return from.isDarkerThan(backgroundColor)
? Color.getLighterColor(from, backgroundColor, transform.factor).transparent(transform.transparency)
: Color.getDarkerColor(from, backgroundColor, transform.factor).transparent(transform.transparency);
default:
throw assertNever(transform);
}
}
export function darken(colorValue: ColorValue, factor: number): ColorTransform {
return { op: ColorTransformType.Darken, value: colorValue, factor };
}
export function lighten(colorValue: ColorValue, factor: number): ColorTransform {
return { op: ColorTransformType.Lighten, value: colorValue, factor };
}
export function transparent(colorValue: ColorValue, factor: number): ColorTransform {
return { op: ColorTransformType.Transparent, value: colorValue, factor };
}
export function oneOf(...colorValues: ColorValue[]): ColorTransform {
return { op: ColorTransformType.OneOf, values: colorValues };
}
function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, factor: number, transparency: number): ColorTransform {
return { op: ColorTransformType.LessProminent, value: colorValue, background: backgroundColorValue, factor, transparency };
}
// ----- implementation
@ -569,8 +580,8 @@ export function resolveColorValue(colorValue: ColorValue | null, theme: IColorTh
return theme.getColor(colorValue);
} else if (colorValue instanceof Color) {
return colorValue;
} else if (typeof colorValue === 'function') {
return colorValue(theme);
} else if (typeof colorValue === 'object') {
return executeTransform(colorValue, theme);
}
return undefined;
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
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';
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';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
import { IThemable, styleFn } from 'vs/base/common/styler';
@ -269,7 +269,7 @@ export function attachStylerCallback(themeService: IThemeService, colors: { [nam
}
export interface IBreadcrumbsWidgetStyleOverrides extends IColorMapping {
breadcrumbsBackground?: ColorIdentifier | ColorFunction;
breadcrumbsBackground?: ColorIdentifier | ColorTransform;
breadcrumbsForeground?: ColorIdentifier;
breadcrumbsHoverForeground?: ColorIdentifier;
breadcrumbsFocusForeground?: ColorIdentifier;

View file

@ -24,7 +24,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IListService, WorkbenchDataTree, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { ColorIdentifier, ColorFunction } from 'vs/platform/theme/common/colorRegistry';
import { ColorIdentifier, ColorTransform } from 'vs/platform/theme/common/colorRegistry';
import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ResourceLabel } from 'vs/workbench/browser/labels';
@ -146,7 +146,7 @@ export interface IBreadcrumbsControlOptions {
showFileIcons: boolean;
showSymbolIcons: boolean;
showDecorationColors: boolean;
breadcrumbsBackground: ColorIdentifier | ColorFunction;
breadcrumbsBackground: ColorIdentifier | ColorTransform;
showPlaceholder: boolean;
}

View file

@ -30,7 +30,7 @@ import { IMenu, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/co
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, transparent } from 'vs/platform/theme/common/colorRegistry';
import { contrastBorder, editorForeground, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, resolveColorValue, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions';
import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget';
@ -805,7 +805,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
renderOptions: {
after: {
contentText: placeholder,
color: `${transparent(editorForeground, 0.4)(this.themeService.getColorTheme())}`
color: `${resolveColorValue(editorForeground, this.themeService.getColorTheme())?.transparent(0.4)}`
}
}
}];

View file

@ -33,7 +33,7 @@ import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
import { transparent, editorForeground } from 'vs/platform/theme/common/colorRegistry';
import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes';
@ -657,7 +657,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
const decorations: IDecorationOptions[] = [];
if (this.isReadonly && this.replInput.hasTextFocus() && !this.replInput.getValue()) {
const transparentForeground = transparent(editorForeground, 0.4)(this.themeService.getColorTheme());
const transparentForeground = resolveColorValue(editorForeground, this.themeService.getColorTheme())?.transparent(0.4);
decorations.push({
range: {
startLineNumber: 0,

View file

@ -13,7 +13,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { editorWidgetBackground, editorWidgetForeground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, textLinkForeground, contrastBorder, darken } from 'vs/platform/theme/common/colorRegistry';
import { editorWidgetBackground, editorWidgetForeground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, textLinkForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Button } from 'vs/base/browser/ui/button/button';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@ -142,7 +142,7 @@ export class FeedbackWidget extends Dropdown {
if (darkenFactor) {
const backgroundBaseColor = theme.getColor(editorWidgetBackground);
if (backgroundBaseColor) {
const backgroundColor = darken(backgroundBaseColor, darkenFactor)(theme);
const backgroundColor = backgroundBaseColor.darken(darkenFactor);
if (backgroundColor) {
closeBtn.style.backgroundColor = backgroundColor.toString();
}

View file

@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Color } from 'vs/base/common/color';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry';
suite('ColorRegistry', () => {
if (process.env.VSCODE_COLOR_REGISTRY_EXPORT) {
test('exports', () => {
const themingRegistry = Registry.as<IColorRegistry>(Extensions.ColorContribution);
const colors = themingRegistry.getColors();
const replacer = (_key: string, value: unknown) =>
value instanceof Color ? Color.Format.CSS.formatHexA(value) : value;
console.log(`#colors:${JSON.stringify(colors, replacer)}\n`);
});
}
});