Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1279780
feat: change DrawObject naming scheme to Painter
Im-Beast Aug 4, 2023
d6a8183
perf: precalculate data used in rerender() in signals
Im-Beast Aug 4, 2023
5592eb0
feat: remove view checks in queueRerender
Im-Beast Aug 4, 2023
618f9f1
feat!: replace render() and rerender() methods with paint()
Im-Beast Aug 6, 2023
e2f9d65
fix: mark TextPainter as rendered if it got painted
Im-Beast Aug 6, 2023
28470d3
feat!: rename `rendered` to `painted`
Im-Beast Aug 6, 2023
f0c648a
chore: remove finished todog
Im-Beast Aug 8, 2023
1c6dba4
feat!: start reworking TextPainter
Im-Beast Aug 8, 2023
51dc4ef
feat(wip): adapt components to new TextPainter
Im-Beast Aug 8, 2023
f10ade0
chore: ignore ts errors for `for in` operation on string
Im-Beast Aug 8, 2023
751db33
feat: add splitToArray and cloneArrayContents utils that will come in…
Im-Beast Aug 10, 2023
20a51ef
feat(wip): support rendering plain string in TextPainter
Im-Beast Aug 10, 2023
aee7223
fix: adapt updateMovement() for TextPainter
Im-Beast Aug 10, 2023
945ee7f
feat: add warning about string TextPainter not supporting newlines
Im-Beast Aug 10, 2023
63a8e3b
fix: fix trying to render negative coordinates
Im-Beast Aug 10, 2023
b7c40e0
fix: fix type errors
Im-Beast Aug 12, 2023
196fece
feat(wip): multiline TextPainter
Im-Beast Aug 14, 2023
acfdf95
feat(wip): TextPainter text alignment
Im-Beast Aug 16, 2023
a76b792
feat: TextPainter text alignment
Im-Beast Aug 16, 2023
322d2f8
feat: add support for overwriteRectangle
Im-Beast Aug 16, 2023
d6bd7f4
chore: remove unused util functions
Im-Beast Aug 16, 2023
6251ad4
feat: add support for multi code point characters
Im-Beast Aug 16, 2023
7d31aee
chore: add FIXME notes
Im-Beast Aug 16, 2023
7521ebd
perf: minor TextPainter optimizations
Im-Beast Aug 17, 2023
bd1af7c
fix: update TextPainter when text changes
Im-Beast Aug 17, 2023
699d6b4
feat(wip): work towards variable character width support in TextPainter
Im-Beast Aug 18, 2023
7669e39
feat: make variable character width support actually okay in TextPainter
Im-Beast Aug 23, 2023
3a20d87
chore: remove unused import
Im-Beast Aug 23, 2023
0440df0
feat: automatically detect multiCodePointSupport
Im-Beast Aug 23, 2023
c530ccf
feat: remove error about changing typeof text
Im-Beast Aug 23, 2023
48d873a
fix: TextPainter rendering text even after it's erased
Im-Beast Aug 24, 2023
9b3a561
fix: elements inside view not being properly rendered on first frame
Im-Beast Aug 24, 2023
35bba22
feat: refactor to only use string[] as TextType
Im-Beast Aug 24, 2023
ddc687f
feat(wip): rewrite components to use new TextPainter properly
Im-Beast Aug 24, 2023
a98a396
feat: rewrite `ProgressBar` to take advantage of TextPainter
Im-Beast Sep 3, 2023
c79a271
feat(TextPainter): add alignment enums
Im-Beast Sep 8, 2023
76bbc9b
fix(TextPainter): fix crash when currentText is shorter than text
Im-Beast Sep 8, 2023
5781ead
feat(wip): progress on converting components to use TextPainter
Im-Beast Sep 8, 2023
54e6eba
feat(TextPainter): improve support for strings containing ansi sequences
Im-Beast Sep 15, 2023
f3c20f4
feat: simplify `Rectangle` types, adapt `Button` and `Label` to TextP…
Im-Beast Sep 15, 2023
98299bc
fix: append reset to characters style in reapplyCharacterStyles()
Im-Beast Sep 16, 2023
b3cc545
fix: don't flush partial styles in reapplyCharacterSTyles
Im-Beast Sep 16, 2023
de559ef
tests: add regression test for reapplyCharacterStyles
Im-Beast Sep 16, 2023
3796d5f
feat: make `style` and `theme` optional
Im-Beast Sep 16, 2023
b792aa8
fix: fix objects under sometimes not being rerendered
Im-Beast Sep 16, 2023
7082b0c
feat(TextPainter): simplify rendering logic
Im-Beast Sep 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions examples/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ProgressBar } from "../src/components/progressbar.ts";

import { Theme } from "../src/theme.ts";
import { View } from "../src/view.ts";
import { Component, Rectangle } from "../mod.ts";
import { Component, HorizontalAlign, Rectangle, VerticalAlign } from "../mod.ts";
import { TextBox } from "../src/components/textbox.ts";
import { Computed, Signal } from "../src/signals/mod.ts";

Expand Down Expand Up @@ -205,8 +205,8 @@ new ProgressBar({
new Label({
parent: tui,
align: {
horizontal: "center",
vertical: "center",
horizontal: HorizontalAlign.Center,
vertical: VerticalAlign.Middle,
},
rectangle: {
column: 17,
Expand Down Expand Up @@ -303,6 +303,7 @@ new TextBox({
base: crayon.bgLightBlue,
},
},
placeholder: "hello\nasdasd\nworld!",
lineNumbering: true,
lineHighlighting: true,
rectangle: {
Expand Down Expand Up @@ -443,19 +444,45 @@ const moveButton = new Button({
zIndex: 2,
});

const moveButton2 = new Button({
parent: tui,
rectangle: {
column: 2,
row: 16,
width: 18,
height: 2,
},
label: { text: "hello\nthere" },
theme: {
base: crayon.bgGreen,
focused: crayon.bgLightGreen,
active: crayon.bgMagenta,
},
zIndex: 2,
});

moveButton.on("mousePress", (event) => {
if (!event.drag) return;
const rectangle = moveButton.rectangle.value;
rectangle.column += event.movementX;
rectangle.row += event.movementY;
});

moveButton2.on("mousePress", (event) => {
if (!event.drag) return;
const rectangle = moveButton2.rectangle.value;
rectangle.column += event.movementX;
rectangle.row += event.movementY;
});

new Text({
parent: tui,
view,
rectangle: {
column: 2,
row: 13,
width: 0,
height: 0,
},
theme: baseTheme,
text: "wopa",
Expand Down Expand Up @@ -499,17 +526,16 @@ tui.canvas.on("render", () => {
const fps = new Signal(60);
let lastRender = 0;

const performanceStats = new Text({
const performanceStats = new Label({
parent: tui,
rectangle: { column: 0, row: 0 },
theme: baseTheme,
rectangle: { column: 0, row: 0, width: 0, height: 0 },
text: new Computed(() =>
`\
FPS: ${fps.value.toFixed(2)}\
| Components: ${tui.components.size}\
| Drawn objects: ${tui.canvas.drawnObjects.length}\
${crayon.bgRed.green(`FPS: ${fps.value.toFixed(2)}`)}\
| ${crayon.yellow(`Components: ${tui.components.size}`)}\
| Drawn objects: ${tui.canvas.painters.length}\
| Updated objects: ${tui.canvas.rerenderedObjects}\
| Press CTRL+F to toggle Frame/Label visibility`
| Press ${crayon.lightBlue.bold("CTRL+F")} to toggle Frame/Label visibility`
),
zIndex: 0,
});
Expand Down
99 changes: 0 additions & 99 deletions src/canvas/box.ts

This file was deleted.

55 changes: 32 additions & 23 deletions src/canvas/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { SortedArray } from "../utils/sorted_array.ts";
import { rectangleIntersection } from "../utils/numbers.ts";

import type { ConsoleSize, Stdout } from "../types.ts";
import { DrawObject } from "./draw_object.ts";
import { Signal, SignalOfObject } from "../signals/mod.ts";
import { Painter } from "./painter.ts";
import { Signal } from "../signals/mod.ts";
import { signalify } from "../utils/signals.ts";

const textEncoder = new TextEncoder();
Expand All @@ -18,27 +18,31 @@ const textEncoder = new TextEncoder();
export interface CanvasOptions {
/** Stdout to which canvas will render frameBuffer */
stdout: Stdout;
size: ConsoleSize | SignalOfObject<ConsoleSize>;
size: ConsoleSize | Signal<ConsoleSize>;
}

/** Map that contains events that {Canvas} can dispatch */
export type CanvasEventMap = {
render: EmitterEvent<[]>;
};

interface Drawable {
draw(row: number, column: number, data: string): void;
}

/**
* Object, which stores data about currently rendered objects.
*
* It is responsible for outputting to stdout.
*/
export class Canvas extends EventEmitter<CanvasEventMap> {
export class Canvas extends EventEmitter<CanvasEventMap> implements Drawable {
stdout: Stdout;
size: Signal<ConsoleSize>;
rerenderedObjects?: number;
frameBuffer: (string | Uint8Array)[][];
frameBuffer: string[][];
rerenderQueue: Set<number>[];
drawnObjects: SortedArray<DrawObject>;
updateObjects: DrawObject[];
painters: SortedArray<Painter>;
updateObjects: Painter[];
resizeNeeded: boolean;

constructor(options: CanvasOptions) {
Expand All @@ -47,7 +51,7 @@ export class Canvas extends EventEmitter<CanvasEventMap> {
this.frameBuffer = [];
this.rerenderQueue = [];
this.stdout = options.stdout;
this.drawnObjects = new SortedArray((a, b) => a.zIndex.peek() - b.zIndex.peek() || a.id - b.id);
this.painters = new SortedArray((a, b) => a.zIndex.peek() - b.zIndex.peek() || a.id - b.id);
this.updateObjects = [];
this.resizeNeeded = false;

Expand All @@ -58,20 +62,28 @@ export class Canvas extends EventEmitter<CanvasEventMap> {
});
}

draw(row: number, column: number, data: string): void {
this.frameBuffer[row] ??= [];
this.rerenderQueue[row] ??= new Set();

this.frameBuffer[row][column] = data;
this.rerenderQueue[row].add(column);
}

resize() {
const { columns, rows } = this.size.peek();

for (const drawObject of this.drawnObjects) {
for (const drawObject of this.painters) {
const { column, row } = drawObject.rectangle.peek();
if (column >= columns || row >= rows) continue;

drawObject.rendered = false;
drawObject.painted = false;
drawObject.updated = false;
this.updateObjects.push(drawObject);
}
}

updateIntersections(object: DrawObject): void {
updateIntersections(object: Painter): void {
const { omitCells, objectsUnder } = object;

const zIndex = object.zIndex.peek();
Expand All @@ -83,7 +95,7 @@ export class Canvas extends EventEmitter<CanvasEventMap> {

objectsUnder.clear();

for (const object2 of this.drawnObjects) {
for (const object2 of this.painters) {
if (object === object2 || object2.outOfBounds) continue;

const zIndex2 = object2.zIndex.peek();
Expand All @@ -99,12 +111,14 @@ export class Canvas extends EventEmitter<CanvasEventMap> {

if (!intersection) continue;

const rowStart = intersection.row;
const rowRange = intersection.row + intersection.height;
const columnStart = intersection.column;
const columnRange = intersection.column + intersection.width;
for (let row = intersection.row; row < rowRange; ++row) {
for (let row = rowStart; row < rowRange; ++row) {
const omitColumns = omitCells[row] ??= new Set();

for (let column = intersection.column; column < columnRange; ++column) {
for (let column = columnStart; column < columnRange; ++column) {
omitColumns.add(column);
}
}
Expand All @@ -130,7 +144,6 @@ export class Canvas extends EventEmitter<CanvasEventMap> {
if (object.updated) continue;
object.updated = true;
++i;
object.update();

object.updateMovement();
object.updatePreviousRectangle();
Expand All @@ -146,12 +159,7 @@ export class Canvas extends EventEmitter<CanvasEventMap> {
object.moved = false;
}

if (object.rendered) {
object.rerender();
} else {
object.render();
object.rendered = true;
}
object.paint();
}

this.rerenderedObjects = i;
Expand All @@ -165,9 +173,10 @@ export class Canvas extends EventEmitter<CanvasEventMap> {
let lastColumn = -1;

const { rerenderQueue } = this;
const size = this.size.peek();
const { rows } = this.size.peek();

for (let row = 0; row < size.rows; ++row) {
// TODO: try to attach moveCursor while rendering in Painter instead of there
for (let row = 0; row < rows; ++row) {
const columns = rerenderQueue[row];
if (!columns?.size) continue;

Expand Down
6 changes: 3 additions & 3 deletions src/canvas/mod.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 Im-Beast. All rights reserved. MIT license.
export * from "./box.ts";
export * from "./text.ts";
export * from "./canvas.ts";
export * from "./draw_object.ts";
export * from "./painter.ts";

export * from "./painters/mod.ts";
Loading