Allow renderer to be swapped in and out at runtime

Fixes #1589
This commit is contained in:
Daniel Imms
2018-07-29 13:50:45 -07:00
parent 6691f80906
commit 5aaf9ef11b
7 changed files with 62 additions and 12 deletions
+3 -4
View File
@@ -174,9 +174,7 @@ function initOptions(term) {
'termName',
'useFlowControl',
// Complex option
'theme',
// Only in constructor
'rendererType'
'theme'
];
var stringOptions = {
bellSound: null,
@@ -185,7 +183,8 @@ function initOptions(term) {
experimentalCharAtlas: ['none', 'static', 'dynamic'],
fontFamily: null,
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
rendererType: ['dom', 'canvas']
};
var options = Object.keys(term._core.options);
var booleanOptions = [];
+28 -7
View File
@@ -72,7 +72,7 @@ const WRITE_BATCH_SIZE = 300;
/**
* The set of options that only have an effect when set in the Terminal constructor.
*/
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows', 'rendererType'];
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows'];
const DEFAULT_OPTIONS: ITerminalOptions = {
cols: 80,
@@ -370,6 +370,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
if (CONSTRUCTOR_ONLY_OPTIONS.indexOf(key) !== -1) {
console.error(`Option "${key}" can only be set in the constructor`);
}
if (this.options[key] === value) {
return;
}
switch (key) {
case 'bellStyle':
if (!value) {
@@ -396,6 +399,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
console.warn(`${key} cannot be less than 1, value: ${value}`);
return;
}
case 'rendererType':
if (!value) {
value = 'canvas';
}
break;
case 'tabStopWidth':
if (value < 1) {
console.warn(`${key} cannot be less than 1, value: ${value}`);
@@ -455,6 +463,15 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
this.renderer.onResize(this.cols, this.rows);
this.refresh(0, this.rows - 1);
}
case 'rendererType':
if (this.renderer) {
this.unregister(this.renderer);
this.renderer.dispose();
this.renderer = null;
}
this._setupRenderer();
this.renderer.onCharSizeChanged();
break;
case 'scrollback':
this.buffers.resize(this.cols, this.rows);
if (this.viewport) {
@@ -674,12 +691,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
// Performance: Add viewport and helper elements from the fragment
this.element.appendChild(fragment);
switch (this.options.rendererType) {
case 'canvas': this.renderer = new Renderer(this, this.options.theme); break;
case 'dom': this.renderer = new DomRenderer(this, this.options.theme); break;
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
}
this.register(this.renderer);
this._setupRenderer();
this.options.theme = null;
this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this.charMeasure);
this.viewport.onThemeChanged(this.renderer.colorManager.colors);
@@ -736,6 +748,15 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
}
private _setupRenderer(): void {
switch (this.options.rendererType) {
case 'canvas': this.renderer = new Renderer(this, this.options.theme); break;
case 'dom': this.renderer = new DomRenderer(this, this.options.theme); break;
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
}
this.register(this.renderer);
}
/**
* Sets the theme on the renderer. The renderer must have been initialized.
* @param theme The theme to ste.
+12
View File
@@ -30,4 +30,16 @@ export abstract class Disposable implements IDisposable {
public register<T extends IDisposable>(d: T): void {
this._disposables.push(d);
}
/**
* Unregisters a disposable object if it has been registered, if not do
* nothing.
* @param d The disposable to unregister.
*/
public unregister<T extends IDisposable>(d: T): void {
const index = this._disposables.indexOf(d);
if (index !== -1) {
this._disposables.splice(index, 1);
}
}
}
+4
View File
@@ -36,6 +36,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
this._container.appendChild(this._canvas);
}
public dispose(): void {
this._container.removeChild(this._canvas);
}
private _initCanvas(): void {
this._ctx = this._canvas.getContext('2d', {alpha: this._alpha});
// Draw the background if this is an opaque layer
+5
View File
@@ -76,6 +76,11 @@ export class Renderer extends EventEmitter implements IRenderer {
}
}
public dispose(): void {
super.dispose();
this._renderLayers.forEach(l => l.dispose());
}
public onIntersectionChange(entry: IntersectionObserverEntry): void {
this._isPaused = entry.intersectionRatio === 0;
if (!this._isPaused && this._needsFullRefresh) {
+1 -1
View File
@@ -66,7 +66,7 @@ export interface IRenderDimensions {
actualCellHeight: number;
}
export interface IRenderLayer {
export interface IRenderLayer extends IDisposable {
/**
* Called when the terminal loses focus.
*/
+9
View File
@@ -81,6 +81,15 @@ export class DomRenderer extends EventEmitter implements IRenderer {
this._terminal.screenElement.appendChild(this._selectionContainer);
}
public dispose(): void {
this._terminal.element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
this._terminal.screenElement.removeChild(this._rowContainer);
this._terminal.screenElement.removeChild(this._selectionContainer);
this._terminal.screenElement.removeChild(this._themeStyleElement);
this._terminal.screenElement.removeChild(this._dimensionsStyleElement);
super.dispose();
}
private _updateDimensions(): void {
this.dimensions.scaledCharWidth = this._terminal.charMeasure.width * window.devicePixelRatio;
this.dimensions.scaledCharHeight = this._terminal.charMeasure.height * window.devicePixelRatio;