Use theme service in more places

This commit is contained in:
Daniel Imms
2022-10-08 13:55:23 -07:00
parent 80e474c6ad
commit 6e35bcd767
18 changed files with 160 additions and 169 deletions
@@ -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,
+1 -1
View File
@@ -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);
@@ -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 });
}
}
@@ -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<IRequestRedrawEvent>,
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();
}
@@ -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) {
@@ -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];
@@ -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<CharData>();
}
@@ -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
-5
View File
@@ -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).
+1 -2
View File
@@ -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);
+11 -11
View File
@@ -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);
}
+1 -1
View File
@@ -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);
}
/**
+39 -3
View File
@@ -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<ReadonlyColorSet>().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;
}
+23 -27
View File
@@ -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<IRequestRedrawEvent>()).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 {
@@ -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);
});
@@ -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) {
@@ -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;
-1
View File
@@ -61,7 +61,6 @@ export interface IRenderer extends IDisposable {
readonly onRequestRedraw: IEvent<IRequestRedrawEvent>;
dispose(): void;
setColors(colors: ReadonlyColorSet): void;
handleDevicePixelRatioChange(): void;
handleResize(cols: number, rows: number): void;
handleCharSizeChanged(): void;
+1 -9
View File
@@ -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