From 6e35bcd7676c830422332ff9201a14154773704f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 8 Oct 2022 13:55:23 -0700 Subject: [PATCH] Use theme service in more places --- .../xterm-addon-canvas/src/BaseRenderLayer.ts | 24 ++++----- addons/xterm-addon-canvas/src/CanvasAddon.ts | 2 +- .../xterm-addon-canvas/src/CanvasRenderer.ts | 18 +++---- .../src/CursorRenderLayer.ts | 20 ++++---- .../xterm-addon-canvas/src/LinkRenderLayer.ts | 14 +++--- .../src/SelectionRenderLayer.ts | 12 ++--- .../xterm-addon-canvas/src/TextRenderLayer.ts | 14 +++--- addons/xterm-addon-canvas/src/Types.d.ts | 5 -- addons/xterm-addon-webgl/src/WebglAddon.ts | 3 +- addons/xterm-addon-webgl/src/WebglRenderer.ts | 22 ++++---- src/browser/Terminal.ts | 2 +- src/browser/TestUtils.test.ts | 42 ++++++++++++++-- src/browser/renderer/dom/DomRenderer.ts | 50 +++++++++---------- .../dom/DomRendererRowFactory.test.ts | 29 ++--------- .../renderer/dom/DomRendererRowFactory.ts | 36 +++++++------ .../renderer/shared/CellColorResolver.ts | 25 +++++----- src/browser/renderer/shared/Types.d.ts | 1 - src/browser/services/RenderService.ts | 10 +--- 18 files changed, 160 insertions(+), 169 deletions(-) diff --git a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts index c1e0d6eb..ad1f1c05 100644 --- a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts @@ -10,7 +10,7 @@ import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs'; import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { IRasterizedGlyph, IRenderDimensions, ISelectionRenderModel, ITextureAtlas } from 'browser/renderer/shared/Types'; import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel'; -import { ICoreBrowserService } from 'browser/services/Services'; +import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { CellData } from 'common/buffer/CellData'; import { WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; @@ -46,20 +46,24 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer id: string, zIndex: number, private _alpha: boolean, - protected _colors: ReadonlyColorSet, + protected readonly _themeService: IThemeService, protected readonly _bufferService: IBufferService, protected readonly _optionsService: IOptionsService, protected readonly _decorationService: IDecorationService, protected readonly _coreBrowserService: ICoreBrowserService ) { super(); - this._cellColorResolver = new CellColorResolver(this._terminal, this._colors, this._selectionModel, this._decorationService, this._coreBrowserService); + this._cellColorResolver = new CellColorResolver(this._terminal, this._selectionModel, this._decorationService, this._coreBrowserService, this._themeService); this._canvas = document.createElement('canvas'); this._canvas.classList.add(`xterm-${id}-layer`); this._canvas.style.zIndex = zIndex.toString(); this._initCanvas(); this._container.appendChild(this._canvas); - this._refreshCharAtlas(this._colors); + this._refreshCharAtlas(this._themeService.colors); + this.register(this._themeService.onChangeColors(e => { + this._refreshCharAtlas(e); + this.reset(); + })); this.register(toDisposable(() => { removeElementFromParent(this._canvas); @@ -85,10 +89,6 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer this._selectionModel.update(this._terminal, start, end, columnSelectMode); } - public setColors(colorSet: ReadonlyColorSet): void { - this._refreshCharAtlas(colorSet); - } - protected _setTransparency(alpha: boolean): void { // Do nothing when alpha doesn't change if (alpha === this._alpha) { @@ -104,7 +104,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer this._container.replaceChild(this._canvas, oldCanvas); // Regenerate char atlas and force a full redraw - this._refreshCharAtlas(this._colors); + this._refreshCharAtlas(this._themeService.colors); this.handleGridChanged(0, this._bufferService.rows - 1); } @@ -138,7 +138,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer this._clearAll(); } - this._refreshCharAtlas(this._colors); + this._refreshCharAtlas(this._themeService.colors); } public abstract reset(): void; @@ -294,7 +294,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer if (this._alpha) { this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); } else { - this._ctx.fillStyle = this._colors.background.css; + this._ctx.fillStyle = this._themeService.colors.background.css; this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); } } @@ -314,7 +314,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer width * this._scaledCellWidth, height * this._scaledCellHeight); } else { - this._ctx.fillStyle = this._colors.background.css; + this._ctx.fillStyle = this._themeService.colors.background.css; this._ctx.fillRect( x * this._scaledCellWidth, y * this._scaledCellHeight, diff --git a/addons/xterm-addon-canvas/src/CanvasAddon.ts b/addons/xterm-addon-canvas/src/CanvasAddon.ts index 1edfb3b9..1dc607e5 100644 --- a/addons/xterm-addon-canvas/src/CanvasAddon.ts +++ b/addons/xterm-addon-canvas/src/CanvasAddon.ts @@ -42,7 +42,7 @@ export class CanvasAddon extends Disposable implements ITerminalAddon { const screenElement: HTMLElement = core.screenElement; const linkifier = core.linkifier2; - this._renderer = new CanvasRenderer(terminal, themeService.colors, screenElement, linkifier, bufferService, charSizeService, optionsService, characterJoinerService, coreService, coreBrowserService, decorationService); + this._renderer = new CanvasRenderer(terminal, screenElement, linkifier, bufferService, charSizeService, optionsService, characterJoinerService, coreService, coreBrowserService, decorationService, themeService); this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas)); renderService.setRenderer(this._renderer); renderService.handleResize(bufferService.cols, bufferService.rows); diff --git a/addons/xterm-addon-canvas/src/CanvasRenderer.ts b/addons/xterm-addon-canvas/src/CanvasRenderer.ts index 367db872..63ed4ed8 100644 --- a/addons/xterm-addon-canvas/src/CanvasRenderer.ts +++ b/addons/xterm-addon-canvas/src/CanvasRenderer.ts @@ -6,7 +6,7 @@ import { removeTerminalFromCache } from 'browser/renderer/shared/CharAtlasCache'; import { observeDevicePixelDimensions } from 'browser/renderer/shared/DevicePixelObserver'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; -import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService } from 'browser/services/Services'; +import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { IColorSet, ILinkifier2, ReadonlyColorSet } from 'browser/Types'; import { EventEmitter } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; @@ -31,7 +31,6 @@ export class CanvasRenderer extends Disposable implements IRenderer { constructor( private readonly _terminal: Terminal, - private _colors: ReadonlyColorSet, private readonly _screenElement: HTMLElement, linkifier2: ILinkifier2, private readonly _bufferService: IBufferService, @@ -40,15 +39,16 @@ export class CanvasRenderer extends Disposable implements IRenderer { characterJoinerService: ICharacterJoinerService, coreService: ICoreService, private readonly _coreBrowserService: ICoreBrowserService, - decorationService: IDecorationService + decorationService: IDecorationService, + private readonly _themeService: IThemeService ) { super(); const allowTransparency = this._optionsService.rawOptions.allowTransparency; this._renderLayers = [ - new TextRenderLayer(this._terminal, this._screenElement, 0, this._colors, allowTransparency, this._bufferService, this._optionsService, characterJoinerService, decorationService, this._coreBrowserService), - new SelectionRenderLayer(this._terminal, this._screenElement, 1, this._colors, this._bufferService, this._coreBrowserService, decorationService, this._optionsService), - new LinkRenderLayer(this._terminal, this._screenElement, 2, this._colors, linkifier2, this._bufferService, this._optionsService, decorationService, this._coreBrowserService), - new CursorRenderLayer(this._terminal, this._screenElement, 3, this._colors, this._onRequestRedraw, this._bufferService, this._optionsService, coreService, this._coreBrowserService, decorationService) + new TextRenderLayer(this._terminal, this._screenElement, 0, allowTransparency, this._bufferService, this._optionsService, characterJoinerService, decorationService, this._coreBrowserService, _themeService), + new SelectionRenderLayer(this._terminal, this._screenElement, 1, this._bufferService, this._coreBrowserService, decorationService, this._optionsService, _themeService), + new LinkRenderLayer(this._terminal, this._screenElement, 2, linkifier2, this._bufferService, this._optionsService, decorationService, this._coreBrowserService, _themeService), + new CursorRenderLayer(this._terminal, this._screenElement, 3, this._onRequestRedraw, this._bufferService, this._optionsService, coreService, this._coreBrowserService, decorationService, _themeService) ]; this.dimensions = { scaledCharWidth: 0, @@ -93,10 +93,8 @@ export class CanvasRenderer extends Disposable implements IRenderer { } public setColors(colors: IColorSet): void { - this._colors = colors; // Clear layers and force a full render for (const l of this._renderLayers) { - l.setColors(this._colors); l.reset(); } } @@ -130,7 +128,7 @@ export class CanvasRenderer extends Disposable implements IRenderer { public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void { this._runOperation(l => l.handleSelectionChanged(start, end, columnSelectMode)); // Selection foreground requires a full re-render - if (this._colors.selectionForeground) { + if (this._themeService.colors.selectionForeground) { this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 }); } } diff --git a/addons/xterm-addon-canvas/src/CursorRenderLayer.ts b/addons/xterm-addon-canvas/src/CursorRenderLayer.ts index 1bf7c6ff..ab8b1e66 100644 --- a/addons/xterm-addon-canvas/src/CursorRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/CursorRenderLayer.ts @@ -10,7 +10,7 @@ import { CellData } from 'common/buffer/CellData'; import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { IBufferService, IOptionsService, ICoreService, IDecorationService } from 'common/services/Services'; import { IEventEmitter } from 'common/EventEmitter'; -import { ICoreBrowserService } from 'browser/services/Services'; +import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { Terminal } from 'xterm'; import { toDisposable } from 'common/Lifecycle'; @@ -37,15 +37,15 @@ export class CursorRenderLayer extends BaseRenderLayer { terminal: Terminal, container: HTMLElement, zIndex: number, - colors: ReadonlyColorSet, private readonly _onRequestRedraw: IEventEmitter, bufferService: IBufferService, optionsService: IOptionsService, private readonly _coreService: ICoreService, coreBrowserService: ICoreBrowserService, - decorationService: IDecorationService + decorationService: IDecorationService, + themeService: IThemeService ) { - super(terminal, container, 'cursor', zIndex, true, colors, bufferService, optionsService, decorationService, coreBrowserService); + super(terminal, container, 'cursor', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService); this._state = { x: 0, y: 0, @@ -146,7 +146,7 @@ export class CursorRenderLayer extends BaseRenderLayer { if (!this._coreBrowserService.isFocused) { this._clearCursor(); this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor.css; + this._ctx.fillStyle = this._themeService.colors.cursor.css; const cursorStyle = this._optionsService.rawOptions.cursorStyle; if (cursorStyle && cursorStyle !== 'block') { this._cursorRenderers[cursorStyle](cursorX, viewportRelativeCursorY, this._cell); @@ -211,30 +211,30 @@ export class CursorRenderLayer extends BaseRenderLayer { private _renderBarCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor.css; + this._ctx.fillStyle = this._themeService.colors.cursor.css; this._fillLeftLineAtCell(x, y, this._optionsService.rawOptions.cursorWidth); this._ctx.restore(); } private _renderBlockCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor.css; + this._ctx.fillStyle = this._themeService.colors.cursor.css; this._fillCells(x, y, cell.getWidth(), 1); - this._ctx.fillStyle = this._colors.cursorAccent.css; + this._ctx.fillStyle = this._themeService.colors.cursorAccent.css; this._fillCharTrueColor(cell, x, y); this._ctx.restore(); } private _renderUnderlineCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); - this._ctx.fillStyle = this._colors.cursor.css; + this._ctx.fillStyle = this._themeService.colors.cursor.css; this._fillBottomLineAtCells(x, y); this._ctx.restore(); } private _renderBlurCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); - this._ctx.strokeStyle = this._colors.cursor.css; + this._ctx.strokeStyle = this._themeService.colors.cursor.css; this._strokeRectAtCell(x, y, cell.getWidth(), 1); this._ctx.restore(); } diff --git a/addons/xterm-addon-canvas/src/LinkRenderLayer.ts b/addons/xterm-addon-canvas/src/LinkRenderLayer.ts index 2ddeee75..2d4c217c 100644 --- a/addons/xterm-addon-canvas/src/LinkRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/LinkRenderLayer.ts @@ -6,7 +6,7 @@ import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; -import { ICoreBrowserService } from 'browser/services/Services'; +import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { IColorSet, ILinkifierEvent, ILinkifier2, ReadonlyColorSet } from 'browser/Types'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; import { is256Color } from 'browser/renderer/shared/CharAtlasUtils'; @@ -19,14 +19,14 @@ export class LinkRenderLayer extends BaseRenderLayer { terminal: Terminal, container: HTMLElement, zIndex: number, - colors: ReadonlyColorSet, linkifier2: ILinkifier2, bufferService: IBufferService, optionsService: IOptionsService, decorationService: IDecorationService, - coreBrowserService: ICoreBrowserService + coreBrowserService: ICoreBrowserService, + themeService: IThemeService ) { - super(terminal, container, 'link', zIndex, true, colors, bufferService, optionsService, decorationService, coreBrowserService); + super(terminal, container, 'link', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService); this.register(linkifier2.onShowLinkUnderline(e => this._handleShowLinkUnderline(e))); this.register(linkifier2.onHideLinkUnderline(e => this._handleHideLinkUnderline(e))); @@ -56,12 +56,12 @@ export class LinkRenderLayer extends BaseRenderLayer { private _handleShowLinkUnderline(e: ILinkifierEvent): void { if (e.fg === INVERTED_DEFAULT_COLOR) { - this._ctx.fillStyle = this._colors.background.css; + this._ctx.fillStyle = this._themeService.colors.background.css; } else if (e.fg && is256Color(e.fg)) { // 256 color support - this._ctx.fillStyle = this._colors.ansi[e.fg].css; + this._ctx.fillStyle = this._themeService.colors.ansi[e.fg].css; } else { - this._ctx.fillStyle = this._colors.foreground.css; + this._ctx.fillStyle = this._themeService.colors.foreground.css; } if (e.y1 === e.y2) { diff --git a/addons/xterm-addon-canvas/src/SelectionRenderLayer.ts b/addons/xterm-addon-canvas/src/SelectionRenderLayer.ts index 49b36813..1c96dcc8 100644 --- a/addons/xterm-addon-canvas/src/SelectionRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/SelectionRenderLayer.ts @@ -7,7 +7,7 @@ import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; -import { ICoreBrowserService } from 'browser/services/Services'; +import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { Terminal } from 'xterm'; interface ISelectionState { @@ -24,13 +24,13 @@ export class SelectionRenderLayer extends BaseRenderLayer { terminal: Terminal, container: HTMLElement, zIndex: number, - colors: ReadonlyColorSet, bufferService: IBufferService, coreBrowserService: ICoreBrowserService, decorationService: IDecorationService, - optionsService: IOptionsService + optionsService: IOptionsService, + themeService: IThemeService ) { - super(terminal, container, 'selection', zIndex, true, colors, bufferService, optionsService, decorationService, coreBrowserService); + super(terminal, container, 'selection', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService); this._clearState(); } @@ -102,8 +102,8 @@ export class SelectionRenderLayer extends BaseRenderLayer { } this._ctx.fillStyle = (this._coreBrowserService.isFocused - ? this._colors.selectionBackgroundTransparent - : this._colors.selectionInactiveBackgroundTransparent).css; + ? this._themeService.colors.selectionBackgroundTransparent + : this._themeService.colors.selectionInactiveBackgroundTransparent).css; if (columnSelectMode) { const startCol = start[0]; diff --git a/addons/xterm-addon-canvas/src/TextRenderLayer.ts b/addons/xterm-addon-canvas/src/TextRenderLayer.ts index 0393b30a..ca9eae56 100644 --- a/addons/xterm-addon-canvas/src/TextRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/TextRenderLayer.ts @@ -12,7 +12,7 @@ import { NULL_CELL_CODE, Content, UnderlineStyle } from 'common/buffer/Constants import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { CellData } from 'common/buffer/CellData'; import { IOptionsService, IBufferService, IDecorationService } from 'common/services/Services'; -import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/Services'; +import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { JoinedCellData } from 'browser/services/CharacterJoinerService'; import { color, css } from 'common/Color'; import { Terminal } from 'xterm'; @@ -35,15 +35,15 @@ export class TextRenderLayer extends BaseRenderLayer { terminal: Terminal, container: HTMLElement, zIndex: number, - colors: ReadonlyColorSet, alpha: boolean, bufferService: IBufferService, optionsService: IOptionsService, private readonly _characterJoinerService: ICharacterJoinerService, decorationService: IDecorationService, - coreBrowserService: ICoreBrowserService + coreBrowserService: ICoreBrowserService, + themeService: IThemeService ) { - super(terminal, container, 'text', zIndex, alpha, colors, bufferService, optionsService, decorationService, coreBrowserService); + super(terminal, container, 'text', zIndex, alpha, themeService, bufferService, optionsService, decorationService, coreBrowserService); this._state = new GridCache(); } @@ -168,16 +168,16 @@ export class TextRenderLayer extends BaseRenderLayer { if (cell.isInverse()) { if (cell.isFgDefault()) { - nextFillStyle = this._colors.foreground.css; + nextFillStyle = this._themeService.colors.foreground.css; } else if (cell.isFgRGB()) { nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`; } else { - nextFillStyle = this._colors.ansi[cell.getFgColor()].css; + nextFillStyle = this._themeService.colors.ansi[cell.getFgColor()].css; } } else if (cell.isBgRGB()) { nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`; } else if (cell.isBgPalette()) { - nextFillStyle = this._colors.ansi[cell.getBgColor()].css; + nextFillStyle = this._themeService.colors.ansi[cell.getBgColor()].css; } // Apply dim to the background, this is relatively slow as the CSS is re-parsed but dim is diff --git a/addons/xterm-addon-canvas/src/Types.d.ts b/addons/xterm-addon-canvas/src/Types.d.ts index 4e9373c3..19b1b426 100644 --- a/addons/xterm-addon-canvas/src/Types.d.ts +++ b/addons/xterm-addon-canvas/src/Types.d.ts @@ -79,11 +79,6 @@ export interface IRenderLayer extends IDisposable { */ handleOptionsChanged(): void; - /** - * Called when the theme changes. - */ - setColors(colorSet: ReadonlyColorSet): void; - /** * Called when the data in the grid has changed (or needs to be rendered * again). diff --git a/addons/xterm-addon-webgl/src/WebglAddon.ts b/addons/xterm-addon-webgl/src/WebglAddon.ts index f0e0a0e9..71487315 100644 --- a/addons/xterm-addon-webgl/src/WebglAddon.ts +++ b/addons/xterm-addon-webgl/src/WebglAddon.ts @@ -43,8 +43,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon { const coreService: ICoreService = core.coreService; const decorationService: IDecorationService = core._decorationService; 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._renderer = this.register(new WebglRenderer(terminal, themeService, 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 072e11a9..65361f67 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -8,7 +8,7 @@ import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver'; import { acquireTextureAtlas, removeTerminalFromCache } from 'browser/renderer/shared/CharAtlasCache'; 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 { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { IColorSet, ITerminal, ReadonlyColorSet } from 'browser/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; @@ -55,7 +55,7 @@ export class WebglRenderer extends Disposable implements IRenderer { constructor( private _terminal: Terminal, - private _colors: ReadonlyColorSet, + private readonly _themeService: IThemeService, private readonly _characterJoinerService: ICharacterJoinerService, private readonly _coreBrowserService: ICoreBrowserService, coreService: ICoreService, @@ -64,13 +64,15 @@ export class WebglRenderer extends Disposable implements IRenderer { ) { super(); - this._cellColorResolver = new CellColorResolver(this._terminal, this._colors, this._model.selection, this._decorationService, this._coreBrowserService); + this.register(this._themeService.onChangeColors(e => this._handleColorChange(e))); + + this._cellColorResolver = new CellColorResolver(this._terminal, this._model.selection, this._decorationService, this._coreBrowserService, this._themeService); this._core = (this._terminal as any)._core; this._renderLayers = [ - new LinkRenderLayer(this._core.screenElement!, 2, this._colors, this._core, this._coreBrowserService), - new CursorRenderLayer(_terminal, this._core.screenElement!, 3, this._colors, this._onRequestRedraw, this._coreBrowserService, coreService) + new LinkRenderLayer(this._core.screenElement!, 2, this._themeService.colors, this._core, this._coreBrowserService), + new CursorRenderLayer(_terminal, this._core.screenElement!, 3, this._themeService.colors, this._onRequestRedraw, this._coreBrowserService, coreService) ]; this.dimensions = { scaledCharWidth: 0, @@ -145,15 +147,13 @@ export class WebglRenderer extends Disposable implements IRenderer { return this._charAtlas?.cacheCanvas; } - public setColors(colors: IColorSet): void { - this._colors = colors; + private _handleColorChange(colors: ReadonlyColorSet): void { // Clear layers and force a full render for (const l of this._renderLayers) { - l.setColors(this._terminal, this._colors); + l.setColors(this._terminal, colors); l.reset(this._terminal); } - this._cellColorResolver.setColors(colors); this._rectangleRenderer.setColors(); this._refreshCharAtlas(); @@ -254,7 +254,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._rectangleRenderer?.dispose(); this._glyphRenderer?.dispose(); - this._rectangleRenderer = this.register(new RectangleRenderer(this._terminal, this._colors, this._gl, this.dimensions)); + this._rectangleRenderer = this.register(new RectangleRenderer(this._terminal, this._themeService.colors, this._gl, this.dimensions)); this._glyphRenderer = this.register(new GlyphRenderer(this._terminal, this._gl, this.dimensions)); // Update dimensions and acquire char atlas @@ -273,7 +273,7 @@ export class WebglRenderer extends Disposable implements IRenderer { return; } - const atlas = acquireTextureAtlas(this._terminal, this._colors, this.dimensions.scaledCellWidth, this.dimensions.scaledCellHeight, this.dimensions.scaledCharWidth, this.dimensions.scaledCharHeight, this._coreBrowserService.dpr); + const atlas = acquireTextureAtlas(this._terminal, this._themeService.colors, this.dimensions.scaledCellWidth, this.dimensions.scaledCellHeight, this.dimensions.scaledCharWidth, this.dimensions.scaledCharHeight, this._coreBrowserService.dpr); if (this._charAtlas !== atlas) { this._onChangeTextureAtlas.fire(atlas.cacheCanvas); } diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index d535f9a0..1a409f35 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -605,7 +605,7 @@ export class Terminal extends CoreTerminal implements ITerminal { private _createRenderer(): IRenderer { // TODO: Listen to theme service - return this._instantiationService.createInstance(DomRenderer, this._themeService!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier2); + return this._instantiationService.createInstance(DomRenderer, this.element!, this.screenElement!, this._viewportElement!, this.linkifier2); } /** diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 282d77a7..467c4544 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -5,11 +5,11 @@ import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from 'xterm'; import { IEvent, EventEmitter } from 'common/EventEmitter'; -import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService } from 'browser/services/Services'; +import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; -import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, IColorManager, ICompositionHelper, CharacterJoinerHandler, IBufferRange } from 'browser/Types'; +import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, IColorManager, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet } from 'browser/Types'; import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'; -import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions } from 'common/Types'; +import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from 'common/Types'; import { Buffer } from 'common/buffer/Buffer'; import * as Browser from 'common/Platform'; import { Terminal } from 'browser/Terminal'; @@ -17,6 +17,7 @@ import { IUnicodeService, IOptionsService, ICoreService, ICoreMouseService } fro import { IFunctionIdentifier, IParams } from 'common/parser/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; +import { css } from 'common/Color'; export class TestTerminal extends Terminal { public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; } @@ -505,3 +506,38 @@ export class MockSelectionService implements ISelectionService { return false; } } + +export class MockThemeService implements IThemeService{ + public serviceBrand: undefined; + public onChangeColors = new EventEmitter().event; + public restoreColor(slot?: ColorIndex | undefined): void { + throw new Error('Method not implemented.'); + } + public modifyColors(callback: (colors: IColorSet) => void): void { + throw new Error('Method not implemented.'); + } + public colors: ReadonlyColorSet = { + background: css.toColor('#010101'), + foreground: css.toColor('#020202'), + ansi: [ + // dark: + css.toColor('#2e3436'), + css.toColor('#cc0000'), + css.toColor('#4e9a06'), + css.toColor('#c4a000'), + css.toColor('#3465a4'), + css.toColor('#75507b'), + css.toColor('#06989a'), + css.toColor('#d3d7cf'), + // bright: + css.toColor('#555753'), + css.toColor('#ef2929'), + css.toColor('#8ae234'), + css.toColor('#fce94f'), + css.toColor('#729fcf'), + css.toColor('#ad7fa8'), + css.toColor('#34e2e2'), + css.toColor('#eeeeec') + ] + } as any; +} diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index d9d0268b..5d2729c0 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -8,7 +8,7 @@ import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSO import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { IColorSet, ILinkifierEvent, ILinkifier2, ReadonlyColorSet } from 'browser/Types'; -import { ICharSizeService, ICoreBrowserService } from 'browser/services/Services'; +import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { IOptionsService, IBufferService, IInstantiationService } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { color } from 'common/Color'; @@ -43,7 +43,6 @@ export class DomRenderer extends Disposable implements IRenderer { public readonly onRequestRedraw = this.register(new EventEmitter()).event; constructor( - private _colors: ReadonlyColorSet, private readonly _element: HTMLElement, private readonly _screenElement: HTMLElement, private readonly _viewportElement: HTMLElement, @@ -52,7 +51,8 @@ export class DomRenderer extends Disposable implements IRenderer { @ICharSizeService private readonly _charSizeService: ICharSizeService, @IOptionsService private readonly _optionsService: IOptionsService, @IBufferService private readonly _bufferService: IBufferService, - @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService + @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService, + @IThemeService themeService: IThemeService ) { super(); this._rowContainer = document.createElement('div'); @@ -79,9 +79,11 @@ export class DomRenderer extends Disposable implements IRenderer { actualCellHeight: 0 }; this._updateDimensions(); - this._injectCss(); - this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document, this._colors); + this.register(themeService.onChangeColors(e => this._injectCss(e))); + this._injectCss(themeService.colors); + + this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document); this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass); this._screenElement.appendChild(this._rowContainer); @@ -142,12 +144,7 @@ export class DomRenderer extends Disposable implements IRenderer { this._screenElement.style.height = `${this.dimensions.canvasHeight}px`; } - public setColors(colors: IColorSet): void { - this._colors = colors; - this._injectCss(); - } - - private _injectCss(): void { + private _injectCss(colors: ReadonlyColorSet): void { if (!this._themeStyleElement) { this._themeStyleElement = document.createElement('style'); this._screenElement.appendChild(this._themeStyleElement); @@ -156,7 +153,7 @@ export class DomRenderer extends Disposable implements IRenderer { // Base CSS let styles = `${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` + - ` color: ${this._colors.foreground.css};` + + ` color: ${colors.foreground.css};` + ` font-family: ${this._optionsService.rawOptions.fontFamily};` + ` font-size: ${this._optionsService.rawOptions.fontSize}px;` + `}`; @@ -181,18 +178,18 @@ export class DomRenderer extends Disposable implements IRenderer { styles += `@keyframes blink_block` + `_` + this._terminalClass + ` {` + ` 0% {` + - ` background-color: ${this._colors.cursor.css};` + - ` color: ${this._colors.cursorAccent.css};` + + ` background-color: ${colors.cursor.css};` + + ` color: ${colors.cursorAccent.css};` + ` }` + ` 50% {` + - ` background-color: ${this._colors.cursorAccent.css};` + - ` color: ${this._colors.cursor.css};` + + ` background-color: ${colors.cursorAccent.css};` + + ` color: ${colors.cursor.css};` + ` }` + `}`; // Cursor styles += `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + - ` outline: 1px solid ${this._colors.cursor.css};` + + ` outline: 1px solid ${colors.cursor.css};` + ` outline-offset: -1px;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` + @@ -202,14 +199,14 @@ export class DomRenderer extends Disposable implements IRenderer { ` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + - ` background-color: ${this._colors.cursor.css};` + - ` color: ${this._colors.cursorAccent.css};` + + ` background-color: ${colors.cursor.css};` + + ` color: ${colors.cursorAccent.css};` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` + - ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${this._colors.cursor.css} inset;` + + ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` + - ` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` + + ` box-shadow: 0 -1px 0 ${colors.cursor.css} inset;` + `}`; // Selection styles += @@ -222,21 +219,21 @@ export class DomRenderer extends Disposable implements IRenderer { `}` + `${this._terminalSelector}.focus .${SELECTION_CLASS} div {` + ` position: absolute;` + - ` background-color: ${this._colors.selectionBackgroundOpaque.css};` + + ` background-color: ${colors.selectionBackgroundOpaque.css};` + `}` + `${this._terminalSelector} .${SELECTION_CLASS} div {` + ` position: absolute;` + - ` background-color: ${this._colors.selectionInactiveBackgroundOpaque.css};` + + ` background-color: ${colors.selectionInactiveBackgroundOpaque.css};` + `}`; // Colors - for (const [i, c] of this._colors.ansi.entries()) { + for (const [i, c] of colors.ansi.entries()) { styles += `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`; } styles += - `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).css}; }` + - `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`; + `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` + + `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`; this._themeStyleElement.textContent = styles; } @@ -348,7 +345,6 @@ export class DomRenderer extends Disposable implements IRenderer { public handleOptionsChanged(): void { // Force a refresh this._updateDimensions(); - this._injectCss(); } public clear(): void { diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index b228f3d6..fbb48536 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -12,7 +12,7 @@ import { IBufferLine } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { MockCoreService, MockDecorationService, MockOptionsService } from 'common/TestUtils.test'; import { css } from 'common/Color'; -import { MockCharacterJoinerService, MockCoreBrowserService } from 'browser/TestUtils.test'; +import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from 'browser/TestUtils.test'; describe('DomRendererRowFactory', () => { let dom: jsdom.JSDOM; @@ -23,35 +23,12 @@ describe('DomRendererRowFactory', () => { dom = new jsdom.JSDOM(''); rowFactory = new DomRendererRowFactory( dom.window.document, - { - background: css.toColor('#010101'), - foreground: css.toColor('#020202'), - ansi: [ - // dark: - css.toColor('#2e3436'), - css.toColor('#cc0000'), - css.toColor('#4e9a06'), - css.toColor('#c4a000'), - css.toColor('#3465a4'), - css.toColor('#75507b'), - css.toColor('#06989a'), - css.toColor('#d3d7cf'), - // bright: - css.toColor('#555753'), - css.toColor('#ef2929'), - css.toColor('#8ae234'), - css.toColor('#fce94f'), - css.toColor('#729fcf'), - css.toColor('#ad7fa8'), - css.toColor('#34e2e2'), - css.toColor('#eeeeec') - ] - } as any, new MockCharacterJoinerService(), new MockOptionsService({ drawBoldTextInBrightColors: true }), new MockCoreBrowserService(), new MockCoreService(), - new MockDecorationService() + new MockDecorationService(), + new MockThemeService() ); lineData = createEmptyLineData(2); }); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 56b19601..14b26c92 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -10,7 +10,7 @@ import { CellData } from 'common/buffer/CellData'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { color, rgba } from 'common/Color'; import { IColorSet, ReadonlyColorSet } from 'browser/Types'; -import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/Services'; +import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { JoinedCellData } from 'browser/services/CharacterJoinerService'; import { excludeFromContrastRatioDemands } from 'browser/renderer/shared/RendererUtils'; import { AttributeData } from 'common/buffer/AttributeData'; @@ -35,19 +35,15 @@ export class DomRendererRowFactory { constructor( private readonly _document: Document, - private _colors: ReadonlyColorSet, @ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService, @IOptionsService private readonly _optionsService: IOptionsService, @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService, @ICoreService private readonly _coreService: ICoreService, - @IDecorationService private readonly _decorationService: IDecorationService + @IDecorationService private readonly _decorationService: IDecorationService, + @IThemeService private readonly _themeService: IThemeService ) { } - public setColors(colors: IColorSet): void { - this._colors = colors; - } - public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void { this._selectionStart = start; this._selectionEnd = end; @@ -71,6 +67,8 @@ export class DomRendererRowFactory { } } + const colors = this._themeService.colors; + for (let x = 0; x < lineLength; x++) { lineData.loadCell(x, this._workCell); let width = this._workCell.getWidth(); @@ -176,7 +174,7 @@ export class DomRendererRowFactory { if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8) { fg += 8; } - charElement.style.textDecorationColor = this._colors.ansi[fg].css; + charElement.style.textDecorationColor = colors.ansi[fg].css; } } } @@ -224,17 +222,17 @@ export class DomRendererRowFactory { // Apply selection foreground if applicable const isInSelection = this._isCellInSelection(x, row); if (!isTop) { - if (this._colors.selectionForeground && isInSelection) { + if (colors.selectionForeground && isInSelection) { fgColorMode = Attributes.CM_RGB; - fg = this._colors.selectionForeground.rgba >> 8 & 0xFFFFFF; - fgOverride = this._colors.selectionForeground; + fg = colors.selectionForeground.rgba >> 8 & 0xFFFFFF; + fgOverride = colors.selectionForeground; } } // If in the selection, force the element to be above the selection to improve contrast and // support opaque selections if (isInSelection) { - bgOverride = this._coreBrowserService.isFocused ? this._colors.selectionBackgroundOpaque : this._colors.selectionInactiveBackgroundOpaque; + bgOverride = this._coreBrowserService.isFocused ? colors.selectionBackgroundOpaque : colors.selectionInactiveBackgroundOpaque; isTop = true; } @@ -248,7 +246,7 @@ export class DomRendererRowFactory { switch (bgColorMode) { case Attributes.CM_P16: case Attributes.CM_P256: - resolvedBg = this._colors.ansi[bg]; + resolvedBg = colors.ansi[bg]; charElement.classList.add(`xterm-bg-${bg}`); break; case Attributes.CM_RGB: @@ -258,10 +256,10 @@ export class DomRendererRowFactory { case Attributes.CM_DEFAULT: default: if (isInverse) { - resolvedBg = this._colors.foreground; + resolvedBg = colors.foreground; charElement.classList.add(`xterm-bg-${INVERTED_DEFAULT_COLOR}`); } else { - resolvedBg = this._colors.background; + resolvedBg = colors.background; } } @@ -279,7 +277,7 @@ export class DomRendererRowFactory { if (cell.isBold() && fg < 8 && this._optionsService.rawOptions.drawBoldTextInBrightColors) { fg += 8; } - if (!this._applyMinimumContrast(charElement, resolvedBg, this._colors.ansi[fg], cell, bgOverride, undefined)) { + if (!this._applyMinimumContrast(charElement, resolvedBg, colors.ansi[fg], cell, bgOverride, undefined)) { charElement.classList.add(`xterm-fg-${fg}`); } break; @@ -295,7 +293,7 @@ export class DomRendererRowFactory { break; case Attributes.CM_DEFAULT: default: - if (!this._applyMinimumContrast(charElement, resolvedBg, this._colors.foreground, cell, bgOverride, undefined)) { + if (!this._applyMinimumContrast(charElement, resolvedBg, colors.foreground, cell, bgOverride, undefined)) { if (isInverse) { charElement.classList.add(`xterm-fg-${INVERTED_DEFAULT_COLOR}`); } @@ -317,13 +315,13 @@ export class DomRendererRowFactory { // Try get from cache first, only use the cache when there are no decoration overrides let adjustedColor: IColor | undefined | null = undefined; if (!bgOverride && !fgOverride) { - adjustedColor = this._colors.contrastCache.getColor(bg.rgba, fg.rgba); + adjustedColor = this._themeService.colors.contrastCache.getColor(bg.rgba, fg.rgba); } // Calculate and store in cache if (adjustedColor === undefined) { adjustedColor = color.ensureContrastRatio(bgOverride || bg, fgOverride || fg, this._optionsService.rawOptions.minimumContrastRatio); - this._colors.contrastCache.setColor((bgOverride || bg).rgba, (fgOverride || fg).rgba, adjustedColor ?? null); + this._themeService.colors.contrastCache.setColor((bgOverride || bg).rgba, (fgOverride || fg).rgba, adjustedColor ?? null); } if (adjustedColor) { diff --git a/src/browser/renderer/shared/CellColorResolver.ts b/src/browser/renderer/shared/CellColorResolver.ts index eeb0edb6..ae5019e2 100644 --- a/src/browser/renderer/shared/CellColorResolver.ts +++ b/src/browser/renderer/shared/CellColorResolver.ts @@ -1,5 +1,5 @@ import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; -import { ICoreBrowserService } from 'browser/services/Services'; +import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { Attributes, BgFlags, FgFlags } from 'common/buffer/Constants'; import { IDecorationService } from 'common/services/Services'; @@ -12,6 +12,7 @@ let $bg = 0; let $hasFg = false; let $hasBg = false; let $isSelected = false; +let $colors: ReadonlyColorSet | undefined; export class CellColorResolver { /** @@ -26,17 +27,13 @@ export class CellColorResolver { constructor( private readonly _terminal: Terminal, - private _colors: ReadonlyColorSet, private readonly _selectionRenderModel: ISelectionRenderModel, private readonly _decorationService: IDecorationService, - private readonly _coreBrowserService: ICoreBrowserService + private readonly _coreBrowserService: ICoreBrowserService, + private readonly _themeService: IThemeService ) { } - public setColors(colors: IColorSet): void { - this._colors = colors; - } - /** * Resolves colors for the cell, putting the result into the shared {@link result}. This resolves * overrides, inverse and selection for the cell which can then be used to feed into the renderer. @@ -54,6 +51,7 @@ export class CellColorResolver { $hasBg = false; $hasFg = false; $isSelected = false; + $colors = this._themeService.colors; // Apply decorations on the bottom layer this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => { @@ -70,10 +68,10 @@ export class CellColorResolver { // Apply the selection color if needed $isSelected = this._selectionRenderModel.isCellSelected(this._terminal, x, y); if ($isSelected) { - $bg = (this._coreBrowserService.isFocused ? this._colors.selectionBackgroundOpaque : this._colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF; + $bg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF; $hasBg = true; - if (this._colors.selectionForeground) { - $fg = this._colors.selectionForeground.rgba >> 8 & 0xFFFFFF; + if ($colors.selectionForeground) { + $fg = $colors.selectionForeground.rgba >> 8 & 0xFFFFFF; $hasFg = true; } } @@ -112,7 +110,7 @@ export class CellColorResolver { if ($hasBg && !$hasFg) { // Resolve bg color type (default color has a different meaning in fg vs bg) if ((this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) { - $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | ((this._colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB; + $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | (($colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB; } else { $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this.result.bg & (Attributes.RGB_MASK | Attributes.CM_MASK); } @@ -121,7 +119,7 @@ export class CellColorResolver { if (!$hasBg && $hasFg) { // Resolve bg color type (default color has a different meaning in fg vs bg) if ((this.result.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) { - $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | ((this._colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB; + $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB; } else { $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this.result.fg & (Attributes.RGB_MASK | Attributes.CM_MASK); } @@ -129,6 +127,9 @@ export class CellColorResolver { } } + // Release object + $colors = undefined; + // Use the override if it exists this.result.bg = $hasBg ? $bg : this.result.bg; this.result.fg = $hasFg ? $fg : this.result.fg; diff --git a/src/browser/renderer/shared/Types.d.ts b/src/browser/renderer/shared/Types.d.ts index d5dd378b..1def0f77 100644 --- a/src/browser/renderer/shared/Types.d.ts +++ b/src/browser/renderer/shared/Types.d.ts @@ -61,7 +61,6 @@ export interface IRenderer extends IDisposable { readonly onRequestRedraw: IEvent; dispose(): void; - setColors(colors: ReadonlyColorSet): void; handleDevicePixelRatioChange(): void; handleResize(cols: number, rows: number): void; handleCharSizeChanged(): void; diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 585344f9..dd0c6b50 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -91,7 +91,7 @@ export class RenderService extends Disposable implements IRenderService { // matchMedia query. this.register(addDisposableDomListener(coreBrowserService.window, 'resize', () => this.handleDevicePixelRatioChange())); - this.register(themeService.onChangeColors(e => this._handleChangeColors(e))); + this.register(themeService.onChangeColors(() => this._fullRefresh())); // Detect whether IntersectionObserver is detected and enable renderer pause // and resume based on terminal visibility if so @@ -208,14 +208,6 @@ export class RenderService extends Disposable implements IRenderService { this._fullRefresh(); } - private _handleChangeColors(colors: ReadonlyColorSet): void { - if (!this._renderer) { - return; - } - this._renderer.setColors(colors); - this._fullRefresh(); - } - public handleDevicePixelRatioChange(): void { // Force char size measurement as DomMeasureStrategy(getBoundingClientRect) is not stable // when devicePixelRatio changes