diff --git a/src/AccessibilityManager.ts b/src/AccessibilityManager.ts index 4351a336..bc1dcdd0 100644 --- a/src/AccessibilityManager.ts +++ b/src/AccessibilityManager.ts @@ -9,6 +9,7 @@ import { isMac } from './common/Platform'; import { RenderDebouncer } from './ui/RenderDebouncer'; import { addDisposableDomListener } from './ui/Lifecycle'; import { Disposable } from './common/Lifecycle'; +import { ScreenDprMonitor } from './ui/ScreenDprMonitor'; const MAX_ROWS_TO_READ = 20; @@ -25,6 +26,7 @@ export class AccessibilityManager extends Disposable { private _liveRegionLineCount: number = 0; private _renderRowsDebouncer: RenderDebouncer; + private _screenDprMonitor: ScreenDprMonitor; private _topBoundaryFocusListener: (e: FocusEvent) => void; private _bottomBoundaryFocusListener: (e: FocusEvent) => void; @@ -58,7 +60,7 @@ export class AccessibilityManager extends Disposable { this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener); this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener); - this._refreshRowsDimensions(); + this.refreshRowsDimensions(); this._accessibilityTreeRoot.appendChild(this._rowContainer); this._renderRowsDebouncer = new RenderDebouncer(this._renderRows.bind(this)); @@ -81,14 +83,13 @@ export class AccessibilityManager extends Disposable { this.register(this._terminal.addDisposableListener('a11y.tab', spaceCount => this._onTab(spaceCount))); this.register(this._terminal.onKey(e => this._onKey(e.key))); this.register(this._terminal.addDisposableListener('blur', () => this._clearLiveRegion())); - // TODO: Maybe renderer should fire an event on terminal when the characters change and that - // should be listened to instead? That would mean that the order of events are always - // guarenteed - this.register(this._terminal.addDisposableListener('dprchange', () => this._refreshRowsDimensions())); - this.register(this._terminal.renderer.onCanvasResize(() => this._refreshRowsDimensions())); + + this._screenDprMonitor = new ScreenDprMonitor(); + this.register(this._screenDprMonitor); + this._screenDprMonitor.setListener(() => this.refreshRowsDimensions()); // This shouldn't be needed on modern browsers but is present in case the - // media query that drives the dprchange event isn't supported - this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions())); + // media query that drives the ScreenDprMonitor isn't supported + this.register(addDisposableDomListener(window, 'resize', () => this.refreshRowsDimensions())); } public dispose(): void { @@ -174,7 +175,7 @@ export class AccessibilityManager extends Disposable { // Add bottom boundary listener this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener); - this._refreshRowsDimensions(); + this.refreshRowsDimensions(); } private _createAccessibilityTreeNode(): HTMLElement { @@ -257,7 +258,7 @@ export class AccessibilityManager extends Disposable { } } - private _refreshRowsDimensions(): void { + public refreshRowsDimensions(): void { if (!this._terminal.renderer.dimensions.actualCellHeight) { return; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 60409859..652e04ca 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -42,7 +42,6 @@ import { MouseHelper } from './MouseHelper'; import { DEFAULT_BELL_SOUND, SoundManager } from './SoundManager'; import { MouseZoneManager } from './MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; -import { ScreenDprMonitor } from './ui/ScreenDprMonitor'; import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; @@ -54,6 +53,7 @@ import { EventEmitter2, IEvent } from './common/EventEmitter2'; import { Attributes, DEFAULT_ATTR_DATA } from './core/buffer/BufferLine'; import { applyWindowsMode } from './WindowsMode'; import { ColorManager } from './ui/ColorManager'; +import { RenderCoordinator } from './renderer/RenderCoordinator'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -203,6 +203,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II private _inputHandler: InputHandler; public soundManager: SoundManager; + private _renderCoordinator: RenderCoordinator; public renderer: IRenderer; public selectionManager: SelectionManager; public linkifier: ILinkifier; @@ -214,7 +215,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II public mouseHelper: MouseHelper; private _accessibilityManager: AccessibilityManager; private _colorManager: ColorManager; - private _screenDprMonitor: ScreenDprMonitor; private _theme: ITheme; private _windowsMode: IDisposable | undefined; @@ -705,10 +705,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this._context = this._parent.ownerDocument.defaultView; this._document = this._parent.ownerDocument; - this._screenDprMonitor = new ScreenDprMonitor(); - this._screenDprMonitor.setListener(() => this.emit('dprchange', window.devicePixelRatio)); - this.register(this._screenDprMonitor); - // Create main element container this.element = this._document.createElement('div'); this.element.dir = 'ltr'; // xterm.css assumes LTR @@ -770,6 +766,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this._colorManager.setTheme(this._theme); this._setupRenderer(); + this._renderCoordinator = new RenderCoordinator(this.renderer, this.rows); + this._renderCoordinator.onRender(e => this._onRender.fire(e)); + this.onResize(e => this._renderCoordinator.resize(e.cols, e.rows)); + this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this.charMeasure); this.viewport.onThemeChange(this._colorManager.colors); this.register(this.viewport); @@ -778,12 +778,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this.register(this.onResize(() => this.renderer.onResize(this.cols, this.rows))); this.register(this.addDisposableListener('blur', () => this.renderer.onBlur())); this.register(this.addDisposableListener('focus', () => this.renderer.onFocus())); - this.register(this.addDisposableListener('dprchange', () => this.renderer.onWindowResize(window.devicePixelRatio))); - // dprchange should handle this case, we need this as well for browsers that don't support the - // matchMedia query. - this.register(addDisposableDomListener(window, 'resize', () => this.renderer.onWindowResize(window.devicePixelRatio))); this.register(this.charMeasure.onCharSizeChanged(() => this.renderer.onCharSizeChanged())); - this.register(this.renderer.onCanvasResize(() => this.viewport.syncScrollArea())); + this.register(this._renderCoordinator.onCanvasResize(() => this.viewport.syncScrollArea())); this.selectionManager = new SelectionManager(this, this.charMeasure); this.register(this.selectionManager.onSelectionChange(() => this._onSelectionChange.fire())); @@ -816,6 +812,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II // Note that this must be done *after* the renderer is created in order to // ensure the correct order of the dprchange event this._accessibilityManager = new AccessibilityManager(this); + this._accessibilityManager.register(this._renderCoordinator.onCanvasResize(() => this._accessibilityManager.refreshRowsDimensions())); } // Measure the character size @@ -839,7 +836,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II case 'dom': this.renderer = new DomRenderer(this, this._colorManager.colors); break; default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } - this.renderer.onRender(e => this._onRender.fire(e)); + // TODO: Setting of renderer should be owned by RenderCoordinator + if (this._renderCoordinator) { + this._renderCoordinator.setRenderer(this.renderer); + } this.register(this.renderer); } @@ -851,7 +851,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this._theme = theme; this._colorManager.setTheme(theme); if (this.renderer) { - this.renderer.onThemeChange(this._colorManager.colors); + this.renderer.setColors(this._colorManager.colors); } if (this.viewport) { this.viewport.onThemeChange(this._colorManager.colors); @@ -1207,8 +1207,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II * @param end The row to end at (between start and this.rows - 1). */ public refresh(start: number, end: number): void { - if (this.renderer) { - this.renderer.refreshRows(start, end); + if (this._renderCoordinator) { + this._renderCoordinator.refreshRows(start, end); } } diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index d3ed93a3..f83e7d22 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -384,7 +384,7 @@ export class MockRenderer implements IRenderer { throw new Error('Method not implemented.'); } dimensions: IRenderDimensions; - onThemeChange(colors: IColorSet): void { + setColors(colors: IColorSet): void { throw new Error('Method not implemented.'); } onResize(cols: number, rows: number): void {} @@ -394,9 +394,9 @@ export class MockRenderer implements IRenderer { onSelectionChanged(start: [number, number], end: [number, number]): void {} onCursorMove(): void {} onOptionsChanged(): void {} - onWindowResize(devicePixelRatio: number): void {} + onDevicePixelRatioChange(): void {} clear(): void {} - refreshRows(start: number, end: number): void {} + renderRows(start: number, end: number): void {} registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; } deregisterCharacterJoiner(): boolean { return true; } } diff --git a/src/public/Terminal.api.ts b/src/public/Terminal.api.ts index 2ad5313e..d8325229 100644 --- a/src/public/Terminal.api.ts +++ b/src/public/Terminal.api.ts @@ -104,6 +104,58 @@ describe('API Integration Tests', () => { assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'dom'); }); + describe('renderer', () => { + it('foreground', async function(): Promise { + this.timeout(10000); + await openTerminal({ rendererType: 'dom' }); + await page.evaluate(`window.term.write('\\x1b[30m0\\x1b[31m1\\x1b[32m2\\x1b[33m3\\x1b[34m4\\x1b[35m5\\x1b[36m6\\x1b[37m7')`); + assert.deepEqual(await page.evaluate(` + [ + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(2)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(3)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(4)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(5)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(6)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(7)').className + ] + `), [ + 'xterm-fg-0', + 'xterm-fg-1', + 'xterm-fg-2', + 'xterm-fg-3', + 'xterm-fg-4', + 'xterm-fg-5', + 'xterm-fg-6' + ]); + }); + + it('background', async function(): Promise { + this.timeout(10000); + await openTerminal({ rendererType: 'dom' }); + await page.evaluate(`window.term.write('\\x1b[40m0\\x1b[41m1\\x1b[42m2\\x1b[43m3\\x1b[44m4\\x1b[45m5\\x1b[46m6\\x1b[47m7')`); + assert.deepEqual(await page.evaluate(` + [ + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(1)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(2)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(3)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(4)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(5)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(6)').className, + document.querySelector('.xterm-rows > :nth-child(1) > :nth-child(7)').className + ] + `), [ + 'xterm-bg-0', + 'xterm-bg-1', + 'xterm-bg-2', + 'xterm-bg-3', + 'xterm-bg-4', + 'xterm-bg-5', + 'xterm-bg-6' + ]); + }); + }); + it('selection', async function(): Promise { this.timeout(10000); await openTerminal({ rows: 5, cols: 5 }); diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 3901a860..bae96d72 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -74,7 +74,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {} public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {} - public onThemeChange(terminal: ITerminal, colorSet: IColorSet): void { + public setColors(terminal: ITerminal, colorSet: IColorSet): void { this._refreshCharAtlas(terminal, colorSet); } diff --git a/src/renderer/RenderCoordinator.ts b/src/renderer/RenderCoordinator.ts new file mode 100644 index 00000000..d8895bc4 --- /dev/null +++ b/src/renderer/RenderCoordinator.ts @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IRenderer } from './Types'; +import { RenderDebouncer } from '../ui/RenderDebouncer'; +import { EventEmitter2, IEvent } from '../common/EventEmitter2'; +import { Disposable } from '../common/Lifecycle'; +import { ScreenDprMonitor } from '../../lib/ui/ScreenDprMonitor'; +import { addDisposableDomListener } from '../ui/Lifecycle'; + +export class RenderCoordinator extends Disposable { + private _renderDebouncer: RenderDebouncer; + private _screenDprMonitor: ScreenDprMonitor; + + private _onCanvasResize = new EventEmitter2<{ width: number, height: number }>(); + public get onCanvasResize(): IEvent<{ width: number, height: number }> { return this._onCanvasResize.event; } + private _onRender = new EventEmitter2<{ start: number, end: number }>(); + public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; } + + private _canvasWidth: number = 0; + private _canvasHeight: number = 0; + + constructor( + private _renderer: IRenderer, + private _rowCount: number + ) { + super(); + this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end)); + this.register(this._renderDebouncer); + + this._screenDprMonitor = new ScreenDprMonitor(); + this._screenDprMonitor.setListener(() => this._renderer.onDevicePixelRatioChange()); + this.register(this._screenDprMonitor); + + // dprchange should handle this case, we need this as well for browsers that don't support the + // matchMedia query. + this.register(addDisposableDomListener(window, 'resize', () => this._renderer.onDevicePixelRatioChange())); + } + + public refreshRows(start: number, end: number): void { + this._renderDebouncer.refresh(start, end, this._rowCount); + } + + private _renderRows(start: number, end: number): void { + this._renderer.renderRows(start, end); + this._onRender.fire({ start, end }); + } + + public resize(cols: number, rows: number): void { + this._rowCount = rows; + this._fireOnCanvasResize(); + } + + public changeOptions(): void { + this._renderer.onOptionsChanged(); + this._fireOnCanvasResize(); + } + + private _fireOnCanvasResize(): void { + // Don't fire the event if the dimensions haven't changed + if (this._renderer.dimensions.canvasWidth === this._canvasWidth && this._renderer.dimensions.canvasHeight === this._canvasHeight) { + return; + } + this._canvasWidth = this._renderer.dimensions.canvasWidth; + this._canvasHeight = this._renderer.dimensions.canvasHeight; + this._onCanvasResize.fire({ + width: this._canvasWidth, + height: this._canvasHeight + }); + } + + public setRenderer(renderer: IRenderer): void { + this._renderer = renderer; + } +} diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 527c5966..50b41c46 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -9,30 +9,19 @@ import { CursorRenderLayer } from './CursorRenderLayer'; import { IRenderLayer, IRenderer, IRenderDimensions, ICharacterJoinerRegistry } from './Types'; import { ITerminal, CharacterJoinerHandler } from '../Types'; import { LinkRenderLayer } from './LinkRenderLayer'; -import { RenderDebouncer } from '../ui/RenderDebouncer'; -import { ScreenDprMonitor } from '../ui/ScreenDprMonitor'; import { CharacterJoinerRegistry } from '../renderer/CharacterJoinerRegistry'; -import { EventEmitter2, IEvent } from '../common/EventEmitter2'; import { Disposable } from '../common/Lifecycle'; import { IColorSet } from '../ui/Types'; export class Renderer extends Disposable implements IRenderer { - private _renderDebouncer: RenderDebouncer; - private _renderLayers: IRenderLayer[]; private _devicePixelRatio: number; - private _screenDprMonitor: ScreenDprMonitor; private _isPaused: boolean = false; private _needsFullRefresh: boolean = false; private _characterJoinerRegistry: ICharacterJoinerRegistry; public dimensions: IRenderDimensions; - private _onCanvasResize = new EventEmitter2<{ width: number, height: number }>(); - public get onCanvasResize(): IEvent<{ width: number, height: number }> { return this._onCanvasResize.event; } - private _onRender = new EventEmitter2<{ start: number, end: number }>(); - public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; } - constructor( private _terminal: ITerminal, private _colors: IColorSet @@ -65,11 +54,6 @@ export class Renderer extends Disposable implements IRenderer { this._updateDimensions(); this.onOptionsChanged(); - this._renderDebouncer = new RenderDebouncer(this._renderRows.bind(this)); - this._screenDprMonitor = new ScreenDprMonitor(); - this._screenDprMonitor.setListener(() => this.onWindowResize(window.devicePixelRatio)); - this.register(this._screenDprMonitor); - // Detect whether IntersectionObserver is detected and enable renderer pause // and resume based on terminal visibility if so if ('IntersectionObserver' in window) { @@ -92,19 +76,21 @@ export class Renderer extends Disposable implements IRenderer { } } - public onWindowResize(devicePixelRatio: number): void { + public onDevicePixelRatioChange(): void { // If the device pixel ratio changed, the char atlas needs to be regenerated // and the terminal needs to refreshed - if (this._devicePixelRatio !== devicePixelRatio) { - this._devicePixelRatio = devicePixelRatio; + if (this._devicePixelRatio !== window.devicePixelRatio) { + this._devicePixelRatio = window.devicePixelRatio; this.onResize(this._terminal.cols, this._terminal.rows); } } - public onThemeChange(colors: IColorSet): void { + public setColors(colors: IColorSet): void { + this._colors = colors; + // Clear layers and force a full render this._renderLayers.forEach(l => { - l.onThemeChange(this._terminal, this._colors); + l.setColors(this._terminal, this._colors); l.reset(this._terminal); }); @@ -132,11 +118,6 @@ export class Renderer extends Disposable implements IRenderer { // Resize the screen this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`; this._terminal.screenElement.style.height = `${this.dimensions.canvasHeight}px`; - - this._onCanvasResize.fire({ - width: this.dimensions.canvasWidth, - height: this.dimensions.canvasHeight - }); } public onCharSizeChanged(): void { @@ -175,27 +156,12 @@ export class Renderer extends Disposable implements IRenderer { } } - /** - * Queues a refresh between two rows (inclusive), to be done on next animation - * frame. - * @param start The start row. - * @param end The end row. - */ - public refreshRows(start: number, end: number): void { - if (this._isPaused) { - this._needsFullRefresh = true; - return; - } - this._renderDebouncer.refresh(start, end, this._terminal.rows); - } - /** * Performs the refresh loop callback, calling refresh only if a refresh is * necessary before queueing up the next one. */ - private _renderRows(start: number, end: number): void { + public renderRows(start: number, end: number): void { this._renderLayers.forEach(l => l.onGridChanged(this._terminal, start, end)); - this._onRender.fire({ start, end }); } /** diff --git a/src/renderer/Types.ts b/src/renderer/Types.ts index 590df0e0..c4028d6c 100644 --- a/src/renderer/Types.ts +++ b/src/renderer/Types.ts @@ -5,7 +5,6 @@ import { ITerminal, CharacterJoinerHandler } from '../Types'; import { IDisposable } from 'xterm'; -import { IEvent } from '../common/EventEmitter2'; import { IColorSet } from '../ui/Types'; /** @@ -28,12 +27,9 @@ export const enum FLAGS { export interface IRenderer extends IDisposable { dimensions: IRenderDimensions; - onCanvasResize: IEvent<{ width: number, height: number }>; - onRender: IEvent<{ start: number, end: number }>; - dispose(): void; - onThemeChange(colors: IColorSet): void; - onWindowResize(devicePixelRatio: number): void; + setColors(colors: IColorSet): void; + onDevicePixelRatioChange(): void; onResize(cols: number, rows: number): void; onCharSizeChanged(): void; onBlur(): void; @@ -42,7 +38,7 @@ export interface IRenderer extends IDisposable { onCursorMove(): void; onOptionsChanged(): void; clear(): void; - refreshRows(start: number, end: number): void; + renderRows(start: number, end: number): void; registerCharacterJoiner(handler: CharacterJoinerHandler): number; deregisterCharacterJoiner(joinerId: number): boolean; } @@ -86,7 +82,7 @@ export interface IRenderLayer extends IDisposable { /** * Called when the theme changes. */ - onThemeChange(terminal: ITerminal, colorSet: IColorSet): void; + setColors(terminal: ITerminal, colorSet: IColorSet): void; /** * Called when the data in the grid has changed (or needs to be rendered diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index 6f46f0ec..2797639d 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -5,10 +5,8 @@ import { IRenderer, IRenderDimensions } from '../Types'; import { ILinkifierEvent, ITerminal, CharacterJoinerHandler } from '../../Types'; -import { RenderDebouncer } from '../../ui/RenderDebouncer'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from './DomRendererRowFactory'; import { INVERTED_DEFAULT_COLOR } from '../atlas/Types'; -import { EventEmitter2, IEvent } from '../../common/EventEmitter2'; import { Disposable } from '../../common/Lifecycle'; import { IColorSet } from '../../ui/Types'; @@ -30,7 +28,6 @@ let nextTerminalId = 1; * canvas is not an option. */ export class DomRenderer extends Disposable implements IRenderer { - private _renderDebouncer: RenderDebouncer; private _rowFactory: DomRendererRowFactory; private _terminalClass: number = nextTerminalId++; @@ -42,11 +39,6 @@ export class DomRenderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; - private _onCanvasResize = new EventEmitter2<{ width: number, height: number }>(); - public get onCanvasResize(): IEvent<{ width: number, height: number }> { return this._onCanvasResize.event; } - private _onRender = new EventEmitter2<{ start: number, end: number }>(); - public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; } - constructor( private _terminal: ITerminal, private _colors: IColorSet @@ -78,7 +70,6 @@ export class DomRenderer extends Disposable implements IRenderer { }; this._updateDimensions(); - this._renderDebouncer = new RenderDebouncer(this._renderRows.bind(this)); this._rowFactory = new DomRendererRowFactory(_terminal.options, document); this._terminal.element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass); @@ -140,7 +131,7 @@ export class DomRenderer extends Disposable implements IRenderer { this._terminal.screenElement.style.height = `${this.dimensions.canvasHeight}px`; } - public onThemeChange(colors: IColorSet): void { + public setColors(colors: IColorSet): void { this._colors = colors; this._injectCss(); } @@ -222,7 +213,7 @@ export class DomRenderer extends Disposable implements IRenderer { this._themeStyleElement.innerHTML = styles; } - public onWindowResize(devicePixelRatio: number): void { + public onDevicePixelRatioChange(): void { this._updateDimensions(); } @@ -242,10 +233,6 @@ export class DomRenderer extends Disposable implements IRenderer { public onResize(cols: number, rows: number): void { this._refreshRowElements(cols, rows); this._updateDimensions(); - this._onCanvasResize.fire({ - width: this.dimensions.canvasWidth, - height: this.dimensions.canvasHeight - }); } public onCharSizeChanged(): void { @@ -337,11 +324,7 @@ export class DomRenderer extends Disposable implements IRenderer { this._rowElements.forEach(e => e.innerHTML = ''); } - public refreshRows(start: number, end: number): void { - this._renderDebouncer.refresh(start, end, this._terminal.rows); - } - - private _renderRows(start: number, end: number): void { + public renderRows(start: number, end: number): void { const terminal = this._terminal; const cursorAbsoluteY = terminal.buffer.ybase + terminal.buffer.y; @@ -357,8 +340,6 @@ export class DomRenderer extends Disposable implements IRenderer { const cursorStyle = terminal.options.cursorStyle; rowElement.appendChild(this._rowFactory.createRow(lineData, row === cursorAbsoluteY, cursorStyle, cursorX, cursorBlink, this.dimensions.actualCellWidth, terminal.cols)); } - - this._onRender.fire({ start, end }); } private get _terminalSelector(): string { diff --git a/src/ui/ColorManager.ts b/src/ui/ColorManager.ts index 6d68c58d..923b2823 100644 --- a/src/ui/ColorManager.ts +++ b/src/ui/ColorManager.ts @@ -3,8 +3,7 @@ * @license MIT */ -import { IColorManager, IColor, IColorSet } from './Types'; -import { ITheme } from 'xterm'; +import { IColorManager, IColor, IColorSet, ITheme } from './Types'; const DEFAULT_FOREGROUND = fromHex('#ffffff'); const DEFAULT_BACKGROUND = fromHex('#000000'); @@ -90,7 +89,11 @@ export class ColorManager implements IColorManager { const canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; - this._ctx = canvas.getContext('2d'); + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Could not get rendering context'); + } + this._ctx = ctx; this._ctx.globalCompositeOperation = 'copy'; this._litmusColor = this._ctx.createLinearGradient(0, 0, 1, 1); this.colors = { @@ -133,11 +136,11 @@ export class ColorManager implements IColorManager { } private _parseColor( - css: string, + css: string | undefined, fallback: IColor, allowTransparency: boolean = this.allowTransparency ): IColor { - if (!css) { + if (css === undefined) { return fallback; } diff --git a/src/ui/Types.ts b/src/ui/Types.ts index ef725ba6..b7b0ac21 100644 --- a/src/ui/Types.ts +++ b/src/ui/Types.ts @@ -20,3 +20,27 @@ export interface IColorSet { selection: IColor; ansi: IColor[]; } + +export interface ITheme { + foreground?: string; + background?: string; + cursor?: string; + cursorAccent?: string; + selection?: string; + black?: string; + red?: string; + green?: string; + yellow?: string; + blue?: string; + magenta?: string; + cyan?: string; + white?: string; + brightBlack?: string; + brightRed?: string; + brightGreen?: string; + brightYellow?: string; + brightBlue?: string; + brightMagenta?: string; + brightCyan?: string; + brightWhite?: string; +} diff --git a/src/ui/tsconfig.json b/src/ui/tsconfig.json index b3613eef..e07bd9c3 100644 --- a/src/ui/tsconfig.json +++ b/src/ui/tsconfig.json @@ -10,12 +10,8 @@ "../../node_modules/@types/mocha" ] }, - "include": [ - "./Lifecycle.ts", - "./RenderDebouncer.ts", - "./ScreenDprMonitor.ts" - ], + "include": [ "./**/*" ], "references": [ { "path": "../common" } ] -} \ No newline at end of file +}