xterm
Version:
Full xterm terminal, in your browser
88 lines (71 loc) • 2.83 kB
text/typescript
/**
* Copyright (c) 2016 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IOptionsService } from 'common/services/Services';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { ICharSizeService } from 'browser/services/Services';
export class CharSizeService implements ICharSizeService {
serviceBrand: any;
public width: number = 0;
public height: number = 0;
private _measureStrategy: IMeasureStrategy;
public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
private _onCharSizeChange = new EventEmitter<void>();
public get onCharSizeChange(): IEvent<void> { return this._onCharSizeChange.event; }
constructor(
readonly document: Document,
readonly parentElement: HTMLElement,
private readonly _optionsService: IOptionsService
) {
this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService);
}
public measure(): void {
const result = this._measureStrategy.measure();
if (result.width !== this.width || result.height !== this.height) {
this.width = result.width;
this.height = result.height;
this._onCharSizeChange.fire();
}
}
}
interface IMeasureStrategy {
measure(): IReadonlyMeasureResult;
}
interface IReadonlyMeasureResult {
readonly width: number;
readonly height: number;
}
interface IMeasureResult {
width: number;
height: number;
}
// TODO: For supporting browsers we should also provide a CanvasCharDimensionsProvider that uses ctx.measureText
class DomMeasureStrategy implements IMeasureStrategy {
private _result: IMeasureResult = { width: 0, height: 0 };
private _measureElement: HTMLElement;
constructor(
private _document: Document,
private _parentElement: HTMLElement,
private _optionsService: IOptionsService
) {
this._measureElement = this._document.createElement('span');
this._measureElement.classList.add('xterm-char-measure-element');
this._measureElement.textContent = 'W';
this._measureElement.setAttribute('aria-hidden', 'true');
this._parentElement.appendChild(this._measureElement);
}
public measure(): IReadonlyMeasureResult {
this._measureElement.style.fontFamily = this._optionsService.options.fontFamily;
this._measureElement.style.fontSize = `${this._optionsService.options.fontSize}px`;
// Note that this triggers a synchronous layout
const geometry = this._measureElement.getBoundingClientRect();
// If values are 0 then the element is likely currently display:none, in which case we should
// retain the previous value.
if (geometry.width !== 0 && geometry.height !== 0) {
this._result.width = geometry.width;
this._result.height = Math.ceil(geometry.height);
}
return this._result;
}
}