mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #440 from Tyriar/439_wide_char_class
Style wide characters at charWidth*2 in DOM
This commit is contained in:
+10
-16
@@ -2,11 +2,11 @@ import { assert } from 'chai';
|
||||
import { Viewport } from './Viewport';
|
||||
|
||||
describe('Viewport', () => {
|
||||
var terminal;
|
||||
var viewportElement;
|
||||
var charMeasureElement;
|
||||
var viewport;
|
||||
var scrollAreaElement;
|
||||
let terminal;
|
||||
let viewportElement;
|
||||
let charMeasure;
|
||||
let viewport;
|
||||
let scrollAreaElement;
|
||||
|
||||
const CHARACTER_HEIGHT = 10;
|
||||
|
||||
@@ -34,21 +34,17 @@ describe('Viewport', () => {
|
||||
height: 0
|
||||
}
|
||||
};
|
||||
charMeasureElement = {
|
||||
getBoundingClientRect: () => {
|
||||
return { width: null, height: CHARACTER_HEIGHT };
|
||||
}
|
||||
charMeasure = {
|
||||
height: CHARACTER_HEIGHT
|
||||
};
|
||||
viewport = new Viewport(terminal, viewportElement, scrollAreaElement, charMeasureElement);
|
||||
viewport = new Viewport(terminal, viewportElement, scrollAreaElement, charMeasure);
|
||||
});
|
||||
|
||||
describe('refresh', () => {
|
||||
it('should set the line-height of the terminal', () => {
|
||||
assert.equal(viewportElement.style.lineHeight, CHARACTER_HEIGHT + 'px');
|
||||
assert.equal(terminal.rowContainer.style.lineHeight, CHARACTER_HEIGHT + 'px');
|
||||
charMeasureElement.getBoundingClientRect = () => {
|
||||
return { width: null, height: 1 };
|
||||
};
|
||||
charMeasure.height = 1;
|
||||
viewport.refresh();
|
||||
assert.equal(viewportElement.style.lineHeight, '1px');
|
||||
assert.equal(terminal.rowContainer.style.lineHeight, '1px');
|
||||
@@ -59,9 +55,7 @@ describe('Viewport', () => {
|
||||
terminal.rows = 1;
|
||||
viewport.refresh();
|
||||
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
|
||||
charMeasureElement.getBoundingClientRect = () => {
|
||||
return { width: null, height: 20 };
|
||||
};
|
||||
charMeasure.height = 20;
|
||||
viewport.refresh();
|
||||
assert.equal(viewportElement.style.height, 20 + 'px');
|
||||
});
|
||||
|
||||
+12
-13
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { ITerminal } from './Interfaces';
|
||||
import { CharMeasure } from './utils/CharMeasure';
|
||||
|
||||
/**
|
||||
* Represents the viewport of a terminal, the visible area within the larger buffer of output.
|
||||
@@ -24,7 +25,7 @@ export class Viewport {
|
||||
private terminal: ITerminal,
|
||||
private viewportElement: HTMLElement,
|
||||
private scrollArea: HTMLElement,
|
||||
private charMeasureElement: HTMLElement
|
||||
private charMeasure: CharMeasure
|
||||
) {
|
||||
this.currentRowHeight = 0;
|
||||
this.lastRecordedBufferLength = 0;
|
||||
@@ -43,21 +44,20 @@ export class Viewport {
|
||||
* @param charSize A character size measurement bounding rect object, if it doesn't exist it will
|
||||
* be created.
|
||||
*/
|
||||
private refresh(charSize?: ClientRect): void {
|
||||
var size = charSize || this.charMeasureElement.getBoundingClientRect();
|
||||
if (size.height > 0) {
|
||||
var rowHeightChanged = size.height !== this.currentRowHeight;
|
||||
private refresh(): void {
|
||||
if (this.charMeasure.height > 0) {
|
||||
var rowHeightChanged = this.charMeasure.height !== this.currentRowHeight;
|
||||
if (rowHeightChanged) {
|
||||
this.currentRowHeight = size.height;
|
||||
this.viewportElement.style.lineHeight = size.height + 'px';
|
||||
this.terminal.rowContainer.style.lineHeight = size.height + 'px';
|
||||
this.currentRowHeight = this.charMeasure.height;
|
||||
this.viewportElement.style.lineHeight = this.charMeasure.height + 'px';
|
||||
this.terminal.rowContainer.style.lineHeight = this.charMeasure.height + 'px';
|
||||
}
|
||||
var viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows;
|
||||
if (rowHeightChanged || viewportHeightChanged) {
|
||||
this.lastRecordedViewportHeight = this.terminal.rows;
|
||||
this.viewportElement.style.height = size.height * this.terminal.rows + 'px';
|
||||
this.viewportElement.style.height = this.charMeasure.height * this.terminal.rows + 'px';
|
||||
}
|
||||
this.scrollArea.style.height = (size.height * this.lastRecordedBufferLength) + 'px';
|
||||
this.scrollArea.style.height = (this.charMeasure.height * this.lastRecordedBufferLength) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +74,8 @@ export class Viewport {
|
||||
this.refresh();
|
||||
} else {
|
||||
// If size has changed, refresh viewport
|
||||
var size = this.charMeasureElement.getBoundingClientRect();
|
||||
if (size.height !== this.currentRowHeight) {
|
||||
this.refresh(size);
|
||||
if (this.charMeasure.height !== this.currentRowHeight) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @module xterm/utils/CharMeasure
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { EventEmitter } from '../EventEmitter.js';
|
||||
|
||||
/**
|
||||
* Utility class that measures the size of a character.
|
||||
*/
|
||||
export class CharMeasure extends EventEmitter {
|
||||
private _parentElement: HTMLElement;
|
||||
private _measureElement: HTMLElement;
|
||||
private _width: number;
|
||||
private _height: number;
|
||||
|
||||
constructor(parentElement: HTMLElement) {
|
||||
super();
|
||||
this._parentElement = parentElement;
|
||||
}
|
||||
|
||||
public get width(): number {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
public get height(): number {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public measure(): void {
|
||||
const oldWidth = this._width;
|
||||
const oldHeight = this._height;
|
||||
|
||||
if (!this._measureElement) {
|
||||
this._measureElement = document.createElement('span');
|
||||
this._measureElement.style.position = 'absolute';
|
||||
this._measureElement.style.top = '0';
|
||||
this._measureElement.style.left = '-9999em';
|
||||
this._measureElement.textContent = 'W';
|
||||
}
|
||||
|
||||
this._parentElement.appendChild(this._measureElement);
|
||||
const geometry = this._measureElement.getBoundingClientRect();
|
||||
this._width = geometry.width;
|
||||
this._height = geometry.height;
|
||||
this._parentElement.removeChild(this._measureElement);
|
||||
|
||||
if (this._width !== oldWidth || this._height !== oldHeight) {
|
||||
this.emit('charsizechanged');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,10 @@
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.terminal .xterm-wide-char {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.terminal .xterm-rows {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
||||
+25
-5
@@ -15,6 +15,7 @@ import { EventEmitter } from './EventEmitter.js';
|
||||
import { Viewport } from './Viewport.js';
|
||||
import { rightClickHandler, pasteHandler, copyHandler } from './handlers/Clipboard.js';
|
||||
import { CircularList } from './utils/CircularList.js';
|
||||
import { CharMeasure } from './utils/CharMeasure.js';
|
||||
import * as Browser from './utils/Browser';
|
||||
import * as Keyboard from './utils/Keyboard';
|
||||
|
||||
@@ -567,17 +568,21 @@ Terminal.prototype.open = function(parent) {
|
||||
this.compositionHelper = new CompositionHelper(this.textarea, this.compositionView, this);
|
||||
this.helperContainer.appendChild(this.compositionView);
|
||||
|
||||
this.charMeasureElement = document.createElement('div');
|
||||
this.charMeasureElement.classList.add('xterm-char-measure-element');
|
||||
this.charMeasureElement.innerHTML = 'W';
|
||||
this.helperContainer.appendChild(this.charMeasureElement);
|
||||
this.charSizeStyleElement = document.createElement('style');
|
||||
this.helperContainer.appendChild(this.charSizeStyleElement);
|
||||
|
||||
for (; i < this.rows; i++) {
|
||||
this.insertRow();
|
||||
}
|
||||
this.parent.appendChild(this.element);
|
||||
|
||||
this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasureElement);
|
||||
this.charMeasure = new CharMeasure(this.rowContainer);
|
||||
this.charMeasure.on('charsizechanged', function () {
|
||||
self.updateCharSizeCSS();
|
||||
});
|
||||
this.charMeasure.measure();
|
||||
|
||||
this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);
|
||||
|
||||
// Setup loop that draws to screen
|
||||
this.queueRefresh(0, this.rows - 1);
|
||||
@@ -636,6 +641,13 @@ Terminal.loadAddon = function(addon, callback) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the helper CSS class with any changes necessary after the terminal's
|
||||
* character width has been changed.
|
||||
*/
|
||||
Terminal.prototype.updateCharSizeCSS = function() {
|
||||
this.charSizeStyleElement.textContent = '.xterm-wide-char{width:' + (this.charMeasure.width * 2) + 'px;}';
|
||||
}
|
||||
|
||||
/**
|
||||
* XTerm mouse events
|
||||
@@ -1182,6 +1194,9 @@ Terminal.prototype.refresh = function(start, end) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ch_width === 2) {
|
||||
out += '<span class="xterm-wide-char">';
|
||||
}
|
||||
switch (ch) {
|
||||
case '&':
|
||||
out += '&';
|
||||
@@ -1200,6 +1215,9 @@ Terminal.prototype.refresh = function(start, end) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ch_width === 2) {
|
||||
out += '</span>';
|
||||
}
|
||||
|
||||
attr = data;
|
||||
}
|
||||
@@ -2945,6 +2963,8 @@ Terminal.prototype.resize = function(x, y) {
|
||||
this.scrollTop = 0;
|
||||
this.scrollBottom = y - 1;
|
||||
|
||||
this.charMeasure.measure();
|
||||
|
||||
this.queueRefresh(0, this.rows - 1);
|
||||
|
||||
this.normal = null;
|
||||
|
||||
Reference in New Issue
Block a user