Skip to content

Commit

Permalink
[theming] #4831: color contribution point
Browse files Browse the repository at this point in the history
- Theia extensions can register new colors
- All colors are translated into css variables with `.` replaced by `-` and `--theia` prefix
- Theia extensions can use registered colors via css or programatically from `ColorRegistrsy.getCurrentColor`

Signed-off-by: Anton Kosyakov <[email protected]>
  • Loading branch information
akosyakov committed Nov 1, 2019
1 parent 52131b4 commit 2f86eed
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 2 deletions.
75 changes: 75 additions & 0 deletions packages/core/src/browser/color-application-contribution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/********************************************************************************
* Copyright (C) 2019 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, inject, named } from 'inversify';
import { ColorRegistry } from './color-registry';
import { Emitter } from '../common/event';
import { ThemeService } from './theming';
import { FrontendApplicationContribution } from './frontend-application';
import { ContributionProvider } from '../common/contribution-provider';
import { Disposable, DisposableCollection } from '../common/disposable';

export const ColorContribution = Symbol('ColorContribution');
export interface ColorContribution {
registerColors(colors: ColorRegistry): void;
}

@injectable()
export class ColorApplicationContribution implements FrontendApplicationContribution {

protected readonly onDidChangeEmitter = new Emitter<void>();
readonly onDidChange = this.onDidChangeEmitter.event;

@inject(ColorRegistry)
protected readonly colors: ColorRegistry;

@inject(ContributionProvider) @named(ColorContribution)
protected readonly colorContributions: ContributionProvider<ColorContribution>;

onStart(): void {
for (const contribution of this.colorContributions.getContributions()) {
contribution.registerColors(this.colors);
}

this.update();
ThemeService.get().onThemeChange(() => this.update());
}

protected readonly toUpdate = new DisposableCollection();
protected update(): void {
if (!document) {
return;
}
this.toUpdate.dispose();
const theme = 'theia-' + ThemeService.get().getCurrentTheme().type;
document.body.classList.add(theme);
this.toUpdate.push(Disposable.create(() => document.body.classList.remove(theme)));

const documentElement = document.documentElement;
if (documentElement) {
for (const id of this.colors.getColors()) {
const color = this.colors.getCurrentColor(id);
if (color) {
const propertyName = `--theia-${id.replace('.', '-')}`;
documentElement.style.setProperty(propertyName, color);
this.toUpdate.push(Disposable.create(() => documentElement.style.removeProperty(propertyName)));
}
}
}
this.onDidChangeEmitter.fire(undefined);
}

}
16 changes: 16 additions & 0 deletions packages/core/src/browser/color-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@
********************************************************************************/

import { injectable } from 'inversify';
import { Disposable } from '../common/disposable';

export interface ColorDefaults {
light?: string
dark?: string
hc?: string
}

export interface ColorOptions {
defaults?: ColorDefaults
description: string
}

/**
* It should be implemented by an extension, e.g. by the monaco extension.
Expand All @@ -28,4 +40,8 @@ export class ColorRegistry {
return undefined;
}

register(id: string, options: ColorOptions): Disposable {
return Disposable.NULL;
}

}
5 changes: 5 additions & 0 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,19 @@ import { TabBarDecoratorService, TabBarDecorator } from './shell/tab-bar-decorat
import { ContextMenuContext } from './menu/context-menu-context';
import { bindResourceProvider, bindMessageService, bindPreferenceService } from './frontend-application-bindings';
import { ColorRegistry } from './color-registry';
import { ColorContribution, ColorApplicationContribution } from './color-application-contribution';

export { bindResourceProvider, bindMessageService, bindPreferenceService };

export const frontendApplicationModule = new ContainerModule((bind, unbind, isBound, rebind) => {
const themeService = ThemeService.get();
themeService.register(...BuiltinThemeProvider.themes);
themeService.startupTheme();

bind(ColorRegistry).toSelf().inSingletonScope();
bindContributionProvider(bind, ColorContribution);
bind(ColorApplicationContribution).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(ColorApplicationContribution);

bind(FrontendApplication).toSelf().inSingletonScope();
bind(FrontendApplicationStateService).toSelf().inSingletonScope();
Expand Down
8 changes: 7 additions & 1 deletion packages/monaco/src/browser/monaco-color-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
********************************************************************************/

import { injectable } from 'inversify';
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
import { ColorRegistry, ColorOptions } from '@theia/core/lib/browser/color-registry';
import { Disposable } from '@theia/core/lib/common/disposable';

@injectable()
export class MonacoColorRegistry implements ColorRegistry {
Expand All @@ -34,4 +35,9 @@ export class MonacoColorRegistry implements ColorRegistry {
return color && color.toString();
}

register(id: string, options: ColorOptions): Disposable {
const identifier = this.monacoColorRegistry.registerColor(id, options.defaults, options.description);
return Disposable.create(() => this.monacoColorRegistry.deregisterColor(identifier));
}

}
7 changes: 7 additions & 0 deletions packages/monaco/src/typings/monaco/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,8 +537,15 @@ declare module monaco.color {
export interface ColorContribution {
readonly id: string;
}
export interface ColorDefaults {
ligh?: string;
dark?: string;
hc?: string;
}
export interface IColorRegistry {
getColors(): ColorContribution[];
registerColor(id: string, defaults: ColorDefaults | undefined, description: string): string;
deregisterColor(id: string): void;
}
export function getColorRegistry(): IColorRegistry;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Emitter } from '@theia/core/lib/common/event';
import { EditorPreferences, EditorConfiguration } from '@theia/editor/lib/browser/editor-preferences';
import { ThemeService } from '@theia/core/lib/browser/theming';
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
import { ColorApplicationContribution } from '@theia/core/lib/browser/color-application-contribution';

export type WebviewThemeType = 'vscode-light' | 'vscode-dark' | 'vscode-high-contrast';
export interface WebviewThemeData {
Expand All @@ -43,6 +44,9 @@ export class WebviewThemeDataProvider {
@inject(ColorRegistry)
protected readonly colorRegistry: ColorRegistry;

@inject(ColorApplicationContribution)
protected readonly colorContribution: ColorApplicationContribution;

protected themeData: WebviewThemeData | undefined;

protected readonly editorStyles = new Map<keyof EditorConfiguration>([
Expand All @@ -53,7 +57,7 @@ export class WebviewThemeDataProvider {

@postConstruct()
protected init(): void {
ThemeService.get().onThemeChange(() => this.reset());
this.colorContribution.onDidChange(() => this.reset());

this.editorPreferences.onPreferenceChanged(e => {
if (this.editorStyles.has(e.preferenceName)) {
Expand Down

0 comments on commit 2f86eed

Please sign in to comment.