ascii-ui
Version:
Graphic terminal emulator for HTML canvas elements
137 lines • 5.65 kB
JavaScript
import { Widget } from '../Widget';
import { clamp } from '../util/clamp';
import { coalesce } from '../util/coalesce';
import { deepAssign } from '../util/deepAssign';
import { noWrap, splitText, tokenizer } from '../util/tokenizer';
export class Text extends Widget {
constructor(terminal, options, parent) {
super(terminal, deepAssign({}, Text.defaultOptions, options), parent);
this.startLine = 0;
this.typewritterLine = 0;
this.typewritterColumn = 0;
this.render();
}
render() {
if (!this.splittedText || this.startLine === undefined) {
return;
}
if (this.options.textStyle) {
this.terminal.setTextStyle(this.options.textStyle);
}
clearTimeout(this.typewritterTimer);
const typewritterEnabled = this.options.typewritterDelay > 0;
const terminalColumn = this.options.col;
let terminalLine = this.options.line;
let lastLine = Math.min(this.startLine + this.options.height, this.splittedText.length);
const completedLines = typewritterEnabled ? Math.min(this.typewritterLine, lastLine) : lastLine;
let line = this.startLine;
while (line < completedLines) {
this.terminal.setText(this.splittedText[line], terminalColumn, terminalLine);
line++;
terminalLine++;
}
lastLine = this.options.line + this.options.height;
if (terminalLine >= lastLine) {
return;
}
if (this.typewritterColumn > 0) {
const text = this.splittedText[line].substring(0, this.typewritterColumn)
+ ' '.repeat(this.options.width - this.typewritterColumn);
this.terminal.setText(text, terminalColumn, terminalLine);
terminalLine++;
}
const emptyLine = ' '.repeat(this.options.width);
while (terminalLine < lastLine) {
this.terminal.setText(emptyLine, terminalColumn, terminalLine);
terminalLine++;
}
if (typewritterEnabled && this.typewritterLine - this.startLine < this.options.height) {
this.typewritterTimer = setTimeout(this.renderInProgressText.bind(this), this.options.typewritterDelay, terminalColumn, this.options.line);
}
}
getTextSize() {
return {
columns: this.options.tokenizer ? this.splittedText[0].length : this.options.text.length,
rows: this.splittedText.length,
};
}
setScrollLine(line) {
clearTimeout(this.typewritterTimer);
const currentOffset = this.startLine;
const maxLine = this.options.fitPageEnd
? this.splittedText.length - this.options.height
: this.splittedText.length - 1;
this.startLine = clamp(line, 0, maxLine);
if (currentOffset !== this.startLine) {
const oldTypewritterLine = this.typewritterLine;
this.typewritterLine = clamp(this.typewritterLine, this.startLine, this.options.persistentTypewritter ? this.splittedText.length : maxLine);
if (oldTypewritterLine !== this.typewritterLine) {
this.typewritterColumn = 0;
}
}
this.render();
return this.startLine < maxLine;
}
scrollLines(lines) {
return this.setScrollLine(this.startLine + lines);
}
scrollPages(pages) {
return this.setScrollLine(this.startLine + pages * this.options.height);
}
updateOptions(changes) {
const dirtyText = coalesce(changes.tokenizer, changes.text, changes.width, changes.skip) !== undefined;
const redraw = dirtyText || coalesce(changes.col, changes.line) !== undefined;
if (changes.skip !== undefined) {
this.options.skip = clamp(changes.skip, 0, this.options.text.length);
}
if (dirtyText) {
this.splittedText = this.splitText(this.options.text);
}
if (redraw) {
this.render();
}
}
renderInProgressText(textCol, textLine) {
const line = this.splittedText[this.typewritterLine];
if (!line) {
return;
}
const partialText = line[this.typewritterColumn];
const x = textCol + this.typewritterColumn;
const y = textLine + this.typewritterLine - this.startLine;
if (!this.isAt(x, y)) {
return;
}
this.terminal.setText(partialText, x, y);
this.typewritterColumn++;
if (this.typewritterColumn >= this.splittedText[this.typewritterLine].trim().length) {
this.typewritterColumn = 0;
this.typewritterLine++;
}
if (this.typewritterLine - this.startLine < this.options.height
&& this.typewritterLine < this.splittedText.length) {
this.typewritterTimer = setTimeout(this.renderInProgressText.bind(this), this.options.typewritterDelay, textCol, textLine);
}
}
splitText(text) {
if (!this.allocated) {
return;
}
const txt = this.options.skip ? text.substr(this.options.skip) : text;
if (!this.options.tokenizer) {
const noWrappedText = noWrap(txt, this.options.width, this.options.ellipsis);
return [noWrappedText + ' '.repeat(this.options.width - noWrappedText.length)];
}
return splitText(txt, this.options.width, this.options.tokenizer);
}
}
Text.defaultOptions = {
tokenizer,
text: '',
ellipsis: '...',
skip: 0,
fitPageEnd: false,
typewritterDelay: 0,
persistentTypewritter: true,
};
//# sourceMappingURL=Text.js.map