First pass at supporting wide UTF8 chars like 中文
This commit is contained in:
parent
97a36b68b4
commit
64431be7c3
4556
package-lock.json
generated
Normal file
4556
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -19,6 +19,7 @@
|
||||||
"webpack": "^3.10.0"
|
"webpack": "^3.10.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.4"
|
"lodash": "^4.17.4",
|
||||||
|
"string-width": "^2.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import charWidthInTTY from 'string-width';
|
||||||
|
|
||||||
import utils from 'utils';
|
import utils from 'utils';
|
||||||
|
|
||||||
// Handle commands from tabs, like sending a frame or information about
|
// Handle commands from tabs, like sending a frame or information about
|
||||||
|
@ -43,13 +45,15 @@ export default (MixinBase) => class extends MixinBase {
|
||||||
_buildTTYRow(text) {
|
_buildTTYRow(text) {
|
||||||
let char;
|
let char;
|
||||||
let row = "";
|
let row = "";
|
||||||
for (let index = 0; index < this.tty_width; index++) {
|
let index = 0;
|
||||||
|
while (index < this.tty_width) {
|
||||||
if (index < text.length) {
|
if (index < text.length) {
|
||||||
char = text[index];
|
char = text[index];
|
||||||
} else {
|
} else {
|
||||||
char = " "
|
char = " "
|
||||||
}
|
}
|
||||||
row += utils.ttyPixel([255, 255, 255], [0, 0, 0], char);
|
row += utils.ttyPixel([255, 255, 255], [0, 0, 0], char);
|
||||||
|
index += charWidthInTTY(char);
|
||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import charWidthInTTY from 'string-width';
|
||||||
|
|
||||||
import utils from 'utils';
|
import utils from 'utils';
|
||||||
import BaseBuilder from 'dom/base_builder';
|
import BaseBuilder from 'dom/base_builder';
|
||||||
import GraphicsBuilder from 'dom/graphics_builder';
|
import GraphicsBuilder from 'dom/graphics_builder';
|
||||||
|
@ -233,21 +235,47 @@ export default class FrameBuilder extends BaseBuilder{
|
||||||
// However we can't just write random pixels to a TTY screen, we must collate 2 rows
|
// However we can't just write random pixels to a TTY screen, we must collate 2 rows
|
||||||
// of native pixels for every row of the terminal.
|
// of native pixels for every row of the terminal.
|
||||||
_buildTtyRow(bg_row, fg_row, y) {
|
_buildTtyRow(bg_row, fg_row, y) {
|
||||||
let tty_index, char;
|
let tty_index, char, x_shoved;
|
||||||
let row = "";
|
let row = "";
|
||||||
|
let char_width_debt = 0;
|
||||||
const tty_row = parseInt(y / 2);
|
const tty_row = parseInt(y / 2);
|
||||||
for (let x = 0; x < this.frame_width; x++) {
|
for (let x = 0; x < this.frame_width; x++) {
|
||||||
|
if (x + char_width_debt >= this.frame_width) {
|
||||||
|
// Ideally this shouldn't happen because the CSS 'should' deal with wide
|
||||||
|
// characters.
|
||||||
|
break;
|
||||||
|
}
|
||||||
tty_index = (tty_row * this.frame_width) + x;
|
tty_index = (tty_row * this.frame_width) + x;
|
||||||
if (this._doesCellHaveACharacter(tty_index)) {
|
if (this._doesCellHaveACharacter(tty_index)) {
|
||||||
char = this.formatted_text[tty_index];
|
char = this.formatted_text[tty_index];
|
||||||
|
char_width_debt = this._calculateCharWidthDebt(char_width_debt, char[0]);
|
||||||
|
// Don't display a wide character in the final column
|
||||||
|
if (x + char_width_debt >= this.frame_width) char[0] = ' ';
|
||||||
row += utils.ttyPixel(char[1], char[2], char[0]);
|
row += utils.ttyPixel(char[1], char[2], char[0]);
|
||||||
} else {
|
} else {
|
||||||
row += utils.ttyPixel(fg_row[x], bg_row[x], '▄');
|
// Wide characters take up more than one cell, so we might not always be
|
||||||
|
// iterating by 1.
|
||||||
|
x_shoved = x + char_width_debt;
|
||||||
|
row += utils.ttyPixel(fg_row[x_shoved], bg_row[x_shoved], '▄');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deal with UTF8 characters that take up more than a single cell in the TTY.
|
||||||
|
// TODO:
|
||||||
|
// 1. Do all terminals deal with wide characters the same?
|
||||||
|
// 2. Use CSS or JS so that wide characters actually flow in the DOM as 2
|
||||||
|
// monospaced characters. This will allow pages of nothing but wide
|
||||||
|
// characters to properly display.
|
||||||
|
_calculateCharWidthDebt(current_debt, char) {
|
||||||
|
const char_width_in_tty = charWidthInTTY(char);
|
||||||
|
if (char_width_in_tty > 1) {
|
||||||
|
current_debt += char_width_in_tty - 1;
|
||||||
|
}
|
||||||
|
return current_debt;
|
||||||
|
}
|
||||||
|
|
||||||
// We need to know this because we want all empty cells to be 'transparent'
|
// We need to know this because we want all empty cells to be 'transparent'
|
||||||
_doesCellHaveACharacter(index) {
|
_doesCellHaveACharacter(index) {
|
||||||
if (this.formatted_text[index] === undefined) return false;
|
if (this.formatted_text[index] === undefined) return false;
|
||||||
|
|
|
@ -172,6 +172,7 @@ export default class TextBuillder extends BaseBuilder {
|
||||||
// real browser.
|
// real browser.
|
||||||
_formatTextForTTYGrid(text, dom_rects) {
|
_formatTextForTTYGrid(text, dom_rects) {
|
||||||
let col, tty_box, step, character, previous_box, origin;
|
let col, tty_box, step, character, previous_box, origin;
|
||||||
|
this.char_width_debt = 0
|
||||||
let character_index = 0;
|
let character_index = 0;
|
||||||
for (const box of dom_rects) {
|
for (const box of dom_rects) {
|
||||||
if (this._isBoxOutsideViewport(box)) return;
|
if (this._isBoxOutsideViewport(box)) return;
|
||||||
|
@ -188,9 +189,9 @@ export default class TextBuillder extends BaseBuilder {
|
||||||
for (step = 0; step < tty_box.width; step++) {
|
for (step = 0; step < tty_box.width; step++) {
|
||||||
character = text.charAt(character_index);
|
character = text.charAt(character_index);
|
||||||
this._placeCharacterOnTTYGrid(col, tty_box.row, origin, character);
|
this._placeCharacterOnTTYGrid(col, tty_box.row, origin, character);
|
||||||
col++;
|
|
||||||
origin.x = origin.x + this.char_width;
|
origin.x = origin.x + this.char_width;
|
||||||
character_index++;
|
character_index++;
|
||||||
|
col++
|
||||||
}
|
}
|
||||||
previous_box = box;
|
previous_box = box;
|
||||||
}
|
}
|
||||||
|
@ -213,7 +214,6 @@ export default class TextBuillder extends BaseBuilder {
|
||||||
|
|
||||||
_placeCharacterOnTTYGrid(col, row, original_position, character) {
|
_placeCharacterOnTTYGrid(col, row, original_position, character) {
|
||||||
const index = (row * this.tty_dom_width) + col;
|
const index = (row * this.tty_dom_width) + col;
|
||||||
if (this._isExistingCharacter(index)) return;
|
|
||||||
if (this._isCharOutsideGrid(col, row)) return;
|
if (this._isCharOutsideGrid(col, row)) return;
|
||||||
const colours = this._getCharacterColours(original_position);
|
const colours = this._getCharacterColours(original_position);
|
||||||
if (!colours) return;
|
if (!colours) return;
|
||||||
|
|
Loading…
Reference in a new issue