Skip to content

Commit

Permalink
[wbview] fix #5518: apply theming
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Nov 7, 2019
1 parent f0a78d8 commit 9a0eb26
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 5 deletions.
31 changes: 31 additions & 0 deletions packages/core/src/browser/color-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/********************************************************************************
* 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 } from 'inversify';

/**
* It should be implemented by an extension, e.g. by the monaco extension.
*/
@injectable()
export class ColorRegistry {

*getColors(): IterableIterator<string> { }

getCurrentColor(id: string): string | undefined {
return undefined;
}

}
2 changes: 2 additions & 0 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,15 @@ import { ProgressStatusBarItem } from './progress-status-bar-item';
import { TabBarDecoratorService, TabBarDecorator } from './shell/tab-bar-decorator';
import { ContextMenuContext } from './menu/context-menu-context';
import { bindResourceProvider, bindMessageService, bindPreferenceService } from './frontend-application-bindings';
import { ColorRegistry } from './color-registry';

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();

bind(FrontendApplication).toSelf().inSingletonScope();
bind(FrontendApplicationStateService).toSelf().inSingletonScope();
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/browser/theming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ import { CommonMenus } from './common-frontend-contribution';

export const ThemeServiceSymbol = Symbol('ThemeService');

export type ThemeType = 'light' | 'dark' | 'hc';

export interface Theme {
readonly id: string;
readonly type: ThemeType;
readonly label: string;
readonly description?: string;
readonly editorTheme?: string;
Expand Down Expand Up @@ -197,8 +200,9 @@ export class BuiltinThemeProvider {
static readonly darkCss = require('../../src/browser/style/variables-dark.useable.css');
static readonly lightCss = require('../../src/browser/style/variables-bright.useable.css');

static readonly darkTheme = {
static readonly darkTheme: Theme = {
id: 'dark',
type: 'dark',
label: 'Dark Theme',
description: 'Bright fonts on dark backgrounds.',
editorTheme: 'dark-plus', // loaded in /packages/monaco/src/browser/textmate/monaco-theme-registry.ts
Expand All @@ -210,8 +214,9 @@ export class BuiltinThemeProvider {
}
};

static readonly lightTheme = {
static readonly lightTheme: Theme = {
id: 'light',
type: 'light',
label: 'Light Theme',
description: 'Dark fonts on light backgrounds.',
editorTheme: 'light-plus', // loaded in /packages/monaco/src/browser/textmate/monaco-theme-registry.ts
Expand Down
37 changes: 37 additions & 0 deletions packages/monaco/src/browser/monaco-color-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/********************************************************************************
* 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 } from 'inversify';
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';

@injectable()
export class MonacoColorRegistry implements ColorRegistry {

protected readonly monacoThemeService = monaco.services.StaticServices.standaloneThemeService.get();
protected readonly monacoColorRegistry = monaco.color.getColorRegistry();

*getColors(): IterableIterator<string> {
for (const { id } of this.monacoColorRegistry.getColors()) {
yield id;
}
}

getCurrentColor(id: string): string | undefined {
const color = this.monacoThemeService.getTheme().getColor(id);
return color && color.toString();
}

}
8 changes: 6 additions & 2 deletions packages/monaco/src/browser/monaco-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import '../../src/browser/style/index.css';
import '../../src/browser/style/symbol-sprite.svg';
import '../../src/browser/style/symbol-icons.css';

import debounce = require('lodash.debounce');
import { ContainerModule, decorate, injectable, interfaces } from 'inversify';
import { MenuContribution, CommandContribution } from '@theia/core/lib/common';
import { PreferenceScope } from '@theia/core/lib/common/preferences/preference-scope';
Expand Down Expand Up @@ -58,9 +59,9 @@ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import { MonacoContextKeyService } from './monaco-context-key-service';
import { MonacoMimeService } from './monaco-mime-service';
import { MimeService } from '@theia/core/lib/browser/mime-service';

import debounce = require('lodash.debounce');
import { MonacoEditorServices } from './monaco-editor';
import { MonacoColorRegistry } from './monaco-color-registry';
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';

decorate(injectable(), MonacoToProtocolConverter);
decorate(injectable(), ProtocolToMonacoConverter);
Expand Down Expand Up @@ -130,6 +131,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {

bind(MonacoMimeService).toSelf().inSingletonScope();
rebind(MimeService).toService(MonacoMimeService);

bind(MonacoColorRegistry).toSelf().inSingletonScope();
rebind(ColorRegistry).toService(MonacoColorRegistry);
});

export const MonacoConfigurationService = Symbol('MonacoConfigurationService');
Expand Down
5 changes: 4 additions & 1 deletion packages/monaco/src/browser/monaco-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export function loadMonaco(vsRequire: any): Promise<void> {
'vs/base/parts/quickopen/browser/quickOpenModel',
'vs/base/common/filters',
'vs/platform/theme/common/styler',
'vs/platform/theme/common/colorRegistry',
'vs/base/common/platform',
'vs/editor/common/modes',
'vs/editor/contrib/suggest/suggest',
Expand All @@ -75,7 +76,8 @@ export function loadMonaco(vsRequire: any): Promise<void> {
], (css: any, html: any, commands: any, actions: any,
keybindingsRegistry: any, keybindingResolver: any, resolvedKeybinding: any, keybindingLabels: any,
keyCodes: any, mime: any, editorExtensions: any, simpleServices: any, standaloneServices: any, quickOpenWidget: any, quickOpenModel: any,
filters: any, styler: any, platform: any, modes: any, suggest: any, snippetParser: any,
filters: any, styler: any, colorRegistry: any,
platform: any, modes: any, suggest: any, snippetParser: any,
configuration: any, configurationModels: any,
codeEditorService: any, codeEditorServiceImpl: any,
contextKey: any, contextKeyService: any,
Expand All @@ -88,6 +90,7 @@ export function loadMonaco(vsRequire: any): Promise<void> {
global.monaco.quickOpen = Object.assign({}, quickOpenWidget, quickOpenModel);
global.monaco.filters = filters;
global.monaco.theme = styler;
global.monaco.color = colorRegistry;
global.monaco.platform = platform;
global.monaco.editorExtensions = editorExtensions;
global.monaco.modes = modes;
Expand Down
11 changes: 11 additions & 0 deletions packages/monaco/src/typings/monaco/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ declare module monaco.services {

export interface IStandaloneTheme {
tokenTheme: TokenTheme;
getColor(color: string): Color | undefined;
}

export interface TokenTheme {
Expand Down Expand Up @@ -532,6 +533,16 @@ declare module monaco.theme {
export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeService): monaco.IDisposable;
}

declare module monaco.color {
export interface ColorContribution {
readonly id: string;
}
export interface IColorRegistry {
getColors(): ColorContribution[];
}
export function getColorRegistry(): IColorRegistry;
}

declare module monaco.referenceSearch {

export interface Location {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import { OutputChannelRegistryMainImpl } from './output-channel-registry-main';
import { InPluginFileSystemWatcherManager } from './in-plugin-filesystem-watcher-manager';
import { WebviewWidget, WebviewWidgetIdentifier } from './webview/webview';
import { WebviewEnvironment } from './webview/webview-environment';
import { WebviewThemeDataProvider } from './webview/webview-theme-data-provider';

export default new ContainerModule((bind, unbind, isBound, rebind) => {

Expand Down Expand Up @@ -149,6 +150,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
})).inSingletonScope();

bind(WebviewEnvironment).toSelf().inSingletonScope();
bind(WebviewThemeDataProvider).toSelf().inSingletonScope();
bind(WebviewWidget).toSelf();
bind(WidgetFactory).toDynamicValue(({ container }) => ({
id: WebviewWidget.FACTORY_ID,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/********************************************************************************
* 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
********************************************************************************/
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// copied and modified from https://github.com/microsoft/vscode/blob/ba40bd16433d5a817bfae15f3b4350e18f144af4/src/vs/workbench/contrib/webview/common/themeing.ts

import { inject, postConstruct, injectable } from 'inversify';
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';

export type WebviewThemeType = 'vscode-light' | 'vscode-dark' | 'vscode-high-contrast';
export interface WebviewThemeData {
readonly activeTheme: WebviewThemeType;
readonly styles: { readonly [key: string]: string | number; };
}

@injectable()
export class WebviewThemeDataProvider {

protected readonly onDidChangeThemeDataEmitter = new Emitter<void>();
readonly onDidChangeThemeData = this.onDidChangeThemeDataEmitter.event;

@inject(EditorPreferences)
protected readonly editorPreferences: EditorPreferences;

@inject(ColorRegistry)
protected readonly colorRegistry: ColorRegistry;

protected themeData: WebviewThemeData | undefined;

protected readonly editorStyles = new Map<keyof EditorConfiguration>([
['editor.fontFamily', 'editor-font-family'],
['editor.fontWeight', 'editor-font-weight'],
['editor.fontSize', 'editor-font-size']
]);

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

this.editorPreferences.onPreferenceChanged(e => {
if (this.editorStyles.has(e.preferenceName)) {
this.reset();
}
});
}

protected reset(): void {
if (this.themeData) {
this.themeData = undefined;
this.onDidChangeThemeDataEmitter.fire(undefined);
}
}

getThemeData(): WebviewThemeData {
if (!this.themeData) {
this.themeData = this.computeThemeData();
}
return this.themeData;
}

protected computeThemeData(): WebviewThemeData {
const styles: { [key: string]: string | number; } = {};
// tslint:disable-next-line:no-any
const addStyle = (id: string, rawValue: any) => {
if (rawValue) {
const value = typeof rawValue === 'number' || typeof rawValue === 'string' ? rawValue : String(rawValue);
styles['vscode-' + id.replace('.', '-')] = value;
styles['theia-' + id.replace('.', '-')] = value;
}
};

addStyle('font-family', '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif');
addStyle('font-weight', 'normal');
addStyle('font-size', '13px');
this.editorStyles.forEach((value, key) => addStyle(value, this.editorPreferences[key]));

for (const id of this.colorRegistry.getColors()) {
const color = this.colorRegistry.getCurrentColor(id);
if (color) {
addStyle(id, color.toString());
}
}

const activeTheme = this.getActiveTheme();
return { styles, activeTheme };
}

protected getActiveTheme(): WebviewThemeType {
const theme = ThemeService.get().getCurrentTheme();
switch (theme.type) {
case 'light': return 'vscode-light';
case 'dark': return 'vscode-dark';
default: return 'vscode-high-contrast';
}
}

}
18 changes: 18 additions & 0 deletions packages/plugin-ext/src/main/browser/webview/webview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// copied and modified from https://github.com/microsoft/vscode/blob/ba40bd16433d5a817bfae15f3b4350e18f144af4/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts
// copied and modified from https://github.com/microsoft/vscode/blob/ba40bd16433d5a817bfae15f3b4350e18f144af4/src/vs/workbench/contrib/webview/browser/webviewElement.ts#

import * as mime from 'mime';
import { JSONExt } from '@phosphor/coreutils/lib/json';
Expand All @@ -36,6 +42,7 @@ import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
import { Schemes } from '../../../common/uri-components';
import { PluginSharedStyle } from '../plugin-shared-style';
import { BuiltinThemeProvider } from '@theia/core/lib/browser/theming';
import { WebviewThemeDataProvider } from './webview-theme-data-provider';

// tslint:disable:no-any

Expand Down Expand Up @@ -103,6 +110,9 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget {
@inject(PluginSharedStyle)
protected readonly sharedStyle: PluginSharedStyle;

@inject(WebviewThemeDataProvider)
protected readonly themeDataProvider: WebviewThemeDataProvider;

viewState: WebviewPanelViewState = {
visible: false,
active: false,
Expand Down Expand Up @@ -200,6 +210,9 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget {
// keybinding service because these events do not bubble to the parent window anymore.
this.dispatchKeyDown(data);
}));

this.style();
this.toDispose.push(this.themeDataProvider.onDidChangeThemeData(() => this.style()));
}

protected get externalEndpoint(): string {
Expand Down Expand Up @@ -272,6 +285,11 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget {
this.doUpdateContent();
}

protected style(): void {
const { styles, activeTheme } = this.themeDataProvider.getThemeData();
this.doSend('styles', { styles, activeTheme });
}

protected dispatchKeyDown(event: KeyboardEventInit): void {
// Create a fake KeyboardEvent from the data provided
const emulatedKeyboardEvent = new KeyboardEvent('keydown', event);
Expand Down

0 comments on commit 9a0eb26

Please sign in to comment.