mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Always draw entire line and allow overlapping
State/diffing code is kept in comments as it may be reinstated soon. Fixes #1035 Fixes #1036 Fixes #1032 Fixes #1037
This commit is contained in:
@@ -242,7 +242,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
const charAtlasCellWidth = this._scaledCharWidth + CHAR_ATLAS_CELL_SPACING;
|
||||
const charAtlasCellHeight = this._scaledCharHeight + CHAR_ATLAS_CELL_SPACING;
|
||||
this._ctx.drawImage(this._charAtlas,
|
||||
code * charAtlasCellWidth, colorIndex * charAtlasCellHeight, this._scaledCharWidth, this._scaledCharHeight,
|
||||
code * charAtlasCellWidth, colorIndex * charAtlasCellHeight, charAtlasCellWidth, this._scaledCharHeight,
|
||||
x * this._scaledCharWidth, y * this._scaledLineHeight + this._scaledLineDrawY, this._scaledCharWidth, this._scaledCharHeight);
|
||||
} else {
|
||||
this._drawUncachedChar(terminal, char, width, fg, x, y, bold);
|
||||
@@ -285,7 +285,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
// can bleed into other cells. This code will clip the following fillText,
|
||||
// ensuring that its contents don't go beyond the cell bounds.
|
||||
this._ctx.beginPath();
|
||||
this._ctx.rect(x * this._scaledCharWidth, y * this._scaledLineHeight + this._scaledLineDrawY, width * this._scaledCharWidth, this._scaledCharHeight);
|
||||
this._ctx.rect(0, y * this._scaledLineHeight + this._scaledLineDrawY, terminal.cols * this._scaledCharWidth, this._scaledCharHeight);
|
||||
this._ctx.clip();
|
||||
|
||||
// Draw the character
|
||||
|
||||
@@ -142,13 +142,23 @@ class CharAtlasGenerator {
|
||||
|
||||
// Default color
|
||||
for (let i = 0; i < 256; i++) {
|
||||
this._ctx.save();
|
||||
this._ctx.beginPath();
|
||||
this._ctx.rect(i * cellWidth, 0, cellWidth, cellHeight);
|
||||
this._ctx.clip();
|
||||
this._ctx.fillText(String.fromCharCode(i), i * cellWidth, 0);
|
||||
this._ctx.restore();
|
||||
}
|
||||
// Default color bold
|
||||
this._ctx.save();
|
||||
this._ctx.font = `bold ${this._ctx.font}`;
|
||||
for (let i = 0; i < 256; i++) {
|
||||
this._ctx.save();
|
||||
this._ctx.beginPath();
|
||||
this._ctx.rect(i * cellWidth, cellHeight, cellWidth, cellHeight);
|
||||
this._ctx.clip();
|
||||
this._ctx.fillText(String.fromCharCode(i), i * cellWidth, cellHeight);
|
||||
this._ctx.restore();
|
||||
}
|
||||
this._ctx.restore();
|
||||
|
||||
@@ -162,14 +172,17 @@ class CharAtlasGenerator {
|
||||
const y = (colorIndex + 2) * cellHeight;
|
||||
// Draw ascii characters
|
||||
for (let i = 0; i < 256; i++) {
|
||||
this._ctx.save();
|
||||
this._ctx.beginPath();
|
||||
this._ctx.rect(i * cellWidth, y, cellWidth, cellHeight);
|
||||
this._ctx.clip();
|
||||
this._ctx.fillStyle = ansiColors[colorIndex];
|
||||
this._ctx.fillText(String.fromCharCode(i), i * cellWidth, y);
|
||||
this._ctx.restore();
|
||||
}
|
||||
}
|
||||
this._ctx.restore();
|
||||
|
||||
const charAtlasImageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);
|
||||
|
||||
// Support is patchy for createImageBitmap at the moment, pass a canvas back
|
||||
// if support is lacking as drawImage works there too. Firefox is also
|
||||
// included here as ImageBitmap appears both buggy and has horrible
|
||||
@@ -183,9 +196,27 @@ class CharAtlasGenerator {
|
||||
return result;
|
||||
}
|
||||
|
||||
const charAtlasImageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);
|
||||
|
||||
// Remove the background color from the image so characters may overlap
|
||||
const r = parseInt(background.substr(1, 2), 16);
|
||||
const g = parseInt(background.substr(3, 2), 16);
|
||||
const b = parseInt(background.substr(5, 2), 16);
|
||||
this._clearColor(charAtlasImageData, r, g, b);
|
||||
|
||||
const promise = window.createImageBitmap(charAtlasImageData);
|
||||
// Clear the rect while the promise is in progress
|
||||
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
return promise;
|
||||
}
|
||||
|
||||
private _clearColor(imageData: ImageData, r: number, g: number, b: number): void {
|
||||
for (let offset = 0; offset < imageData.data.length; offset += 4) {
|
||||
if (imageData.data[offset] === r &&
|
||||
imageData.data[offset + 1] === g &&
|
||||
imageData.data[offset + 2] === b) {
|
||||
imageData.data[offset + 3] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export class ColorManager implements IColorManager {
|
||||
*/
|
||||
public setTheme(theme: ITheme): void {
|
||||
this.colors.foreground = theme.foreground || DEFAULT_FOREGROUND;
|
||||
this.colors.background = theme.background || DEFAULT_BACKGROUND;
|
||||
this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND);
|
||||
this.colors.cursor = theme.cursor || DEFAULT_CURSOR;
|
||||
this.colors.cursorAccent = theme.cursorAccent || DEFAULT_CURSOR_ACCENT;
|
||||
this.colors.selection = theme.selection || DEFAULT_SELECTION;
|
||||
@@ -107,4 +107,17 @@ export class ColorManager implements IColorManager {
|
||||
this.colors.ansi[14] = theme.brightCyan || DEFAULT_ANSI_COLORS[14];
|
||||
this.colors.ansi[15] = theme.brightWhite || DEFAULT_ANSI_COLORS[15];
|
||||
}
|
||||
|
||||
private _validateColor(color: string, fallback: string): string {
|
||||
if (color.length === 7 && color.charAt(0) === '#') {
|
||||
return color;
|
||||
}
|
||||
if (color.length === 4 && color.charAt(0) === '#') {
|
||||
const r = color.charAt(1);
|
||||
const g = color.charAt(2);
|
||||
const b = color.charAt(3);
|
||||
return `#${r}${r}${g}${g}${b}${b}`;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
const row = y + terminal.buffer.ydisp;
|
||||
const line = terminal.buffer.lines.get(row);
|
||||
|
||||
this.clearCells(0, y, terminal.cols, 1);
|
||||
// for (let x = 0; x < terminal.cols; x++) {
|
||||
// this._state.cache[x][y] = null;
|
||||
// }
|
||||
|
||||
for (let x = 0; x < terminal.cols; x++) {
|
||||
const charData = line[x];
|
||||
const code: number = <number>charData[CHAR_DATA_CODE_INDEX];
|
||||
@@ -69,7 +74,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
// The character to the left is a wide character, drawing is owned by
|
||||
// the char at x-1
|
||||
if (width === 0) {
|
||||
this._state.cache[x][y] = null;
|
||||
// this._state.cache[x][y] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -86,19 +91,19 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
|
||||
// Skip rendering if the character is identical
|
||||
const state = this._state.cache[x][y];
|
||||
if (state && state[CHAR_DATA_CHAR_INDEX] === char && state[CHAR_DATA_ATTR_INDEX] === attr) {
|
||||
// Skip render, contents are identical
|
||||
this._state.cache[x][y] = charData;
|
||||
continue;
|
||||
}
|
||||
// const state = this._state.cache[x][y];
|
||||
// if (state && state[CHAR_DATA_CHAR_INDEX] === char && state[CHAR_DATA_ATTR_INDEX] === attr) {
|
||||
// // Skip render, contents are identical
|
||||
// this._state.cache[x][y] = charData;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Clear the old character was not a space with the default background
|
||||
const wasInverted = !!(state && state[CHAR_DATA_ATTR_INDEX] && state[CHAR_DATA_ATTR_INDEX] >> 18 & FLAGS.INVERSE);
|
||||
if (state && !(state[CHAR_DATA_CODE_INDEX] === 32 /*' '*/ && (state[CHAR_DATA_ATTR_INDEX] & 0x1ff) >= 256 && !wasInverted)) {
|
||||
this._clearChar(x, y);
|
||||
}
|
||||
this._state.cache[x][y] = charData;
|
||||
// const wasInverted = !!(state && state[CHAR_DATA_ATTR_INDEX] && state[CHAR_DATA_ATTR_INDEX] >> 18 & FLAGS.INVERSE);
|
||||
// if (state && !(state[CHAR_DATA_CODE_INDEX] === 32 /*' '*/ && (state[CHAR_DATA_ATTR_INDEX] & 0x1ff) >= 256 && !wasInverted)) {
|
||||
// this._clearChar(x, y);
|
||||
// }
|
||||
// this._state.cache[x][y] = charData;
|
||||
|
||||
const flags = attr >> 18;
|
||||
let bg = attr & 0x1ff;
|
||||
@@ -120,14 +125,14 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
// space is added. Without this, the first half of `b` would never
|
||||
// get removed, and `a` would not re-render because it thinks it's
|
||||
// already in the correct state.
|
||||
this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
|
||||
// this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
|
||||
if (x < line.length - 1 && line[x + 1][CHAR_DATA_CODE_INDEX] === 32 /*' '*/) {
|
||||
width = 2;
|
||||
this._clearChar(x + 1, y);
|
||||
// this._clearChar(x + 1, y);
|
||||
// The overlapping char's char data will force a clear and render when the
|
||||
// overlapping char is no longer to the left of the character and also when
|
||||
// the space changes to another character.
|
||||
this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;
|
||||
// this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +153,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
|
||||
// Clear the cell next to this character if it's wide
|
||||
if (width === 2) {
|
||||
this.clearCells(x + 1, y, 1, 1);
|
||||
// this.clearCells(x + 1, y, 1, 1);
|
||||
}
|
||||
|
||||
// Draw background
|
||||
|
||||
Reference in New Issue
Block a user