Add alignment to text display
This commit is contained in:
parent
2bedecb677
commit
a571d6cc2a
|
|
@ -138,6 +138,11 @@ export enum Glyphs {
|
||||||
DIAMOND = '♦',
|
DIAMOND = '♦',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AlignHorizontal {
|
||||||
|
LEFT = 'left',
|
||||||
|
RIGHT = 'right',
|
||||||
|
}
|
||||||
|
|
||||||
export const isVertical = (char: string) => char === Glyphs.SINGLE_VERTICAL || char === Glyphs.DOUBLE_VERTICAL;
|
export const isVertical = (char: string) => char === Glyphs.SINGLE_VERTICAL || char === Glyphs.DOUBLE_VERTICAL;
|
||||||
export const isHorizontal = (char: string) => char === Glyphs.SINGLE_HORIZONTAL || char === Glyphs.DOUBLE_HORIZONTAL;
|
export const isHorizontal = (char: string) => char === Glyphs.SINGLE_HORIZONTAL || char === Glyphs.DOUBLE_HORIZONTAL;
|
||||||
export const isCorner = (char: string) => [
|
export const isCorner = (char: string) => [
|
||||||
|
|
@ -145,7 +150,14 @@ export const isCorner = (char: string) => [
|
||||||
Glyphs.DOUBLE_TOP_LEFT, Glyphs.DOUBLE_TOP_RIGHT, Glyphs.DOUBLE_BOTTOM_LEFT, Glyphs.DOUBLE_BOTTOM_RIGHT,
|
Glyphs.DOUBLE_TOP_LEFT, Glyphs.DOUBLE_TOP_RIGHT, Glyphs.DOUBLE_BOTTOM_LEFT, Glyphs.DOUBLE_BOTTOM_RIGHT,
|
||||||
].includes(char as Glyphs);
|
].includes(char as Glyphs);
|
||||||
|
|
||||||
interface BoxOptions {
|
interface StringOptions {
|
||||||
|
fg?: ColorLike;
|
||||||
|
bg?: ColorLike;
|
||||||
|
alignH?: AlignHorizontal;
|
||||||
|
anchorH?: AlignHorizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BoxOptions extends StringOptions {
|
||||||
vertical?: string;
|
vertical?: string;
|
||||||
horizontal?: string;
|
horizontal?: string;
|
||||||
topLeft?: string;
|
topLeft?: string;
|
||||||
|
|
@ -153,8 +165,6 @@ interface BoxOptions {
|
||||||
bottomLeft?: string;
|
bottomLeft?: string;
|
||||||
bottomRight?: string;
|
bottomRight?: string;
|
||||||
fill?: Char;
|
fill?: Char;
|
||||||
fg?: ColorLike;
|
|
||||||
bg?: ColorLike;
|
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,9 +323,14 @@ export class TextDisplay {
|
||||||
return [this.chars[y * this.width + x], this.fgs[y * this.width + x], this.bgs[y * this.width + x]];
|
return [this.chars[y * this.width + x], this.fgs[y * this.width + x], this.bgs[y * this.width + x]];
|
||||||
}
|
}
|
||||||
|
|
||||||
setRegion(x: number, y: number, region: TextRegion) {
|
setRegion(region: TextRegion, x: number, y: number, anchorH = AlignHorizontal.LEFT) {
|
||||||
x = x | 0;
|
x = x | 0;
|
||||||
y = y | 0;
|
y = y | 0;
|
||||||
|
|
||||||
|
if (anchorH === AlignHorizontal.RIGHT) {
|
||||||
|
x -= region.width + 1;
|
||||||
|
}
|
||||||
|
|
||||||
const { chars, fgs, bgs } = region[REGION_DATA];
|
const { chars, fgs, bgs } = region[REGION_DATA];
|
||||||
const rw = region.width;
|
const rw = region.width;
|
||||||
const x0 = Math.max(this.clipLeft, x);
|
const x0 = Math.max(this.clipLeft, x);
|
||||||
|
|
@ -375,15 +390,31 @@ export class TextDisplay {
|
||||||
return new TextRegion(data);
|
return new TextRegion(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawString(text: unknown, x: number, y: number, fg: ColorLike = DEFAULT_FG, bg: ColorLike = DEFAULT_BG) {
|
drawString(
|
||||||
|
text: unknown, x: number, y: number,
|
||||||
|
options: StringOptions,
|
||||||
|
) {
|
||||||
x = x | 0; y = y | 0;
|
x = x | 0; y = y | 0;
|
||||||
|
const {
|
||||||
|
fg = DEFAULT_FG,
|
||||||
|
bg = DEFAULT_BG,
|
||||||
|
alignH = AlignHorizontal.LEFT,
|
||||||
|
anchorH = AlignHorizontal.LEFT,
|
||||||
|
} = options;
|
||||||
const lines = String(text).split('\n');
|
const lines = String(text).split('\n');
|
||||||
|
const maxWidth = lines.reduce((m, line) => Math.max(m, line.length), 0);
|
||||||
|
|
||||||
|
if (anchorH === AlignHorizontal.RIGHT) {
|
||||||
|
x -= maxWidth - 1;
|
||||||
|
}
|
||||||
|
|
||||||
for (let row = 0; row < lines.length; row++) {
|
for (let row = 0; row < lines.length; row++) {
|
||||||
const line = lines[row];
|
let line = lines[row];
|
||||||
const ry = y + row;
|
const ry = y + row;
|
||||||
if (ry < this.clipTop || ry >= this.clipBottom) continue;
|
if (ry < this.clipTop || ry >= this.clipBottom) continue;
|
||||||
|
const offset = alignH === AlignHorizontal.RIGHT ? maxWidth - line.length : 0;
|
||||||
for (let col = 0; col < line.length; col++) {
|
for (let col = 0; col < line.length; col++) {
|
||||||
this.setCharRaw(x + col, ry, line[col], fg, bg);
|
this.setCharRaw(x + col + offset, ry, line[col], fg, bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -428,8 +459,13 @@ export class TextDisplay {
|
||||||
bg = DEFAULT_BG,
|
bg = DEFAULT_BG,
|
||||||
fill,
|
fill,
|
||||||
title,
|
title,
|
||||||
|
anchorH = AlignHorizontal.LEFT,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
|
if (anchorH === AlignHorizontal.RIGHT) {
|
||||||
|
x -= width + 1;
|
||||||
|
}
|
||||||
|
|
||||||
this.setCharRaw(x, y, topLeft, fg, bg);
|
this.setCharRaw(x, y, topLeft, fg, bg);
|
||||||
this.setCharRaw(x + width + 1, y, topRight, fg, bg);
|
this.setCharRaw(x + width + 1, y, topRight, fg, bg);
|
||||||
this.setCharRaw(x, y + height + 1, bottomLeft, fg, bg);
|
this.setCharRaw(x, y + height + 1, bottomLeft, fg, bg);
|
||||||
|
|
@ -446,7 +482,7 @@ export class TextDisplay {
|
||||||
this.drawVLine(x + width + 1, y + 1, y + height, [vertical, fg, bg]);
|
this.drawVLine(x + width + 1, y + 1, y + height, [vertical, fg, bg]);
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
this.drawString(title, x + 1, y, fg, bg);
|
this.drawString(title, x + 1, y, { fg, bg });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -455,13 +491,26 @@ export class TextDisplay {
|
||||||
const {
|
const {
|
||||||
fg = DEFAULT_FG,
|
fg = DEFAULT_FG,
|
||||||
bg = DEFAULT_BG,
|
bg = DEFAULT_BG,
|
||||||
|
alignH = AlignHorizontal.LEFT,
|
||||||
|
anchorH = AlignHorizontal.LEFT,
|
||||||
|
title,
|
||||||
} = options;
|
} = options;
|
||||||
const lines = String(text).split('\n');
|
const lines = String(text).split('\n');
|
||||||
const width = lines.reduce((m, line) => Math.max(m, line.length), 0);
|
const textWidth = lines.reduce((m, line) => Math.max(m, line.length), 0);
|
||||||
const height = lines.length;
|
const height = lines.length;
|
||||||
|
|
||||||
|
let width = textWidth;
|
||||||
|
if (title) {
|
||||||
|
width = Math.max(width, title.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dx = anchorH === AlignHorizontal.RIGHT ? -1 : 1;
|
||||||
|
if (alignH === AlignHorizontal.LEFT && anchorH === AlignHorizontal.RIGHT) {
|
||||||
|
dx -= width - textWidth;
|
||||||
|
}
|
||||||
|
|
||||||
this.drawBox(x, y, width, height, { ...options, fill: ' ' });
|
this.drawBox(x, y, width, height, { ...options, fill: ' ' });
|
||||||
this.drawString(text, x + 1, y + 1, fg, bg);
|
this.drawString(text, x + dx, y + 1, { fg, bg, alignH, anchorH });
|
||||||
}
|
}
|
||||||
|
|
||||||
fillBox(x: number, y: number, width: number, height: number, char: Char = Glyphs.FULL_BLOCK) {
|
fillBox(x: number, y: number, width: number, height: number, char: Char = Glyphs.FULL_BLOCK) {
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ export class TextDisplaySystem extends System {
|
||||||
const region = data instanceof TextRegion ? data : new TextRegion(data);
|
const region = data instanceof TextRegion ? data : new TextRegion(data);
|
||||||
|
|
||||||
if (absolute || !offset || !viewportClipRect) {
|
if (absolute || !offset || !viewportClipRect) {
|
||||||
this.display.setRegion(x, y, region);
|
this.display.setRegion(region, x, y);
|
||||||
} else {
|
} else {
|
||||||
const clipRect = this.display.getClipRect();
|
const clipRect = this.display.getClipRect();
|
||||||
this.display.setClipRect(viewportClipRect);
|
this.display.setClipRect(viewportClipRect);
|
||||||
this.display.setRegion(x - offset.x, y - offset.y, region);
|
this.display.setRegion(region, x - offset.x, y - offset.y);
|
||||||
this.display.setClipRect(clipRect);
|
this.display.setClipRect(clipRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue