diff --git a/CHANGELOG.md b/CHANGELOG.md index dbaef69b3763b..beab502268e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,20 @@ - [[monaco]](#1.5.0_non_blocking_bulk_edit) `MonacoWorkspace.applyBulkEdit` does not open any editors anymore to avoid blocking [#8329](https://github.com/eclipse-theia/theia/pull/8329) - Consequently, it does not accept editor opener options, and `MonacoWorkspace.openEditors` and `MonacoWorkspace.toTextEditWithEditor` are removed. + +- [[theming]](#1.5.0_declarative_default_themes) Default color and icon themes should be declared in the application package.json. [#8381](https://github.com/eclipse-theia/theia/pull/8381) + + ```json + "theia": { + "frontend": { + "config": { + "defaultTheme": "light", + "defaultIconTheme": "vs-seti" + } + } + }, + ``` + - Consequently, `ThemeService` and `IconThemeService` don't allow to change the default color or icon theme anymore. ## v1.4.0 diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index 0076193e50532..ffe30d72aa255 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -72,6 +72,8 @@ export class FrontendGenerator extends AbstractGenerator { ${this.ifBrowser("require('es6-promise/auto');")} require('reflect-metadata'); const { Container } = require('inversify'); +const { FrontendApplicationConfigProvider } = require('@theia/core/lib/browser/frontend-application-config-provider'); +FrontendApplicationConfigProvider.set(${this.prettyStringify(this.pck.props.frontend.config)}); const { FrontendApplication } = require('@theia/core/lib/browser'); const { frontendApplicationModule } = require('@theia/core/lib/browser/frontend-application-module'); const { messagingFrontendModule } = require('@theia/core/lib/${this.pck.isBrowser() @@ -79,9 +81,6 @@ const { messagingFrontendModule } = require('@theia/core/lib/${this.pck.isBrowse : 'electron-browser/messaging/electron-messaging-frontend-module'}'); const { loggerFrontendModule } = require('@theia/core/lib/browser/logger-frontend-module'); const { ThemeService } = require('@theia/core/lib/browser/theming'); -const { FrontendApplicationConfigProvider } = require('@theia/core/lib/browser/frontend-application-config-provider'); - -FrontendApplicationConfigProvider.set(${this.prettyStringify(this.pck.props.frontend.config)}); const container = new Container(); container.load(frontendApplicationModule); diff --git a/dev-packages/application-package/src/application-props.ts b/dev-packages/application-package/src/application-props.ts index ccb36f36e1935..977eb0f237eb1 100644 --- a/dev-packages/application-package/src/application-props.ts +++ b/dev-packages/application-package/src/application-props.ts @@ -80,7 +80,9 @@ export namespace ApplicationProps { }, frontend: { config: { - applicationName: 'Eclipse Theia' + applicationName: 'Eclipse Theia', + defaultTheme: 'dark', + defaultIconTheme: 'none' } }, generator: { @@ -108,7 +110,12 @@ export interface FrontendApplicationConfig extends ApplicationConfig { /** * The default theme for the application. If not give, defaults to `dark`. If invalid theme is given, also defaults to `dark`. */ - readonly defaultTheme?: string; + readonly defaultTheme: string; + + /** + * The default icon theme for the application. If not give, defaults to `none`. If invalid theme is given, also defaults to `none`. + */ + readonly defaultIconTheme: string; /** * The name of the application. `Eclipse Theia` by default. diff --git a/dev-packages/cli/README.md b/dev-packages/cli/README.md index 865d873d06237..612ca3a8a0597 100644 --- a/dev-packages/cli/README.md +++ b/dev-packages/cli/README.md @@ -18,6 +18,7 @@ - [**Build Target**](#build-target) - [**Application Properties**](#application-properties) - [**Default Preferences**](#default-preferences) + - [**Default Theme**](#default-theme) - [**Using Latest Builds**](#using-latest-builds) - [**Building**](#building) - [**Build**](#build) @@ -94,6 +95,20 @@ For example, an application can update the preference value for `files.enableTra }, ``` +### Default Theme + +Default color and icon themes can be configured in `theia.frontend.config` section: +```json +"theia": { + "frontend": { + "config": { + "defaultTheme": "light", + "defaultIconTheme": "vs-seti" + } + } + }, +``` + ### Build Target The following targets are supported: `browser` and `electron`. By default `browser` target is used. diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 59b91ba310b48..fc16ea689b538 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -361,7 +361,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi } protected updateThemePreference(preferenceName: 'workbench.colorTheme' | 'workbench.iconTheme'): void { - const inspect = this.preferenceService.inspect(preferenceName); + const inspect = this.preferenceService.inspect(preferenceName); const workspaceValue = inspect && inspect.workspaceValue; const userValue = inspect && inspect.globalValue; const value = workspaceValue || userValue; @@ -373,16 +373,15 @@ export class CommonFrontendContribution implements FrontendApplicationContributi } protected updateThemeFromPreference(preferenceName: 'workbench.colorTheme' | 'workbench.iconTheme'): void { - const value = this.preferences[preferenceName]; + const inspect = this.preferenceService.inspect(preferenceName); + const workspaceValue = inspect && inspect.workspaceValue; + const userValue = inspect && inspect.globalValue; + const value = workspaceValue || userValue; if (value !== undefined) { if (preferenceName === 'workbench.colorTheme') { - if (!value) { - this.themeService.reset(); - } else { - this.themeService.setCurrentTheme(value); - } + this.themeService.setCurrentTheme(value || this.themeService.defaultTheme.id); } else { - this.iconThemes.current = value || 'none'; + this.iconThemes.current = value || this.iconThemes.default.id; } } } diff --git a/packages/core/src/browser/connection-status-service.spec.ts b/packages/core/src/browser/connection-status-service.spec.ts index 0cc470e97fbb5..9ecac7d26dbd4 100644 --- a/packages/core/src/browser/connection-status-service.spec.ts +++ b/packages/core/src/browser/connection-status-service.spec.ts @@ -18,6 +18,12 @@ import { enableJSDOM } from '../browser/test/jsdom'; let disableJSDOM = enableJSDOM(); +import { FrontendApplicationConfigProvider } from './frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config +}); + import { expect } from 'chai'; import { ConnectionStatus } from './connection-status-service'; import { MockConnectionStatusService } from './test/mock-connection-status-service'; diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index e4dcd5ae3eeed..8064840136abb 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -17,6 +17,7 @@ import { interfaces } from 'inversify'; import { createPreferenceProxy, PreferenceProxy, PreferenceService, PreferenceContribution, PreferenceSchema } from './preferences'; import { SUPPORTED_ENCODINGS } from './supported-encodings'; +import { FrontendApplicationConfigProvider } from './frontend-application-config-provider'; export const corePreferenceSchema: PreferenceSchema = { 'type': 'object', @@ -53,10 +54,12 @@ export const corePreferenceSchema: PreferenceSchema = { }, 'workbench.colorTheme': { type: 'string', + default: FrontendApplicationConfigProvider.get().defaultTheme, description: 'Specifies the color theme used in the workbench.' }, 'workbench.iconTheme': { type: ['string', 'null'], + default: FrontendApplicationConfigProvider.get().defaultIconTheme, description: "Specifies the icon theme used in the workbench or 'null' to not show any file icons." }, 'workbench.silentNotifications': { @@ -87,8 +90,8 @@ export interface CoreConfiguration { 'workbench.list.openMode': 'singleClick' | 'doubleClick'; 'workbench.commandPalette.history': number; 'workbench.editor.highlightModifiedTabs': boolean; - 'workbench.colorTheme'?: string; - 'workbench.iconTheme'?: string | null; + 'workbench.colorTheme': string; + 'workbench.iconTheme': string | null; 'workbench.silentNotifications': boolean; 'files.encoding': string 'workbench.tree.renderIndentGuides': 'onHover' | 'none' | 'always'; diff --git a/packages/core/src/browser/icon-theme-contribution.ts b/packages/core/src/browser/icon-theme-contribution.ts index 6c9cd13653819..a82fe083d3288 100644 --- a/packages/core/src/browser/icon-theme-contribution.ts +++ b/packages/core/src/browser/icon-theme-contribution.ts @@ -53,7 +53,6 @@ export class DefaultFileIconThemeContribution implements IconTheme, IconThemeCon registerIconThemes(iconThemes: IconThemeService): MaybePromise { iconThemes.register(this); - iconThemes.default = this.id; } /* rely on behaviour before for backward-compatibility */ diff --git a/packages/core/src/browser/icon-theme-service.ts b/packages/core/src/browser/icon-theme-service.ts index fb82f1787f273..b46ae09074782 100644 --- a/packages/core/src/browser/icon-theme-service.ts +++ b/packages/core/src/browser/icon-theme-service.ts @@ -18,6 +18,7 @@ import { injectable, inject, postConstruct } from 'inversify'; import { Emitter } from '../common/event'; import { Disposable, DisposableCollection } from '../common/disposable'; import { LabelProviderContribution, DidChangeLabelEvent } from './label-provider'; +import { FrontendApplicationConfigProvider } from './frontend-application-config-provider'; export interface IconThemeDefinition { readonly id: string @@ -94,13 +95,10 @@ export class IconThemeService { protected readonly onDidChangeCurrentEmitter = new Emitter(); readonly onDidChangeCurrent = this.onDidChangeCurrentEmitter.event; - protected _default: IconTheme; - protected readonly toDeactivate = new DisposableCollection(); @postConstruct() protected init(): void { - this._default = this.noneIconTheme; this.register(this.noneIconTheme); } @@ -124,12 +122,9 @@ export class IconThemeService { return undefined; } this._iconThemes.delete(id); - if (this._default === iconTheme) { - this._default = this.noneIconTheme; - } if (window.localStorage.getItem('iconTheme') === id) { window.localStorage.removeItem('iconTheme'); - this.onDidChangeCurrentEmitter.fire(this._default.id); + this.onDidChangeCurrentEmitter.fire(this.default.id); } this.onDidChangeEmitter.fire(undefined); return iconTheme; @@ -140,7 +135,7 @@ export class IconThemeService { } set current(id: string) { - const newCurrent = this._iconThemes.get(id) || this._default; + const newCurrent = this._iconThemes.get(id) || this.default; if (this.getCurrent().id !== newCurrent.id) { this.setCurrent(newCurrent); } @@ -148,7 +143,7 @@ export class IconThemeService { protected getCurrent(): IconTheme { const id = window.localStorage.getItem('iconTheme'); - return id && this._iconThemes.get(id) || this._default; + return id && this._iconThemes.get(id) || this.default; } protected setCurrent(current: IconTheme): void { @@ -158,21 +153,9 @@ export class IconThemeService { this.onDidChangeCurrentEmitter.fire(current.id); } - get default(): string { - return this._default.id; + get default(): IconTheme { + return this._iconThemes.get(FrontendApplicationConfigProvider.get().defaultIconTheme) || this.noneIconTheme; } - - set default(id: string) { - const newDefault = this._iconThemes.get(id) || this.noneIconTheme; - if (this._default.id === newDefault.id) { - return; - } - this._default = newDefault; - if (!window.localStorage.getItem('iconTheme')) { - this.onDidChangeCurrentEmitter.fire(newDefault.id); - } - } - protected load(): string | undefined { return window.localStorage.getItem('iconTheme') || undefined; } diff --git a/packages/core/src/browser/preferences/preference-proxy.spec.ts b/packages/core/src/browser/preferences/preference-proxy.spec.ts index 6f9660bd92c6f..c919841a8a225 100644 --- a/packages/core/src/browser/preferences/preference-proxy.spec.ts +++ b/packages/core/src/browser/preferences/preference-proxy.spec.ts @@ -31,6 +31,7 @@ import { PreferenceScope } from './preference-scope'; import { PreferenceProvider } from './preference-provider'; import { FrontendApplicationConfigProvider } from '../frontend-application-config-provider'; import { createPreferenceProxy, PreferenceProxyOptions, PreferenceProxy, PreferenceChangeEvent } from './preference-proxy'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; disableJSDOM(); @@ -56,7 +57,8 @@ describe('Preference Proxy', () => { before(() => { disableJSDOM = enableJSDOM(); FrontendApplicationConfigProvider.set({ - 'applicationName': 'test', + ...ApplicationProps.DEFAULT.frontend.config, + 'applicationName': 'test' }); }); diff --git a/packages/core/src/browser/preferences/preference-service.spec.ts b/packages/core/src/browser/preferences/preference-service.spec.ts index f2bb356311a58..bbc7bed5b00ef 100644 --- a/packages/core/src/browser/preferences/preference-service.spec.ts +++ b/packages/core/src/browser/preferences/preference-service.spec.ts @@ -31,6 +31,7 @@ import { PreferenceScope } from './preference-scope'; import { PreferenceProvider } from './preference-provider'; import { FrontendApplicationConfigProvider } from '../frontend-application-config-provider'; import { createPreferenceProxy, PreferenceChangeEvent } from './preference-proxy'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; disableJSDOM(); @@ -56,6 +57,7 @@ describe('Preference Service', () => { before(() => { disableJSDOM = enableJSDOM(); FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config, 'applicationName': 'test', }); }); diff --git a/packages/core/src/browser/theming.ts b/packages/core/src/browser/theming.ts index 3f58cafc6c7fd..7f7e1f9227da4 100644 --- a/packages/core/src/browser/theming.ts +++ b/packages/core/src/browser/theming.ts @@ -17,6 +17,7 @@ import { Emitter, Event } from '../common/event'; import { Disposable } from '../common/disposable'; import { FrontendApplicationConfigProvider } from './frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; export const ThemeServiceSymbol = Symbol('ThemeService'); @@ -50,10 +51,7 @@ export class ThemeService { return global[ThemeServiceSymbol] || new ThemeService(); } - protected constructor( - protected _defaultTheme: string | undefined = FrontendApplicationConfigProvider.get().defaultTheme, - protected fallbackTheme: string = 'dark' - ) { + protected constructor() { const global = window as any; // eslint-disable-line @typescript-eslint/no-explicit-any global[ThemeServiceSymbol] = this; } @@ -134,7 +132,7 @@ export class ThemeService { * The default theme. If that is not applicable, returns with the fallback theme. */ get defaultTheme(): Theme { - return this.themes[this._defaultTheme || this.fallbackTheme] || this.themes[this.fallbackTheme]; + return this.themes[FrontendApplicationConfigProvider.get().defaultTheme] || this.themes[ApplicationProps.DEFAULT.frontend.config.defaultTheme]; } /** diff --git a/packages/editor-preview/src/browser/editor-preview-factory.spec.ts b/packages/editor-preview/src/browser/editor-preview-factory.spec.ts index 57c37de6912a7..d4ee56496812c 100644 --- a/packages/editor-preview/src/browser/editor-preview-factory.spec.ts +++ b/packages/editor-preview/src/browser/editor-preview-factory.spec.ts @@ -21,6 +21,12 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; const disableJsDom = enableJSDOM(); +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config +}); + import { Container } from 'inversify'; import { WidgetFactory, WidgetManager } from '@theia/core/lib/browser'; import { EditorWidget, EditorManager } from '@theia/editor/lib/browser'; diff --git a/packages/git/src/browser/git-repository-provider.spec.ts b/packages/git/src/browser/git-repository-provider.spec.ts index 7e82c8d356273..39db2e52c2974 100644 --- a/packages/git/src/browser/git-repository-provider.spec.ts +++ b/packages/git/src/browser/git-repository-provider.spec.ts @@ -17,6 +17,12 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; let disableJSDOM = enableJSDOM(); +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config +}); + import { Container } from 'inversify'; import { Git, Repository } from '../common'; import { DugiteGit } from '../node/dugite-git'; diff --git a/packages/markers/src/browser/marker-tree-label-provider.spec.ts b/packages/markers/src/browser/marker-tree-label-provider.spec.ts index 5bb3a5d9efe36..82b349d2d1f65 100644 --- a/packages/markers/src/browser/marker-tree-label-provider.spec.ts +++ b/packages/markers/src/browser/marker-tree-label-provider.spec.ts @@ -18,6 +18,12 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; let disableJSDOM = enableJSDOM(); +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config +}); + import URI from '@theia/core/lib/common/uri'; import { expect } from 'chai'; import { Container } from 'inversify'; diff --git a/packages/navigator/src/browser/navigator-diff.spec.ts b/packages/navigator/src/browser/navigator-diff.spec.ts index 99aa6e561cf58..41d4bda7dd5e9 100644 --- a/packages/navigator/src/browser/navigator-diff.spec.ts +++ b/packages/navigator/src/browser/navigator-diff.spec.ts @@ -17,6 +17,12 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; const disableJSDOM = enableJSDOM(); +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config +}); + import { expect } from 'chai'; import { NavigatorDiff } from './navigator-diff'; import * as path from 'path'; diff --git a/packages/preferences/src/browser/util/preference-tree-generator.spec.ts b/packages/preferences/src/browser/util/preference-tree-generator.spec.ts index 0147abc01a782..f24d6bc4aa82f 100644 --- a/packages/preferences/src/browser/util/preference-tree-generator.spec.ts +++ b/packages/preferences/src/browser/util/preference-tree-generator.spec.ts @@ -19,6 +19,12 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; const disableJSDOM = enableJSDOM(); +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config +}); + import { expect } from 'chai'; import { Container } from 'inversify'; import { PreferenceTreeGenerator } from './preference-tree-generator'; diff --git a/packages/preview/src/browser/markdown/markdown-preview-handler.spec.ts b/packages/preview/src/browser/markdown/markdown-preview-handler.spec.ts index 8605bcb0d4fcf..810d53e7e5122 100644 --- a/packages/preview/src/browser/markdown/markdown-preview-handler.spec.ts +++ b/packages/preview/src/browser/markdown/markdown-preview-handler.spec.ts @@ -18,6 +18,12 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; let disableJSDOM = enableJSDOM(); +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config +}); + import * as chai from 'chai'; import { expect } from 'chai'; import URI from '@theia/core/lib/common/uri'; diff --git a/packages/workspace/src/browser/workspace-frontend-contribution.spec.ts b/packages/workspace/src/browser/workspace-frontend-contribution.spec.ts index 8a136a112211e..d601f4a6ed2c3 100644 --- a/packages/workspace/src/browser/workspace-frontend-contribution.spec.ts +++ b/packages/workspace/src/browser/workspace-frontend-contribution.spec.ts @@ -17,6 +17,12 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; let disableJSDOM = enableJSDOM(); +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { ApplicationProps } from '@theia/application-package/lib/application-props'; +FrontendApplicationConfigProvider.set({ + ...ApplicationProps.DEFAULT.frontend.config +}); + import { expect } from 'chai'; import { OS } from '@theia/core/lib/common/os'; import { OpenFileDialogProps } from '@theia/filesystem/lib/browser/file-dialog';