@xterm/xterm 5.4.0-beta.1
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/LICENSE +21 -0
- package/README.md +235 -0
- package/css/xterm.css +209 -0
- package/lib/xterm.js +2 -0
- package/lib/xterm.js.map +1 -0
- package/package.json +101 -0
- package/src/browser/AccessibilityManager.ts +278 -0
- package/src/browser/Clipboard.ts +93 -0
- package/src/browser/ColorContrastCache.ts +34 -0
- package/src/browser/Lifecycle.ts +33 -0
- package/src/browser/Linkifier2.ts +416 -0
- package/src/browser/LocalizableStrings.ts +12 -0
- package/src/browser/OscLinkProvider.ts +128 -0
- package/src/browser/RenderDebouncer.ts +83 -0
- package/src/browser/Terminal.ts +1317 -0
- package/src/browser/TimeBasedDebouncer.ts +86 -0
- package/src/browser/Types.d.ts +181 -0
- package/src/browser/Viewport.ts +401 -0
- package/src/browser/decorations/BufferDecorationRenderer.ts +134 -0
- package/src/browser/decorations/ColorZoneStore.ts +117 -0
- package/src/browser/decorations/OverviewRulerRenderer.ts +218 -0
- package/src/browser/input/CompositionHelper.ts +246 -0
- package/src/browser/input/Mouse.ts +54 -0
- package/src/browser/input/MoveToCell.ts +249 -0
- package/src/browser/public/Terminal.ts +260 -0
- package/src/browser/renderer/dom/DomRenderer.ts +509 -0
- package/src/browser/renderer/dom/DomRendererRowFactory.ts +526 -0
- package/src/browser/renderer/dom/WidthCache.ts +160 -0
- package/src/browser/renderer/shared/CellColorResolver.ts +137 -0
- package/src/browser/renderer/shared/CharAtlasCache.ts +96 -0
- package/src/browser/renderer/shared/CharAtlasUtils.ts +75 -0
- package/src/browser/renderer/shared/Constants.ts +14 -0
- package/src/browser/renderer/shared/CursorBlinkStateManager.ts +146 -0
- package/src/browser/renderer/shared/CustomGlyphs.ts +687 -0
- package/src/browser/renderer/shared/DevicePixelObserver.ts +41 -0
- package/src/browser/renderer/shared/README.md +1 -0
- package/src/browser/renderer/shared/RendererUtils.ts +58 -0
- package/src/browser/renderer/shared/SelectionRenderModel.ts +91 -0
- package/src/browser/renderer/shared/TextureAtlas.ts +1082 -0
- package/src/browser/renderer/shared/Types.d.ts +173 -0
- package/src/browser/selection/SelectionModel.ts +144 -0
- package/src/browser/selection/Types.d.ts +15 -0
- package/src/browser/services/CharSizeService.ts +102 -0
- package/src/browser/services/CharacterJoinerService.ts +339 -0
- package/src/browser/services/CoreBrowserService.ts +137 -0
- package/src/browser/services/MouseService.ts +46 -0
- package/src/browser/services/RenderService.ts +279 -0
- package/src/browser/services/SelectionService.ts +1031 -0
- package/src/browser/services/Services.ts +147 -0
- package/src/browser/services/ThemeService.ts +237 -0
- package/src/common/CircularList.ts +241 -0
- package/src/common/Clone.ts +23 -0
- package/src/common/Color.ts +357 -0
- package/src/common/CoreTerminal.ts +284 -0
- package/src/common/EventEmitter.ts +78 -0
- package/src/common/InputHandler.ts +3461 -0
- package/src/common/Lifecycle.ts +108 -0
- package/src/common/MultiKeyMap.ts +42 -0
- package/src/common/Platform.ts +44 -0
- package/src/common/SortedList.ts +118 -0
- package/src/common/TaskQueue.ts +166 -0
- package/src/common/TypedArrayUtils.ts +17 -0
- package/src/common/Types.d.ts +553 -0
- package/src/common/WindowsMode.ts +27 -0
- package/src/common/buffer/AttributeData.ts +196 -0
- package/src/common/buffer/Buffer.ts +654 -0
- package/src/common/buffer/BufferLine.ts +524 -0
- package/src/common/buffer/BufferRange.ts +13 -0
- package/src/common/buffer/BufferReflow.ts +223 -0
- package/src/common/buffer/BufferSet.ts +134 -0
- package/src/common/buffer/CellData.ts +94 -0
- package/src/common/buffer/Constants.ts +149 -0
- package/src/common/buffer/Marker.ts +43 -0
- package/src/common/buffer/Types.d.ts +52 -0
- package/src/common/data/Charsets.ts +256 -0
- package/src/common/data/EscapeSequences.ts +153 -0
- package/src/common/input/Keyboard.ts +398 -0
- package/src/common/input/TextDecoder.ts +346 -0
- package/src/common/input/UnicodeV6.ts +145 -0
- package/src/common/input/WriteBuffer.ts +246 -0
- package/src/common/input/XParseColor.ts +80 -0
- package/src/common/parser/Constants.ts +58 -0
- package/src/common/parser/DcsParser.ts +192 -0
- package/src/common/parser/EscapeSequenceParser.ts +792 -0
- package/src/common/parser/OscParser.ts +238 -0
- package/src/common/parser/Params.ts +229 -0
- package/src/common/parser/Types.d.ts +275 -0
- package/src/common/public/AddonManager.ts +53 -0
- package/src/common/public/BufferApiView.ts +35 -0
- package/src/common/public/BufferLineApiView.ts +29 -0
- package/src/common/public/BufferNamespaceApi.ts +36 -0
- package/src/common/public/ParserApi.ts +37 -0
- package/src/common/public/UnicodeApi.ts +27 -0
- package/src/common/services/BufferService.ts +151 -0
- package/src/common/services/CharsetService.ts +34 -0
- package/src/common/services/CoreMouseService.ts +318 -0
- package/src/common/services/CoreService.ts +87 -0
- package/src/common/services/DecorationService.ts +140 -0
- package/src/common/services/InstantiationService.ts +85 -0
- package/src/common/services/LogService.ts +124 -0
- package/src/common/services/OptionsService.ts +202 -0
- package/src/common/services/OscLinkService.ts +115 -0
- package/src/common/services/ServiceRegistry.ts +49 -0
- package/src/common/services/Services.ts +373 -0
- package/src/common/services/UnicodeService.ts +111 -0
- package/src/headless/Terminal.ts +136 -0
- package/src/headless/public/Terminal.ts +195 -0
- package/typings/xterm.d.ts +1857 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { FontWeight, Terminal } from '@xterm/xterm';
|
|
7
|
+
import { IColorSet } from 'browser/Types';
|
|
8
|
+
import { IDisposable } from 'common/Types';
|
|
9
|
+
import { IEvent } from 'common/EventEmitter';
|
|
10
|
+
|
|
11
|
+
export interface ICharAtlasConfig {
|
|
12
|
+
customGlyphs: boolean;
|
|
13
|
+
devicePixelRatio: number;
|
|
14
|
+
letterSpacing: number;
|
|
15
|
+
lineHeight: number;
|
|
16
|
+
fontSize: number;
|
|
17
|
+
fontFamily: string;
|
|
18
|
+
fontWeight: FontWeight;
|
|
19
|
+
fontWeightBold: FontWeight;
|
|
20
|
+
deviceCellWidth: number;
|
|
21
|
+
deviceCellHeight: number;
|
|
22
|
+
deviceCharWidth: number;
|
|
23
|
+
deviceCharHeight: number;
|
|
24
|
+
allowTransparency: boolean;
|
|
25
|
+
drawBoldTextInBrightColors: boolean;
|
|
26
|
+
minimumContrastRatio: number;
|
|
27
|
+
colors: IColorSet;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface IDimensions {
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface IOffset {
|
|
36
|
+
top: number;
|
|
37
|
+
left: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface IRenderDimensions {
|
|
41
|
+
/**
|
|
42
|
+
* Dimensions measured in CSS pixels (ie. device pixels / device pixel ratio).
|
|
43
|
+
*/
|
|
44
|
+
css: {
|
|
45
|
+
canvas: IDimensions;
|
|
46
|
+
cell: IDimensions;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Dimensions measured in actual pixels as rendered to the device.
|
|
50
|
+
*/
|
|
51
|
+
device: {
|
|
52
|
+
canvas: IDimensions;
|
|
53
|
+
cell: IDimensions;
|
|
54
|
+
char: IDimensions & IOffset;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface IRequestRedrawEvent {
|
|
59
|
+
start: number;
|
|
60
|
+
end: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Note that IRenderer implementations should emit the refresh event after
|
|
65
|
+
* rendering rows to the screen.
|
|
66
|
+
*/
|
|
67
|
+
export interface IRenderer extends IDisposable {
|
|
68
|
+
readonly dimensions: IRenderDimensions;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Fires when the renderer is requesting to be redrawn on the next animation
|
|
72
|
+
* frame but is _not_ a result of content changing (eg. selection changes).
|
|
73
|
+
*/
|
|
74
|
+
readonly onRequestRedraw: IEvent<IRequestRedrawEvent>;
|
|
75
|
+
|
|
76
|
+
dispose(): void;
|
|
77
|
+
handleDevicePixelRatioChange(): void;
|
|
78
|
+
handleResize(cols: number, rows: number): void;
|
|
79
|
+
handleCharSizeChanged(): void;
|
|
80
|
+
handleBlur(): void;
|
|
81
|
+
handleFocus(): void;
|
|
82
|
+
handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
|
|
83
|
+
handleCursorMove(): void;
|
|
84
|
+
clear(): void;
|
|
85
|
+
renderRows(start: number, end: number): void;
|
|
86
|
+
clearTextureAtlas?(): void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ITextureAtlas extends IDisposable {
|
|
90
|
+
readonly pages: { canvas: HTMLCanvasElement, version: number }[];
|
|
91
|
+
|
|
92
|
+
onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
|
|
93
|
+
onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Warm up the texture atlas, adding common glyphs to avoid slowing early frame.
|
|
97
|
+
*/
|
|
98
|
+
warmUp(): void;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Call when a frame is being drawn, this will return true if the atlas was cleared to make room
|
|
102
|
+
* for a new set of glyphs.
|
|
103
|
+
*/
|
|
104
|
+
beginFrame(): boolean;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Clear all glyphs from the texture atlas.
|
|
108
|
+
*/
|
|
109
|
+
clearTexture(): void;
|
|
110
|
+
getRasterizedGlyph(code: number, bg: number, fg: number, ext: number, restrictToCellHeight: boolean): IRasterizedGlyph;
|
|
111
|
+
getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean): IRasterizedGlyph;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Represents a rasterized glyph within a texture atlas. Some numbers are
|
|
116
|
+
* tracked in CSS pixels as well in order to reduce calculations during the
|
|
117
|
+
* render loop.
|
|
118
|
+
*/
|
|
119
|
+
export interface IRasterizedGlyph {
|
|
120
|
+
/**
|
|
121
|
+
* The x and y offset between the glyph's top/left and the top/left of a cell
|
|
122
|
+
* in pixels.
|
|
123
|
+
*/
|
|
124
|
+
offset: IVector;
|
|
125
|
+
/**
|
|
126
|
+
* The index of the texture page that the glyph is on.
|
|
127
|
+
*/
|
|
128
|
+
texturePage: number;
|
|
129
|
+
/**
|
|
130
|
+
* the x and y position of the glyph in the texture in pixels.
|
|
131
|
+
*/
|
|
132
|
+
texturePosition: IVector;
|
|
133
|
+
/**
|
|
134
|
+
* the x and y position of the glyph in the texture in clip space coordinates.
|
|
135
|
+
*/
|
|
136
|
+
texturePositionClipSpace: IVector;
|
|
137
|
+
/**
|
|
138
|
+
* The width and height of the glyph in the texture in pixels.
|
|
139
|
+
*/
|
|
140
|
+
size: IVector;
|
|
141
|
+
/**
|
|
142
|
+
* The width and height of the glyph in the texture in clip space coordinates.
|
|
143
|
+
*/
|
|
144
|
+
sizeClipSpace: IVector;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface IVector {
|
|
148
|
+
x: number;
|
|
149
|
+
y: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface IBoundingBox {
|
|
153
|
+
top: number;
|
|
154
|
+
left: number;
|
|
155
|
+
right: number;
|
|
156
|
+
bottom: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface ISelectionRenderModel {
|
|
160
|
+
readonly hasSelection: boolean;
|
|
161
|
+
readonly columnSelectMode: boolean;
|
|
162
|
+
readonly viewportStartRow: number;
|
|
163
|
+
readonly viewportEndRow: number;
|
|
164
|
+
readonly viewportCappedStartRow: number;
|
|
165
|
+
readonly viewportCappedEndRow: number;
|
|
166
|
+
readonly startCol: number;
|
|
167
|
+
readonly endCol: number;
|
|
168
|
+
readonly selectionStart: [number, number] | undefined;
|
|
169
|
+
readonly selectionEnd: [number, number] | undefined;
|
|
170
|
+
clear(): void;
|
|
171
|
+
update(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode?: boolean): void;
|
|
172
|
+
isCellSelected(terminal: Terminal, x: number, y: number): boolean;
|
|
173
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IBufferService } from 'common/services/Services';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Represents a selection within the buffer. This model only cares about column
|
|
10
|
+
* and row coordinates, not wide characters.
|
|
11
|
+
*/
|
|
12
|
+
export class SelectionModel {
|
|
13
|
+
/**
|
|
14
|
+
* Whether select all is currently active.
|
|
15
|
+
*/
|
|
16
|
+
public isSelectAllActive: boolean = false;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The minimal length of the selection from the start position. When double
|
|
20
|
+
* clicking on a word, the word will be selected which makes the selection
|
|
21
|
+
* start at the start of the word and makes this variable the length.
|
|
22
|
+
*/
|
|
23
|
+
public selectionStartLength: number = 0;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The [x, y] position the selection starts at.
|
|
27
|
+
*/
|
|
28
|
+
public selectionStart: [number, number] | undefined;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The [x, y] position the selection ends at.
|
|
32
|
+
*/
|
|
33
|
+
public selectionEnd: [number, number] | undefined;
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
private _bufferService: IBufferService
|
|
37
|
+
) {
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Clears the current selection.
|
|
42
|
+
*/
|
|
43
|
+
public clearSelection(): void {
|
|
44
|
+
this.selectionStart = undefined;
|
|
45
|
+
this.selectionEnd = undefined;
|
|
46
|
+
this.isSelectAllActive = false;
|
|
47
|
+
this.selectionStartLength = 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The final selection start, taking into consideration select all.
|
|
52
|
+
*/
|
|
53
|
+
public get finalSelectionStart(): [number, number] | undefined {
|
|
54
|
+
if (this.isSelectAllActive) {
|
|
55
|
+
return [0, 0];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!this.selectionEnd || !this.selectionStart) {
|
|
59
|
+
return this.selectionStart;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The final selection end, taking into consideration select all, double click
|
|
67
|
+
* word selection and triple click line selection.
|
|
68
|
+
*/
|
|
69
|
+
public get finalSelectionEnd(): [number, number] | undefined {
|
|
70
|
+
if (this.isSelectAllActive) {
|
|
71
|
+
return [this._bufferService.cols, this._bufferService.buffer.ybase + this._bufferService.rows - 1];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!this.selectionStart) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Use the selection start + length if the end doesn't exist or they're reversed
|
|
79
|
+
if (!this.selectionEnd || this.areSelectionValuesReversed()) {
|
|
80
|
+
const startPlusLength = this.selectionStart[0] + this.selectionStartLength;
|
|
81
|
+
if (startPlusLength > this._bufferService.cols) {
|
|
82
|
+
// Ensure the trailing EOL isn't included when the selection ends on the right edge
|
|
83
|
+
if (startPlusLength % this._bufferService.cols === 0) {
|
|
84
|
+
return [this._bufferService.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._bufferService.cols) - 1];
|
|
85
|
+
}
|
|
86
|
+
return [startPlusLength % this._bufferService.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._bufferService.cols)];
|
|
87
|
+
}
|
|
88
|
+
return [startPlusLength, this.selectionStart[1]];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Ensure the the word/line is selected after a double/triple click
|
|
92
|
+
if (this.selectionStartLength) {
|
|
93
|
+
// Select the larger of the two when start and end are on the same line
|
|
94
|
+
if (this.selectionEnd[1] === this.selectionStart[1]) {
|
|
95
|
+
// Keep the whole wrapped word/line selected if the content wraps multiple lines
|
|
96
|
+
const startPlusLength = this.selectionStart[0] + this.selectionStartLength;
|
|
97
|
+
if (startPlusLength > this._bufferService.cols) {
|
|
98
|
+
return [startPlusLength % this._bufferService.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._bufferService.cols)];
|
|
99
|
+
}
|
|
100
|
+
return [Math.max(startPlusLength, this.selectionEnd[0]), this.selectionEnd[1]];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return this.selectionEnd;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Returns whether the selection start and end are reversed.
|
|
108
|
+
*/
|
|
109
|
+
public areSelectionValuesReversed(): boolean {
|
|
110
|
+
const start = this.selectionStart;
|
|
111
|
+
const end = this.selectionEnd;
|
|
112
|
+
if (!start || !end) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Handle the buffer being trimmed, adjust the selection position.
|
|
120
|
+
* @param amount The amount the buffer is being trimmed.
|
|
121
|
+
* @returns Whether a refresh is necessary.
|
|
122
|
+
*/
|
|
123
|
+
public handleTrim(amount: number): boolean {
|
|
124
|
+
// Adjust the selection position based on the trimmed amount.
|
|
125
|
+
if (this.selectionStart) {
|
|
126
|
+
this.selectionStart[1] -= amount;
|
|
127
|
+
}
|
|
128
|
+
if (this.selectionEnd) {
|
|
129
|
+
this.selectionEnd[1] -= amount;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// The selection has moved off the buffer, clear it.
|
|
133
|
+
if (this.selectionEnd && this.selectionEnd[1] < 0) {
|
|
134
|
+
this.clearSelection();
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If the selection start is trimmed, ensure the start column is 0.
|
|
139
|
+
if (this.selectionStart && this.selectionStart[1] < 0) {
|
|
140
|
+
this.selectionStart[1] = 0;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ISelectionRedrawRequestEvent {
|
|
7
|
+
start: [number, number] | undefined;
|
|
8
|
+
end: [number, number] | undefined;
|
|
9
|
+
columnSelectMode: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ISelectionRequestScrollLinesEvent {
|
|
13
|
+
amount: number;
|
|
14
|
+
suppressScrollEvent: boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
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 { EventEmitter } from 'common/EventEmitter';
|
|
8
|
+
import { ICharSizeService } from 'browser/services/Services';
|
|
9
|
+
import { Disposable } from 'common/Lifecycle';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const enum MeasureSettings {
|
|
13
|
+
REPEAT = 32
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export class CharSizeService extends Disposable implements ICharSizeService {
|
|
18
|
+
public serviceBrand: undefined;
|
|
19
|
+
|
|
20
|
+
public width: number = 0;
|
|
21
|
+
public height: number = 0;
|
|
22
|
+
private _measureStrategy: IMeasureStrategy;
|
|
23
|
+
|
|
24
|
+
public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
|
|
25
|
+
|
|
26
|
+
private readonly _onCharSizeChange = this.register(new EventEmitter<void>());
|
|
27
|
+
public readonly onCharSizeChange = this._onCharSizeChange.event;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
document: Document,
|
|
31
|
+
parentElement: HTMLElement,
|
|
32
|
+
@IOptionsService private readonly _optionsService: IOptionsService
|
|
33
|
+
) {
|
|
34
|
+
super();
|
|
35
|
+
this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService);
|
|
36
|
+
this.register(this._optionsService.onMultipleOptionChange(['fontFamily', 'fontSize'], () => this.measure()));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public measure(): void {
|
|
40
|
+
const result = this._measureStrategy.measure();
|
|
41
|
+
if (result.width !== this.width || result.height !== this.height) {
|
|
42
|
+
this.width = result.width;
|
|
43
|
+
this.height = result.height;
|
|
44
|
+
this._onCharSizeChange.fire();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface IMeasureStrategy {
|
|
50
|
+
measure(): IReadonlyMeasureResult;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface IReadonlyMeasureResult {
|
|
54
|
+
readonly width: number;
|
|
55
|
+
readonly height: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface IMeasureResult {
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
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 };
|
|
67
|
+
private _measureElement: HTMLElement;
|
|
68
|
+
|
|
69
|
+
constructor(
|
|
70
|
+
private _document: Document,
|
|
71
|
+
private _parentElement: HTMLElement,
|
|
72
|
+
private _optionsService: IOptionsService
|
|
73
|
+
) {
|
|
74
|
+
this._measureElement = this._document.createElement('span');
|
|
75
|
+
this._measureElement.classList.add('xterm-char-measure-element');
|
|
76
|
+
this._measureElement.textContent = 'W'.repeat(MeasureSettings.REPEAT);
|
|
77
|
+
this._measureElement.setAttribute('aria-hidden', 'true');
|
|
78
|
+
this._measureElement.style.whiteSpace = 'pre';
|
|
79
|
+
this._measureElement.style.fontKerning = 'none';
|
|
80
|
+
this._parentElement.appendChild(this._measureElement);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public measure(): IReadonlyMeasureResult {
|
|
84
|
+
this._measureElement.style.fontFamily = this._optionsService.rawOptions.fontFamily;
|
|
85
|
+
this._measureElement.style.fontSize = `${this._optionsService.rawOptions.fontSize}px`;
|
|
86
|
+
|
|
87
|
+
// Note that this triggers a synchronous layout
|
|
88
|
+
const geometry = {
|
|
89
|
+
height: Number(this._measureElement.offsetHeight),
|
|
90
|
+
width: Number(this._measureElement.offsetWidth)
|
|
91
|
+
};
|
|
92
|
+
|
|
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);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this._result;
|
|
101
|
+
}
|
|
102
|
+
}
|