From c4750eaf779d3d2afbb949ab1548949b47196cf8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 8 Oct 2022 11:42:55 -0700 Subject: [PATCH] Create basic theme service Part of #4184 --- .../src/RectangleRenderer.ts | 4 +- addons/xterm-addon-webgl/src/WebglAddon.ts | 7 +-- addons/xterm-addon-webgl/src/WebglRenderer.ts | 4 +- .../src/renderLayer/BaseRenderLayer.ts | 8 ++-- .../src/renderLayer/CursorRenderLayer.ts | 4 +- .../src/renderLayer/LinkRenderLayer.ts | 4 +- .../src/renderLayer/Types.ts | 4 +- src/browser/Terminal.ts | 48 ++++++++++--------- src/browser/Types.d.ts | 4 +- src/browser/renderer/dom/DomRenderer.ts | 4 +- .../renderer/dom/DomRendererRowFactory.ts | 4 +- .../renderer/shared/CellColorResolver.ts | 4 +- src/browser/renderer/shared/CharAtlasCache.ts | 4 +- src/browser/renderer/shared/CharAtlasUtils.ts | 4 +- src/browser/services/Services.ts | 21 ++++++-- src/browser/services/ThemeService.ts | 46 ++++++++++++++++++ 16 files changed, 120 insertions(+), 54 deletions(-) create mode 100644 src/browser/services/ThemeService.ts diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index ff935d1c..62a61101 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -8,7 +8,7 @@ import { IRenderModel, IWebGLVertexArrayObject, IWebGL2RenderingContext } from ' import { Attributes, BgFlags, FgFlags } from 'common/buffer/Constants'; import { Terminal } from 'xterm'; import { IColor } from 'common/Types'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; import { Disposable, toDisposable } from 'common/Lifecycle'; @@ -84,7 +84,7 @@ export class RectangleRenderer extends Disposable { constructor( private _terminal: Terminal, - private _colors: IColorSet, + private _colors: ReadonlyColorSet, private _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions ) { diff --git a/addons/xterm-addon-webgl/src/WebglAddon.ts b/addons/xterm-addon-webgl/src/WebglAddon.ts index 59dd1e40..f0e0a0e9 100644 --- a/addons/xterm-addon-webgl/src/WebglAddon.ts +++ b/addons/xterm-addon-webgl/src/WebglAddon.ts @@ -5,7 +5,7 @@ import { Terminal, ITerminalAddon, IEvent } from 'xterm'; import { WebglRenderer } from './WebglRenderer'; -import { ICharacterJoinerService, ICoreBrowserService, IRenderService } from 'browser/services/Services'; +import { ICharacterJoinerService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { IColorSet } from 'browser/Types'; import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { isSafari } from 'common/Platform'; @@ -42,8 +42,9 @@ export class WebglAddon extends Disposable implements ITerminalAddon { const coreBrowserService: ICoreBrowserService = core._coreBrowserService; const coreService: ICoreService = core.coreService; const decorationService: IDecorationService = core._decorationService; - const colors: IColorSet = core._colorManager.colors; - this._renderer = this.register(new WebglRenderer(terminal, colors, characterJoinerService, coreBrowserService, coreService, decorationService, this._preserveDrawingBuffer)); + const themeService: IThemeService = core._themeService; + // TODO: Pass in theme service + this._renderer = this.register(new WebglRenderer(terminal, themeService.colors, characterJoinerService, coreBrowserService, coreService, decorationService, this._preserveDrawingBuffer)); this.register(forwardEvent(this._renderer.onContextLoss, this._onContextLoss)); this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas)); renderService.setRenderer(this._renderer); diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index bd906b22..072e11a9 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -9,7 +9,7 @@ import { acquireTextureAtlas, removeTerminalFromCache } from 'browser/renderer/s import { observeDevicePixelDimensions } from 'browser/renderer/shared/DevicePixelObserver'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ITextureAtlas } from 'browser/renderer/shared/Types'; import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/Services'; -import { IColorSet, ITerminal } from 'browser/Types'; +import { IColorSet, ITerminal, ReadonlyColorSet } from 'browser/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; @@ -55,7 +55,7 @@ export class WebglRenderer extends Disposable implements IRenderer { constructor( private _terminal: Terminal, - private _colors: IColorSet, + private _colors: ReadonlyColorSet, private readonly _characterJoinerService: ICharacterJoinerService, private readonly _coreBrowserService: ICoreBrowserService, coreService: ICoreService, diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts index 50f47f6c..d4a0a86c 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -6,7 +6,7 @@ import { IRenderLayer } from './Types'; import { acquireTextureAtlas } from 'browser/renderer/shared/CharAtlasCache'; import { Terminal } from 'xterm'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { TEXT_BASELINE } from 'browser/renderer/shared/Constants'; import { ICoreBrowserService } from 'browser/services/Services'; import { IRenderDimensions, ITextureAtlas } from 'browser/renderer/shared/Types'; @@ -31,7 +31,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer id: string, zIndex: number, private _alpha: boolean, - protected _colors: IColorSet, + protected _colors: ReadonlyColorSet, protected readonly _coreBrowserService: ICoreBrowserService ) { super(); @@ -61,7 +61,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer public handleGridChanged(terminal: Terminal, startRow: number, endRow: number): void {} public handleSelectionChanged(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {} - public setColors(terminal: Terminal, colorSet: IColorSet): void { + public setColors(terminal: Terminal, colorSet: ReadonlyColorSet): void { this._refreshCharAtlas(terminal, colorSet); } @@ -89,7 +89,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer * @param terminal The terminal. * @param colorSet The color set to use for the char atlas. */ - private _refreshCharAtlas(terminal: Terminal, colorSet: IColorSet): void { + private _refreshCharAtlas(terminal: Terminal, colorSet: ReadonlyColorSet): void { if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { return; } diff --git a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts index d685dd94..b1718d75 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/CursorRenderLayer.ts @@ -7,7 +7,7 @@ import { Terminal } from 'xterm'; import { BaseRenderLayer } from './BaseRenderLayer'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { IEventEmitter } from 'common/EventEmitter'; import { ICoreBrowserService } from 'browser/services/Services'; @@ -37,7 +37,7 @@ export class CursorRenderLayer extends BaseRenderLayer { terminal: Terminal, container: HTMLElement, zIndex: number, - colors: IColorSet, + colors: ReadonlyColorSet, private _onRequestRefreshRowsEvent: IEventEmitter, coreBrowserService: ICoreBrowserService, private readonly _coreService: ICoreService diff --git a/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts index 2ce90daf..9c841843 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts @@ -6,7 +6,7 @@ import { Terminal } from 'xterm'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; -import { ITerminal, IColorSet, ILinkifierEvent } from 'browser/Types'; +import { ITerminal, IColorSet, ILinkifierEvent, ReadonlyColorSet } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { ICoreBrowserService } from 'browser/services/Services'; import { is256Color } from 'browser/renderer/shared/CharAtlasUtils'; @@ -18,7 +18,7 @@ export class LinkRenderLayer extends BaseRenderLayer { constructor( container: HTMLElement, zIndex: number, - colors: IColorSet, + colors: ReadonlyColorSet, terminal: ITerminal, coreBrowserService: ICoreBrowserService ) { diff --git a/addons/xterm-addon-webgl/src/renderLayer/Types.ts b/addons/xterm-addon-webgl/src/renderLayer/Types.ts index 08561b44..32013ee3 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/Types.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/Types.ts @@ -4,7 +4,7 @@ */ import { IDisposable, Terminal } from 'xterm'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/shared/Types'; export interface IRenderLayer extends IDisposable { @@ -31,7 +31,7 @@ export interface IRenderLayer extends IDisposable { /** * Called when the theme changes. */ - setColors(terminal: Terminal, colorSet: IColorSet): void; + setColors(terminal: Terminal, colorSet: ReadonlyColorSet): void; /** * Called when the data in the grid has changed (or needs to be rendered diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index b3a5b317..c641a544 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -39,9 +39,8 @@ import { KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseActio import { evaluateKeyboardEvent } from 'common/input/Keyboard'; import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ICoreBrowserService, ICharacterJoinerService } from 'browser/services/Services'; +import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ICoreBrowserService, ICharacterJoinerService, IThemeService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; import { IBuffer } from 'common/buffer/Types'; import { MouseService } from 'browser/services/MouseService'; @@ -57,6 +56,7 @@ import { DecorationService } from 'common/services/DecorationService'; import { IDecorationService } from 'common/services/Services'; import { OscLinkProvider } from 'browser/OscLinkProvider'; import { toDisposable } from 'common/Lifecycle'; +import { ThemeService } from 'browser/services/ThemeService'; // Let it work inside Node.js for automated testing purposes. const document: Document = (typeof window !== 'undefined') ? window.document : null as any; @@ -86,6 +86,7 @@ export class Terminal extends CoreTerminal implements ITerminal { private _coreBrowserService: ICoreBrowserService | undefined; private _mouseService: IMouseService | undefined; private _renderService: IRenderService | undefined; + private _themeService: IThemeService | undefined; private _characterJoinerService: ICharacterJoinerService | undefined; private _selectionService: ISelectionService | undefined; @@ -120,8 +121,6 @@ export class Terminal extends CoreTerminal implements ITerminal { public viewport: IViewport | undefined; private _compositionHelper: ICompositionHelper | undefined; private _accessibilityManager: AccessibilityManager | undefined; - private _colorManager: ColorManager | undefined; - private _theme: ITheme | undefined; private readonly _onCursorMove = this.register(new EventEmitter()); public readonly onCursorMove = this._onCursorMove.event; @@ -199,9 +198,9 @@ export class Terminal extends CoreTerminal implements ITerminal { * while an event from OSC 10|110 | 11|111 | 12|112 always contains a single request. */ private _handleColorEvent(event: IColorEvent): void { - if (!this._colorManager) return; + if (!this._themeService) return; for (const req of event) { - let acc: 'foreground' | 'background' | 'cursor' | 'ansi' | undefined = undefined; + let acc: 'foreground' | 'background' | 'cursor' | 'ansi'; let ident = ''; switch (req.index) { case ColorIndex.FOREGROUND: // OSC 10 | 110 @@ -224,21 +223,26 @@ export class Terminal extends CoreTerminal implements ITerminal { switch (req.type) { case ColorRequestType.REPORT: const channels = color.toColorRGB(acc === 'ansi' - ? this._colorManager.colors.ansi[req.index] - : this._colorManager.colors[acc]); + ? this._themeService.colors.ansi[req.index] + : this._themeService.colors[acc]); this.coreService.triggerDataEvent(`${C0.ESC}]${ident};${toRgbString(channels)}${C1_ESCAPED.ST}`); break; case ColorRequestType.SET: - if (acc === 'ansi') this._colorManager.colors.ansi[req.index] = rgba.toColor(...req.color); - else this._colorManager.colors[acc] = rgba.toColor(...req.color); + if (acc === 'ansi') { + this._themeService.modifyColors(colors => colors.ansi[req.index] = rgba.toColor(...req.color)); + } else { + const narrowedAcc = acc; + this._themeService.modifyColors(colors => colors[narrowedAcc] = rgba.toColor(...req.color)); + } break; case ColorRequestType.RESTORE: - this._colorManager.restoreColor(req.index); + this._themeService.restoreColor(req.index); break; } } - this._renderService?.setColors(this._colorManager.colors); - this.viewport?.handleThemeChange(this._colorManager.colors); + // TODO: Have these listen to theme change + this._renderService?.setColors(this._themeService.colors); + this.viewport?.handleThemeChange(this._themeService.colors); } protected _setup(): void { @@ -498,10 +502,8 @@ export class Terminal extends CoreTerminal implements ITerminal { this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer); this._instantiationService.setService(ICharSizeService, this._charSizeService); - this._theme = this.options.theme || this._theme; - this._colorManager = new ColorManager(); - this.register(this.optionsService.onOptionChange(e => this._colorManager!.handleOptionsChange(e, this.optionsService.rawOptions[e]))); - this._colorManager.setTheme(this._theme); + this._themeService = this._instantiationService.createInstance(ThemeService); + this._instantiationService.setService(IThemeService, this._themeService); this._characterJoinerService = this._instantiationService.createInstance(CharacterJoinerService); this._instantiationService.setService(ICharacterJoinerService, this._characterJoinerService); @@ -533,7 +535,8 @@ export class Terminal extends CoreTerminal implements ITerminal { this._viewportScrollArea, this.element ); - this.viewport.handleThemeChange(this._colorManager.colors); + // TODO: Listen to theme service event + this.viewport.handleThemeChange(this._themeService.colors); this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea())); this.register(this.viewport); @@ -610,7 +613,8 @@ export class Terminal extends CoreTerminal implements ITerminal { } private _createRenderer(): IRenderer { - return this._instantiationService.createInstance(DomRenderer, this._colorManager!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier2); + // TODO: Listen to theme service + return this._instantiationService.createInstance(DomRenderer, this._themeService!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier2); } /** @@ -618,10 +622,8 @@ export class Terminal extends CoreTerminal implements ITerminal { * @param theme The theme to set. */ private _setTheme(theme: ITheme): void { - this._theme = theme; - this._colorManager?.setTheme(theme); - this._renderService?.setColors(this._colorManager!.colors); - this.viewport?.handleThemeChange(this._colorManager!.colors); + // TODO: Listen to theme service events + this.viewport?.handleThemeChange(this._themeService!.colors); } /** diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index 5b475b52..830cbac1 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -126,6 +126,8 @@ export interface IColorSet { contrastCache: IColorContrastCache; } +export type ReadonlyColorSet = Readonly> & { ansi: Readonly['ansi']> }; + export interface IColorContrastCache { clear(): void; setCss(bg: number, fg: number, value: string | null): void; @@ -150,7 +152,7 @@ export interface IViewport extends IDisposable { handleWheel(ev: WheelEvent): boolean; handleTouchStart(ev: TouchEvent): void; handleTouchMove(ev: TouchEvent): boolean; - handleThemeChange(colors: IColorSet): void; + handleThemeChange(colors: ReadonlyColorSet): void; } export interface ILinkifierEvent { diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index a961bbe0..d9d0268b 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -7,7 +7,7 @@ import { IRenderer, IRenderDimensions, IRequestRedrawEvent } from 'browser/rende import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; import { Disposable, toDisposable } from 'common/Lifecycle'; -import { IColorSet, ILinkifierEvent, ILinkifier2 } from 'browser/Types'; +import { IColorSet, ILinkifierEvent, ILinkifier2, ReadonlyColorSet } from 'browser/Types'; import { ICharSizeService, ICoreBrowserService } from 'browser/services/Services'; import { IOptionsService, IBufferService, IInstantiationService } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; @@ -43,7 +43,7 @@ export class DomRenderer extends Disposable implements IRenderer { public readonly onRequestRedraw = this.register(new EventEmitter()).event; constructor( - private _colors: IColorSet, + private _colors: ReadonlyColorSet, private readonly _element: HTMLElement, private readonly _screenElement: HTMLElement, private readonly _viewportElement: HTMLElement, diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 6c9f1845..56b19601 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -9,7 +9,7 @@ import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/ import { CellData } from 'common/buffer/CellData'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { color, rgba } from 'common/Color'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/Services'; import { JoinedCellData } from 'browser/services/CharacterJoinerService'; import { excludeFromContrastRatioDemands } from 'browser/renderer/shared/RendererUtils'; @@ -35,7 +35,7 @@ export class DomRendererRowFactory { constructor( private readonly _document: Document, - private _colors: IColorSet, + private _colors: ReadonlyColorSet, @ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService, @IOptionsService private readonly _optionsService: IOptionsService, @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService, diff --git a/src/browser/renderer/shared/CellColorResolver.ts b/src/browser/renderer/shared/CellColorResolver.ts index b29475b9..eeb0edb6 100644 --- a/src/browser/renderer/shared/CellColorResolver.ts +++ b/src/browser/renderer/shared/CellColorResolver.ts @@ -1,6 +1,6 @@ import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; import { ICoreBrowserService } from 'browser/services/Services'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { Attributes, BgFlags, FgFlags } from 'common/buffer/Constants'; import { IDecorationService } from 'common/services/Services'; import { ICellData } from 'common/Types'; @@ -26,7 +26,7 @@ export class CellColorResolver { constructor( private readonly _terminal: Terminal, - private _colors: IColorSet, + private _colors: ReadonlyColorSet, private readonly _selectionRenderModel: ISelectionRenderModel, private readonly _decorationService: IDecorationService, private readonly _coreBrowserService: ICoreBrowserService diff --git a/src/browser/renderer/shared/CharAtlasCache.ts b/src/browser/renderer/shared/CharAtlasCache.ts index ee88c708..e953dc46 100644 --- a/src/browser/renderer/shared/CharAtlasCache.ts +++ b/src/browser/renderer/shared/CharAtlasCache.ts @@ -5,7 +5,7 @@ import { TextureAtlas } from 'browser/renderer/shared/TextureAtlas'; import { Terminal } from 'xterm'; -import { IColorSet, ITerminal } from 'browser/Types'; +import { ITerminal, ReadonlyColorSet } from 'browser/Types'; import { ICharAtlasConfig, ITextureAtlas } from 'browser/renderer/shared/Types'; import { generateConfig, configEquals } from 'browser/renderer/shared/CharAtlasUtils'; @@ -27,7 +27,7 @@ const charAtlasCache: ITextureAtlasCacheEntry[] = []; */ export function acquireTextureAtlas( terminal: Terminal, - colors: IColorSet, + colors: ReadonlyColorSet, scaledCellWidth: number, scaledCellHeight: number, scaledCharWidth: number, diff --git a/src/browser/renderer/shared/CharAtlasUtils.ts b/src/browser/renderer/shared/CharAtlasUtils.ts index 58c207e8..e443168e 100644 --- a/src/browser/renderer/shared/CharAtlasUtils.ts +++ b/src/browser/renderer/shared/CharAtlasUtils.ts @@ -6,10 +6,10 @@ import { ICharAtlasConfig } from './Types'; import { Attributes } from 'common/buffer/Constants'; import { Terminal } from 'xterm'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { NULL_COLOR } from 'common/Color'; -export function generateConfig(scaledCellWidth: number, scaledCellHeight: number, scaledCharWidth: number, scaledCharHeight: number, terminal: Terminal, colors: IColorSet, devicePixelRatio: number): ICharAtlasConfig { +export function generateConfig(scaledCellWidth: number, scaledCellHeight: number, scaledCharWidth: number, scaledCharHeight: number, terminal: Terminal, colors: ReadonlyColorSet, devicePixelRatio: number): ICharAtlasConfig { // null out some fields that don't matter const clonedColors: IColorSet = { foreground: colors.foreground, diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index bbc439e8..6132c3ea 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -5,10 +5,11 @@ import { IEvent } from 'common/EventEmitter'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; import { createDecorator } from 'common/services/ServiceRegistry'; -import { IDisposable } from 'common/Types'; +import { ColorIndex, IDisposable } from 'common/Types'; +import { ITheme } from 'common/services/Services'; export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { @@ -73,7 +74,7 @@ export interface IRenderService extends IDisposable { resize(cols: number, rows: number): void; hasRenderer(): boolean; setRenderer(renderer: IRenderer): void; - setColors(colors: IColorSet): void; + setColors(colors: ReadonlyColorSet): void; handleDevicePixelRatioChange(): void; handleResize(cols: number, rows: number): void; handleCharSizeChanged(): void; @@ -121,3 +122,17 @@ export interface ICharacterJoinerService { deregister(joinerId: number): boolean; getJoinedCharacters(row: number): [number, number][]; } + +export const IThemeService = createDecorator('ThemeService'); +export interface IThemeService { + serviceBrand: undefined; + + readonly colors: ReadonlyColorSet; + setTheme(theme?: ITheme): void; + restoreColor(slot?: ColorIndex): void; + /** + * Allows external modifying of colors in the theme, this is used instead of {@link colors} to + * prevent accidental writes. + */ + modifyColors(callback: (colors: IColorSet) => void): void; +} diff --git a/src/browser/services/ThemeService.ts b/src/browser/services/ThemeService.ts new file mode 100644 index 00000000..72f2163e --- /dev/null +++ b/src/browser/services/ThemeService.ts @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2022 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { ColorManager } from 'browser/ColorManager'; +import { IThemeService } from 'browser/services/Services'; +import { IColorSet, ReadonlyColorSet } from 'browser/Types'; +import { Disposable } from 'common/Lifecycle'; +import { IOptionsService, ITheme } from 'common/services/Services'; +import { ColorIndex } from 'common/Types'; + +export class ThemeService extends Disposable implements IThemeService { + public serviceBrand: undefined; + + // TODO: Merge color manager into theme service + private _colorManager = new ColorManager(); + + public get colors(): ReadonlyColorSet { return this._colorManager.colors; } + + constructor( + @IOptionsService private readonly _optionsService: IOptionsService + ) { + super(); + this.register(this._optionsService.onOptionChange(key => { + if (key === 'theme') { + this.setTheme(this._optionsService.rawOptions.theme); + } + this._colorManager.handleOptionsChange(key, this._optionsService.rawOptions[key]); + })); + this._colorManager.setTheme(this._optionsService.rawOptions.theme); + } + + public setTheme(theme: ITheme = {}): void { + this._colorManager.setTheme(theme); + } + + public restoreColor(slot?: ColorIndex): void { + this._colorManager.restoreColor(slot); + } + + public modifyColors(callback: (colors: IColorSet) => void): void { + callback(this._colorManager.colors); + // TODO: Fire event + } +}