Skip to content

Commit

Permalink
keybindingLabel: switch theming to css variables (#165426)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeschli committed Nov 3, 2022
1 parent 0a6c728 commit 2e47c97
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 102 deletions.
80 changes: 31 additions & 49 deletions src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from 'vs/base/browser/dom';
import { Color } from 'vs/base/common/color';
import { UILabelProvider } from 'vs/base/common/keybindingLabels';
import { ResolvedKeybinding, ResolvedKeybindingPart } from 'vs/base/common/keybindings';
import { equals } from 'vs/base/common/objects';
import { OperatingSystem } from 'vs/base/common/platform';
import { IThemable } from 'vs/base/common/styler';
import 'vs/css!./keybindingLabel';
import { localize } from 'vs/nls';

Expand All @@ -32,15 +30,17 @@ export interface KeybindingLabelOptions extends IKeybindingLabelStyles {
renderUnboundKeybindings?: boolean;
}

export type CSSValueString = string;

export interface IKeybindingLabelStyles {
keybindingLabelBackground?: Color;
keybindingLabelForeground?: Color;
keybindingLabelBorder?: Color;
keybindingLabelBottomBorder?: Color;
keybindingLabelShadow?: Color;
keybindingLabelBackground?: CSSValueString;
keybindingLabelForeground?: CSSValueString;
keybindingLabelBorder?: CSSValueString;
keybindingLabelBottomBorder?: CSSValueString;
keybindingLabelShadow?: CSSValueString;
}

export class KeybindingLabel implements IThemable {
export class KeybindingLabel {

private domNode: HTMLElement;
private options: KeybindingLabelOptions;
Expand All @@ -51,22 +51,26 @@ export class KeybindingLabel implements IThemable {
private matches: Matches | undefined;
private didEverRender: boolean;

private labelBackground: Color | undefined;
private labelForeground: Color | undefined;
private labelBorder: Color | undefined;
private labelBottomBorder: Color | undefined;
private labelShadow: Color | undefined;
private labelBackground: CSSValueString | undefined;
private labelBorder: CSSValueString | undefined;
private labelBottomBorder: CSSValueString | undefined;
private labelShadow: CSSValueString | undefined;

constructor(container: HTMLElement, private os: OperatingSystem, options?: KeybindingLabelOptions) {
this.options = options || Object.create(null);

this.labelBackground = this.options.keybindingLabelBackground;
this.labelForeground = this.options.keybindingLabelForeground;
this.labelBorder = this.options.keybindingLabelBorder;
this.labelBottomBorder = this.options.keybindingLabelBottomBorder;
this.labelShadow = this.options.keybindingLabelShadow;

const labelForeground = this.options.keybindingLabelForeground;

this.domNode = dom.append(container, $('.monaco-keybinding'));
if (labelForeground) {
this.domNode.style.color = labelForeground;
}

this.didEverRender = false;
container.appendChild(this.domNode);
}
Expand Down Expand Up @@ -102,8 +106,6 @@ export class KeybindingLabel implements IThemable {
this.renderUnbound(this.domNode);
}

this.applyStyles();

this.didEverRender = true;
}

Expand Down Expand Up @@ -147,40 +149,20 @@ export class KeybindingLabel implements IThemable {
const keyElement = $('span.monaco-keybinding-key' + extraClass, undefined, label);
this.keyElements.add(keyElement);

return keyElement;
}

style(styles: IKeybindingLabelStyles): void {
this.labelBackground = styles.keybindingLabelBackground;
this.labelForeground = styles.keybindingLabelForeground;
this.labelBorder = styles.keybindingLabelBorder;
this.labelBottomBorder = styles.keybindingLabelBottomBorder;
this.labelShadow = styles.keybindingLabelShadow;

this.applyStyles();
}

private applyStyles() {
if (this.element) {
for (const keyElement of this.keyElements) {
if (this.labelBackground) {
keyElement.style.backgroundColor = this.labelBackground?.toString();
}
if (this.labelBorder) {
keyElement.style.borderColor = this.labelBorder.toString();
}
if (this.labelBottomBorder) {
keyElement.style.borderBottomColor = this.labelBottomBorder.toString();
}
if (this.labelShadow) {
keyElement.style.boxShadow = `inset 0 -1px 0 ${this.labelShadow}`;
}
}

if (this.labelForeground) {
this.element.style.color = this.labelForeground.toString();
}
if (this.labelBackground) {
keyElement.style.backgroundColor = this.labelBackground;
}
if (this.labelBorder) {
keyElement.style.borderColor = this.labelBorder;
}
if (this.labelBottomBorder) {
keyElement.style.borderBottomColor = this.labelBottomBorder;
}
if (this.labelShadow) {
keyElement.style.boxShadow = `inset 0 -1px 0 ${this.labelShadow}`;
}

return keyElement;
}

private static areSame(a: Matches | undefined, b: Matches | undefined): boolean {
Expand Down
4 changes: 2 additions & 2 deletions src/vs/editor/browser/editorDom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { asCssVariableName } from 'vs/platform/theme/common/colorRegistry';
import { asCssValue } from 'vs/platform/theme/common/colorRegistry';
import { ThemeColor } from 'vs/platform/theme/common/themeService';

/**
Expand Down Expand Up @@ -378,7 +378,7 @@ class RefCountedCssRule {
const value = (properties as any)[prop] as string | ThemeColor;
let cssValue;
if (typeof value === 'object') {
cssValue = `var(${asCssVariableName(value.id)})`;
cssValue = asCssValue(value.id);
} else {
cssValue = value;
}
Expand Down
26 changes: 26 additions & 0 deletions src/vs/platform/theme/browser/defaultStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IKeybindingLabelStyles } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { ColorIdentifier, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, keybindingLabelForeground, asCssValue, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { IStyleOverrides } from 'vs/platform/theme/common/styler';


export interface IKeybindingLabelStyleOverrides extends IStyleOverrides {
keybindingLabelBackground?: ColorIdentifier;
keybindingLabelForeground?: ColorIdentifier;
keybindingLabelBorder?: ColorIdentifier;
keybindingLabelBottomBorder?: ColorIdentifier;
keybindingLabelShadow?: ColorIdentifier;
}

export function getKeybindingLabelStyles(style?: IKeybindingLabelStyleOverrides): IKeybindingLabelStyles {
return {
keybindingLabelBackground: asCssValue(style?.keybindingLabelBackground || keybindingLabelBackground),
keybindingLabelForeground: asCssValue(style?.keybindingLabelForeground || keybindingLabelForeground),
keybindingLabelBorder: asCssValue(style?.keybindingLabelBorder || keybindingLabelBorder),
keybindingLabelBottomBorder: asCssValue(style?.keybindingLabelBottomBorder || keybindingLabelBottomBorder),
keybindingLabelShadow: asCssValue(style?.keybindingLabelShadow || widgetShadow)
};
}
4 changes: 4 additions & 0 deletions src/vs/platform/theme/common/colorRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export function asCssVariableName(colorIdent: ColorIdentifier): string {
return `--vscode-${colorIdent.replace(/\./g, '-')}`;
}

export function asCssValue(color: ColorIdentifier): string {
return `var(${asCssVariableName(color)})`;
}

export const enum ColorTransformType {
Darken,
Lighten,
Expand Down
21 changes: 1 addition & 20 deletions src/vs/platform/theme/common/styler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { Color } from 'vs/base/common/color';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IThemable, styleFn } from 'vs/base/common/styler';
import { activeContrastBorder, badgeBackground, badgeForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, breadcrumbsFocusForeground, breadcrumbsForeground, buttonBackground, buttonBorder, buttonForeground, buttonHoverBackground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, ColorIdentifier, ColorTransform, ColorValue, contrastBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetForeground, focusBorder, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, keybindingLabelBackground, keybindingLabelBorder, keybindingLabelBottomBorder, keybindingLabelForeground, listActiveSelectionBackground, listActiveSelectionForeground, listActiveSelectionIconForeground, listDropBackground, listFilterWidgetBackground, listFilterWidgetNoMatchesOutline, listFilterWidgetOutline, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, listInactiveFocusBackground, listInactiveFocusOutline, listInactiveSelectionBackground, listInactiveSelectionForeground, listInactiveSelectionIconForeground, menuBackground, menuBorder, menuForeground, menuSelectionBackground, menuSelectionBorder, menuSelectionForeground, menuSeparatorBackground, pickerGroupForeground, problemsErrorIconForeground, problemsInfoIconForeground, problemsWarningIconForeground, progressBarBackground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, resolveColorValue, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, selectBackground, selectBorder, selectForeground, selectListBackground, checkboxBackground, checkboxBorder, checkboxForeground, tableColumnsBorder, tableOddRowsBackgroundColor, textLinkForeground, treeIndentGuidesStroke, widgetShadow, listFocusAndSelectionOutline, listFilterWidgetShadow, buttonSeparator } from 'vs/platform/theme/common/colorRegistry';
import { activeContrastBorder, badgeBackground, badgeForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, breadcrumbsFocusForeground, breadcrumbsForeground, buttonBackground, buttonBorder, buttonForeground, buttonHoverBackground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, ColorIdentifier, ColorTransform, ColorValue, contrastBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetForeground, focusBorder, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, listActiveSelectionBackground, listActiveSelectionForeground, listActiveSelectionIconForeground, listDropBackground, listFilterWidgetBackground, listFilterWidgetNoMatchesOutline, listFilterWidgetOutline, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, listInactiveFocusBackground, listInactiveFocusOutline, listInactiveSelectionBackground, listInactiveSelectionForeground, listInactiveSelectionIconForeground, menuBackground, menuBorder, menuForeground, menuSelectionBackground, menuSelectionBorder, menuSelectionForeground, menuSeparatorBackground, pickerGroupForeground, problemsErrorIconForeground, problemsInfoIconForeground, problemsWarningIconForeground, progressBarBackground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, resolveColorValue, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, selectBackground, selectBorder, selectForeground, selectListBackground, checkboxBackground, checkboxBorder, checkboxForeground, tableColumnsBorder, tableOddRowsBackgroundColor, textLinkForeground, treeIndentGuidesStroke, widgetShadow, listFocusAndSelectionOutline, listFilterWidgetShadow, buttonSeparator } from 'vs/platform/theme/common/colorRegistry';
import { isHighContrast } from 'vs/platform/theme/common/theme';
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';

Expand Down Expand Up @@ -261,25 +261,6 @@ export function attachButtonStyler(widget: IThemable, themeService: IThemeServic
buttonBorder: style?.buttonBorder || buttonBorder,
} as IButtonStyleOverrides, widget);
}

export interface IKeybindingLabelStyleOverrides extends IStyleOverrides {
keybindingLabelBackground?: ColorIdentifier;
keybindingLabelForeground?: ColorIdentifier;
keybindingLabelBorder?: ColorIdentifier;
keybindingLabelBottomBorder?: ColorIdentifier;
keybindingLabelShadow?: ColorIdentifier;
}

export function attachKeybindingLabelStyler(widget: IThemable, themeService: IThemeService, style?: IKeybindingLabelStyleOverrides): IDisposable {
return attachStyler(themeService, {
keybindingLabelBackground: (style && style.keybindingLabelBackground) || keybindingLabelBackground,
keybindingLabelForeground: (style && style.keybindingLabelForeground) || keybindingLabelForeground,
keybindingLabelBorder: (style && style.keybindingLabelBorder) || keybindingLabelBorder,
keybindingLabelBottomBorder: (style && style.keybindingLabelBottomBorder) || keybindingLabelBottomBorder,
keybindingLabelShadow: (style && style.keybindingLabelShadow) || widgetShadow
} as IKeybindingLabelStyleOverrides, widget);
}

export interface IProgressBarStyleOverrides extends IStyleOverrides {
progressBarBackground?: ColorIdentifier;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { Delegate } from 'vs/workbench/contrib/extensions/browser/extensionsList';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { attachKeybindingLabelStyler } from 'vs/platform/theme/common/styler';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { errorIcon, infoIcon, preReleaseIcon, verifiedPublisherIcon as verifiedPublisherThemeIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons';
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
import { ViewContainerLocation } from 'vs/workbench/common/views';
import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import * as semver from 'vs/base/common/semver/semver';
import { getKeybindingLabelStyles } from 'vs/platform/theme/browser/defaultStyles';

class NavBar extends Disposable {

Expand Down Expand Up @@ -1556,9 +1556,8 @@ export class ExtensionEditor extends EditorPane {

const renderKeybinding = (keybinding: ResolvedKeybinding): HTMLElement => {
const element = $('');
const kbl = new KeybindingLabel(element, OS);
const kbl = new KeybindingLabel(element, OS, getKeybindingLabelStyles());
kbl.set(keybinding);
this.contentDisposables.add(attachKeybindingLabelStyler(kbl, this.themeService));
return element;
};

Expand Down
14 changes: 4 additions & 10 deletions src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { attachInputBoxStyler, attachKeybindingLabelStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorWidgetBackground, editorWidgetForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { SearchWidget, SearchOptions } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
import { withNullAsUndefined } from 'vs/base/common/types';
import { Promises, timeout } from 'vs/base/common/async';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { getKeybindingLabelStyles } from 'vs/platform/theme/browser/defaultStyles';

export interface KeybindingsSearchOptions extends SearchOptions {
recordEnter?: boolean;
Expand Down Expand Up @@ -168,9 +169,6 @@ export class DefineKeybindingWidget extends Widget {
private _onShowExistingKeybindings = this._register(new Emitter<string | null>());
readonly onShowExistingKeybidings: Event<string | null> = this._onShowExistingKeybindings.event;

private disposables = this._register(new DisposableStore());
private keybindingLabelStylers = this.disposables.add(new DisposableStore());

constructor(
parent: HTMLElement | null,
@IInstantiationService private readonly instantiationService: IInstantiationService,
Expand Down Expand Up @@ -279,17 +277,13 @@ export class DefineKeybindingWidget extends Widget {
dom.clearNode(this._outputNode);
dom.clearNode(this._showExistingKeybindingsNode);

this.keybindingLabelStylers.clear();

const firstLabel = new KeybindingLabel(this._outputNode, OS);
const firstLabel = new KeybindingLabel(this._outputNode, OS, getKeybindingLabelStyles());
firstLabel.set(withNullAsUndefined(this._firstPart));
this.keybindingLabelStylers.add(attachKeybindingLabelStyler(firstLabel, this.themeService));

if (this._chordPart) {
this._outputNode.appendChild(document.createTextNode(nls.localize('defineKeybinding.chordsTo', "chord to")));
const chordLabel = new KeybindingLabel(this._outputNode, OS);
const chordLabel = new KeybindingLabel(this._outputNode, OS, getKeybindingLabelStyles());
chordLabel.set(this._chordPart);
this.keybindingLabelStylers.add(attachKeybindingLabelStyler(chordLabel, this.themeService));
}

const label = this.getUserSettingsLabel();
Expand Down
Loading

0 comments on commit 2e47c97

Please sign in to comment.