@xterm/xterm 5.4.0-beta.23 → 5.4.0-beta.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xterm/xterm",
3
3
  "description": "Full xterm terminal, in your browser",
4
- "version": "5.4.0-beta.23",
4
+ "version": "5.4.0-beta.25",
5
5
  "main": "lib/xterm.js",
6
6
  "style": "css/xterm.css",
7
7
  "types": "typings/xterm.d.ts",
@@ -8,12 +8,6 @@ import { EventEmitter } from 'common/EventEmitter';
8
8
  import { ICharSizeService } from 'browser/services/Services';
9
9
  import { Disposable } from 'common/Lifecycle';
10
10
 
11
-
12
- const enum MeasureSettings {
13
- REPEAT = 32
14
- }
15
-
16
-
17
11
  export class CharSizeService extends Disposable implements ICharSizeService {
18
12
  public serviceBrand: undefined;
19
13
 
@@ -32,7 +26,11 @@ export class CharSizeService extends Disposable implements ICharSizeService {
32
26
  @IOptionsService private readonly _optionsService: IOptionsService
33
27
  ) {
34
28
  super();
35
- this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService);
29
+ try {
30
+ this._measureStrategy = this.register(new TextMetricsMeasureStrategy(this._optionsService));
31
+ } catch {
32
+ this._measureStrategy = this.register(new DomMeasureStrategy(document, parentElement, this._optionsService));
33
+ }
36
34
  this.register(this._optionsService.onMultipleOptionChange(['fontFamily', 'fontSize'], () => this.measure()));
37
35
  }
38
36
 
@@ -47,12 +45,7 @@ export class CharSizeService extends Disposable implements ICharSizeService {
47
45
  }
48
46
 
49
47
  interface IMeasureStrategy {
50
- measure(): IReadonlyMeasureResult;
51
- }
52
-
53
- interface IReadonlyMeasureResult {
54
- readonly width: number;
55
- readonly height: number;
48
+ measure(): Readonly<IMeasureResult>;
56
49
  }
57
50
 
58
51
  interface IMeasureResult {
@@ -60,10 +53,26 @@ interface IMeasureResult {
60
53
  height: number;
61
54
  }
62
55
 
63
- // TODO: For supporting browsers we should also provide a CanvasCharDimensionsProvider that uses
64
- // ctx.measureText
65
- class DomMeasureStrategy implements IMeasureStrategy {
66
- private _result: IMeasureResult = { width: 0, height: 0 };
56
+ const enum DomMeasureStrategyConstants {
57
+ REPEAT = 32
58
+ }
59
+
60
+ abstract class BaseMeasureStategy extends Disposable implements IMeasureStrategy {
61
+ protected _result: IMeasureResult = { width: 0, height: 0 };
62
+
63
+ protected _validateAndSet(width: number | undefined, height: number | undefined): void {
64
+ // If values are 0 then the element is likely currently display:none, in which case we should
65
+ // retain the previous value.
66
+ if (width !== undefined && width > 0 && height !== undefined && height > 0) {
67
+ this._result.width = width;
68
+ this._result.height = height;
69
+ }
70
+ }
71
+
72
+ public abstract measure(): Readonly<IMeasureResult>;
73
+ }
74
+
75
+ class DomMeasureStrategy extends BaseMeasureStategy {
67
76
  private _measureElement: HTMLElement;
68
77
 
69
78
  constructor(
@@ -71,32 +80,48 @@ class DomMeasureStrategy implements IMeasureStrategy {
71
80
  private _parentElement: HTMLElement,
72
81
  private _optionsService: IOptionsService
73
82
  ) {
83
+ super();
74
84
  this._measureElement = this._document.createElement('span');
75
85
  this._measureElement.classList.add('xterm-char-measure-element');
76
- this._measureElement.textContent = 'W'.repeat(MeasureSettings.REPEAT);
86
+ this._measureElement.textContent = 'W'.repeat(DomMeasureStrategyConstants.REPEAT);
77
87
  this._measureElement.setAttribute('aria-hidden', 'true');
78
88
  this._measureElement.style.whiteSpace = 'pre';
79
89
  this._measureElement.style.fontKerning = 'none';
80
90
  this._parentElement.appendChild(this._measureElement);
81
91
  }
82
92
 
83
- public measure(): IReadonlyMeasureResult {
93
+ public measure(): Readonly<IMeasureResult> {
84
94
  this._measureElement.style.fontFamily = this._optionsService.rawOptions.fontFamily;
85
95
  this._measureElement.style.fontSize = `${this._optionsService.rawOptions.fontSize}px`;
86
96
 
87
97
  // Note that this triggers a synchronous layout
88
- const geometry = {
89
- height: Number(this._measureElement.offsetHeight),
90
- width: Number(this._measureElement.offsetWidth)
91
- };
98
+ this._validateAndSet(Number(this._measureElement.offsetWidth) / DomMeasureStrategyConstants.REPEAT, Number(this._measureElement.offsetHeight));
92
99
 
93
- // If values are 0 then the element is likely currently display:none, in which case we should
94
- // retain the previous value.
95
- if (geometry.width !== 0 && geometry.height !== 0) {
96
- this._result.width = geometry.width / MeasureSettings.REPEAT;
97
- this._result.height = Math.ceil(geometry.height);
100
+ return this._result;
101
+ }
102
+ }
103
+
104
+ class TextMetricsMeasureStrategy extends BaseMeasureStategy {
105
+ private _canvas: OffscreenCanvas;
106
+ private _ctx: OffscreenCanvasRenderingContext2D;
107
+
108
+ constructor(
109
+ private _optionsService: IOptionsService
110
+ ) {
111
+ super();
112
+ // This will throw if any required API is not supported
113
+ this._canvas = new OffscreenCanvas(100, 100);
114
+ this._ctx = this._canvas.getContext('2d')!;
115
+ const a = this._ctx.measureText('W');
116
+ if (!('width' in a && 'fontBoundingBoxAscent' in a && 'fontBoundingBoxDescent' in a)) {
117
+ throw new Error('Required font metrics not supported');
98
118
  }
119
+ }
99
120
 
121
+ public measure(): Readonly<IMeasureResult> {
122
+ this._ctx.font = `${this._optionsService.rawOptions.fontSize}px ${this._optionsService.rawOptions.fontFamily}`;
123
+ const metrics = this._ctx.measureText('W');
124
+ this._validateAndSet(metrics.width, metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent);
100
125
  return this._result;
101
126
  }
102
127
  }