xterm
[VSoRC/.git] / node_modules / xterm / src / browser / services / CharSizeService.ts
1 /**
2  * Copyright (c) 2016 The xterm.js authors. All rights reserved.
3  * @license MIT
4  */
5
6 import { IOptionsService } from 'common/services/Services';
7 import { IEvent, EventEmitter } from 'common/EventEmitter';
8 import { ICharSizeService } from 'browser/services/Services';
9
10 export class CharSizeService implements ICharSizeService {
11   serviceBrand: any;
12
13   public width: number = 0;
14   public height: number = 0;
15   private _measureStrategy: IMeasureStrategy;
16
17   public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
18
19   private _onCharSizeChange = new EventEmitter<void>();
20   public get onCharSizeChange(): IEvent<void> { return this._onCharSizeChange.event; }
21
22   constructor(
23     readonly document: Document,
24     readonly parentElement: HTMLElement,
25     @IOptionsService private readonly _optionsService: IOptionsService
26   ) {
27     this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService);
28   }
29
30   public measure(): void {
31     const result = this._measureStrategy.measure();
32     if (result.width !== this.width || result.height !== this.height) {
33       this.width = result.width;
34       this.height = result.height;
35       this._onCharSizeChange.fire();
36     }
37   }
38 }
39
40 interface IMeasureStrategy {
41   measure(): IReadonlyMeasureResult;
42 }
43
44 interface IReadonlyMeasureResult {
45   readonly width: number;
46   readonly height: number;
47 }
48
49 interface IMeasureResult {
50   width: number;
51   height: number;
52 }
53
54 // TODO: For supporting browsers we should also provide a CanvasCharDimensionsProvider that uses ctx.measureText
55 class DomMeasureStrategy implements IMeasureStrategy {
56   private _result: IMeasureResult = { width: 0, height: 0 };
57   private _measureElement: HTMLElement;
58
59   constructor(
60     private _document: Document,
61     private _parentElement: HTMLElement,
62     private _optionsService: IOptionsService
63   ) {
64     this._measureElement = this._document.createElement('span');
65     this._measureElement.classList.add('xterm-char-measure-element');
66     this._measureElement.textContent = 'W';
67     this._measureElement.setAttribute('aria-hidden', 'true');
68     this._parentElement.appendChild(this._measureElement);
69   }
70
71   public measure(): IReadonlyMeasureResult {
72     this._measureElement.style.fontFamily = this._optionsService.options.fontFamily;
73     this._measureElement.style.fontSize = `${this._optionsService.options.fontSize}px`;
74
75     // Note that this triggers a synchronous layout
76     const geometry = this._measureElement.getBoundingClientRect();
77
78     // If values are 0 then the element is likely currently display:none, in which case we should
79     // retain the previous value.
80     if (geometry.width !== 0 && geometry.height !== 0) {
81       this._result.width = geometry.width;
82       this._result.height = Math.ceil(geometry.height);
83     }
84
85     return this._result;
86   }
87 }