From f4123caafc04799a6b8861a25e22976811c64659 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 16 Mar 2017 23:17:46 +0100 Subject: [PATCH] [theming] Support late themingRegistrations, css participation only through themingRegistry --- src/vs/platform/theme/common/themeService.ts | 15 ++++-- src/vs/workbench/browser/part.ts | 6 +-- src/vs/workbench/common/theme.ts | 10 ++-- .../parts/search/browser/searchViewlet.ts | 20 ++++---- .../themes/electron-browser/themeService.ts | 51 ++++++++++--------- 5 files changed, 56 insertions(+), 46 deletions(-) diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 3f4aaf4ea66..12bc1e8d2e0 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -9,6 +9,7 @@ import { Color } from 'vs/base/common/color'; import { IDisposable } from 'vs/base/common/lifecycle'; import platform = require('vs/platform/platform'); import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; +import Event, { Emitter } from 'vs/base/common/event'; export let IThemeService = createDecorator('themeService'); @@ -49,9 +50,9 @@ export interface IThemeService { getTheme(): ITheme; /** - * Register a theming participant that is invoked on every theme change. + * Register a theming participant that is invoked after every theme change. */ - onThemeChange(participant: IThemingParticipant): IDisposable; + onThemeChange: Event; } @@ -68,18 +69,22 @@ export interface IThemingRegistry { onThemeChange(participant: IThemingParticipant): IDisposable; getThemingParticipants(): IThemingParticipant[]; + + readonly onThemingParticipantAdded: Event; } class ThemingRegistry implements IThemingRegistry { private themingParticipants: IThemingParticipant[] = []; + private onThemingParticipantAddedEmitter: Emitter; constructor() { this.themingParticipants = []; + this.onThemingParticipantAddedEmitter = new Emitter(); } public onThemeChange(participant: IThemingParticipant): IDisposable { this.themingParticipants.push(participant); - + this.onThemingParticipantAddedEmitter.fire(participant); return { dispose: () => { const idx = this.themingParticipants.indexOf(participant); @@ -88,6 +93,10 @@ class ThemingRegistry implements IThemingRegistry { }; } + public get onThemingParticipantAdded(): Event { + return this.onThemingParticipantAddedEmitter.event; + } + public getThemingParticipants(): IThemingParticipant[] { return this.themingParticipants; } diff --git a/src/vs/workbench/browser/part.ts b/src/vs/workbench/browser/part.ts index 73f7e68d58a..b51985315f1 100644 --- a/src/vs/workbench/browser/part.ts +++ b/src/vs/workbench/browser/part.ts @@ -8,7 +8,7 @@ import 'vs/css!./media/part'; import { Dimension, Builder } from 'vs/base/browser/builder'; import { Component } from 'vs/workbench/common/component'; -import { IThemeService, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; export interface IPartOptions { hasTitle?: boolean; @@ -32,11 +32,11 @@ export abstract class Part extends Component { super(id, themeService); } - protected onThemeChange(theme: ITheme, collector: ICssStyleCollector): void { + protected onThemeChange(theme: ITheme): void { // only call if our create() method has been called if (this.parent) { - super.onThemeChange(theme, collector); + super.onThemeChange(theme); } } diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 4ffb709cf15..65f408dc8ee 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -6,7 +6,7 @@ import nls = require('vs/nls'); import { registerColor, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; -import { IThemeService, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { Color, RGBA } from 'vs/base/common/color'; // < --- Tabs --- > @@ -75,20 +75,20 @@ export class Themable extends Disposable { this.theme = themeService.getTheme(); // Hook up to theme changes - this._toUnbind.push(this.themeService.onThemeChange((theme, collector) => this.onThemeChange(theme, collector))); + this._toUnbind.push(this.themeService.onThemeChange(theme => this.onThemeChange(theme))); } protected get toUnbind() { return this._toUnbind; } - protected onThemeChange(theme: ITheme, collector: ICssStyleCollector): void { + protected onThemeChange(theme: ITheme): void { this.theme = theme; - this.updateStyles(theme, collector); + this.updateStyles(theme); } - protected updateStyles(theme: ITheme, collector: ICssStyleCollector): void { + protected updateStyles(theme: ITheme): void { // Subclasses to override } diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index a4ad983f221..2ffa4ff941c 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -58,7 +58,7 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/fileActions'; import * as Constants from 'vs/workbench/parts/search/common/constants'; import { IListService } from 'vs/platform/list/browser/listService'; -import { IThemeService, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ITheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { editorFindMatchHighlight } from 'vs/platform/theme/common/colorRegistry'; export class SearchViewlet extends Viewlet { @@ -142,14 +142,6 @@ export class SearchViewlet extends Viewlet { this.updateGlobalPatternExclusions(configuration); } - protected updateStyles(theme: ITheme, collector: ICssStyleCollector) { - let matchHighlightColor = theme.getColor(editorFindMatchHighlight); - if (matchHighlightColor) { - collector.addRule(`.search-viewlet .findInFileMatch { background-color: ${matchHighlightColor}; }`); - collector.addRule(`.search-viewlet .highlight { background-color: ${matchHighlightColor}; }`); - } - } - public create(parent: Builder): TPromise { super.create(parent); @@ -1372,4 +1364,12 @@ export class SearchViewlet extends Viewlet { super.dispose(); } -} \ No newline at end of file +} + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + let matchHighlightColor = theme.getColor(editorFindMatchHighlight); + if (matchHighlightColor) { + collector.addRule(`.search-viewlet .findInFileMatch { background-color: ${matchHighlightColor}; }`); + collector.addRule(`.search-viewlet .highlight { background-color: ${matchHighlightColor}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/themes/electron-browser/themeService.ts b/src/vs/workbench/services/themes/electron-browser/themeService.ts index 5532a5a80f1..7284354ab65 100644 --- a/src/vs/workbench/services/themes/electron-browser/themeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/themeService.ts @@ -27,7 +27,7 @@ import { IMessageService } from 'vs/platform/message/common/message'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import Severity from 'vs/base/common/severity'; import { ColorThemeData } from './colorThemeData'; -import { ITheme, IThemingParticipant, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; +import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; import { $ } from 'vs/base/browser/builder'; import Event, { Emitter } from 'vs/base/common/event'; @@ -186,7 +186,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private currentIconTheme: IFileIconTheme; private onFileIconThemeChange: Emitter; - private themingParticipants: IThemingParticipant[]; + private themingParticipantChangeListener: IDisposable; private _configurationWriter: ConfigurationWriter; constructor( @@ -202,7 +202,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.container = container; this.knownColorThemes = []; - this.themingParticipants = []; // In order to avoid paint flashing for tokens, because // themes are loaded asynchronously, we need to initialize @@ -277,15 +276,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.onFileIconThemeChange.event; } - public onThemeChange(participant: IThemingParticipant): IDisposable { - this.themingParticipants.push(participant); - - return { - dispose: () => { - const idx = this.themingParticipants.indexOf(participant); - this.themingParticipants.splice(idx, 1); - } - }; + public get onThemeChange(): Event { + return this.onColorThemeChange.event; } private backupSettings(): TPromise { @@ -384,6 +376,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { themeId = validateThemeId(themeId); // migrate theme ids + if (this.themingParticipantChangeListener) { + this.themingParticipantChangeListener.dispose(); + this.themingParticipantChangeListener = null; + } + let onApply = (newTheme: ColorThemeData) => { let newThemeId = newTheme.id; if (this.container) { @@ -393,6 +390,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { $(this.container).addClass(newThemeId); } this.currentColorTheme = newTheme; + this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(p => this.updateDynamicCSSRules(this.currentColorTheme)); this.sendTelemetry(newTheme.id, newTheme.extensionData, 'color'); @@ -408,19 +406,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => { if (themeData) { return themeData.ensureLoaded().then(_ => { - let cssRules = []; - let hasRule = {}; - let ruleCollector = { - addRule: (rule: string) => { - if (!hasRule[rule]) { - cssRules.push(rule); - hasRule[rule] = true; - } - } - }; - this.themingParticipants.forEach(p => p(themeData, ruleCollector)); - themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector)); - _applyRules(cssRules.join('\n'), colorThemeRulesClassName); + this.updateDynamicCSSRules(themeData); return onApply(themeData); }, error => { return TPromise.wrapError(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.path, error.message)); @@ -430,6 +416,21 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } + private updateDynamicCSSRules(themeData: ITheme) { + let cssRules = []; + let hasRule = {}; + let ruleCollector = { + addRule: (rule: string) => { + if (!hasRule[rule]) { + cssRules.push(rule); + hasRule[rule] = true; + } + } + }; + themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector)); + _applyRules(cssRules.join('\n'), colorThemeRulesClassName); + } + private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget): TPromise { if (!types.isUndefinedOrNull(settingsTarget)) { return this.configurationWriter.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); -- GitLab