cc-haxe/src/lib/ui/Canvas.hx
2023-02-05 02:56:04 +01:00

198 lines
3.8 KiB
Haxe

package lib.ui;
import kernel.ui.WindowContext;
import lib.Pos;
import lib.Rect;
import kernel.ui.Pixel;
abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> from Array<Array<Pixel>>{
inline public function new() {
this = [[]];
}
public inline function set(i:Pos, pixel:Pixel) {
if (this[i.y] == null) {
this[i.y] = [];
}
this[i.y][i.x] = pixel;
}
public inline function get(i:Pos):Null<Pixel> {
if (this[i.y] == null) {
return null;
}
return this[i.y][i.x];
}
public function keyValueIterator():KeyValueIterator<Pos, Pixel> {
return new CanvasKeyValueIterator(this);
}
public function combine(other:Canvas, offset:Pos) {
for (key => value in other) {
if (value == null) {
continue;
}
var y = offset.y + key.y;
var x = offset.x + key.x;
if (this[y] == null) {
this[y] = [];
}
this[y][x] = value;
}
}
public function height():Int {
return this.length;
}
public function maxWidth() {
var max = 0;
for (i in 0...this.length) {
if (this[i] != null && this[i].length > max) {
max = this[i].length;
}
}
return max;
}
public function getBounds(): Rect {
return new Rect({x:0,y:0},{
x: maxWidth() - 1,
y: height() - 1
});
}
/**
Renders the canvas directly to the context
**/
public function renderToContext(ctx: WindowContext){
var lastBgColor: Color = null;
var lastTextColor: Color = null;
for (lineIndex => line in this) {
if (line == null || line.length == 0) {
// Line is empty
continue;
}
ctx.setCursorPos(0, lineIndex);
var pritableLine = "";
for (pixelIndex => pixel in line) {
if (pixel == null) {
// If the pixel is null we need to skip it with setCurosrPos.
// Otherwise we will print an empty space with bg color
ctx.write(pritableLine);
pritableLine = "";
ctx.setCursorPos(pixelIndex + 1, lineIndex);
continue;
}
if (pixel.bg != lastBgColor) {
// The background color has changed, we need to print the current line and set the new background color
ctx.write(pritableLine);
pritableLine = "";
// Set the new background color
ctx.setBackgroundColor(pixel.bg);
lastBgColor = pixel.bg;
}
// Same as above but for the text color
if (pixel.textColor != lastTextColor) {
// The text color has changed, we need to print the current line and set the new text color
ctx.write(pritableLine);
pritableLine = "";
// Set the new text color
ctx.setTextColor(pixel.textColor);
lastTextColor = pixel.textColor;
}
pritableLine += pixel.char;
}
ctx.write(pritableLine);
}
}
}
class CanvasKeyValueIterator {
private final canvas:Array<Array<Pixel>> ;
private var index:Null<Pos> = {x: 0, y: 0};
private var nextIndex:Null<Pos> = null;
@:allow(lib.ui.Canvas)
private function new(canvas:Array<Array<Pixel>>) {
this.canvas = canvas;
if (!isValidPos(this.index)){
this.index = nextValidPixel();
}
this.nextIndex = nextValidPixel();
}
private function isValidPos(pos: Pos): Bool {
if (this.canvas[pos.y] == null) {
return false;
}
if (this.canvas[pos.y][pos.x] == null) {
return false;
}
return true;
}
public function hasNext():Bool {
return this.index != null;
}
private function nextValidPixel(): Null<Pos> {
if (this.index == null) {
return null;
}
var startX = this.index.x + 1;
for (y in this.index.y...this.canvas.length){
if (this.canvas[y] == null) {
continue;
}
for (x in startX...(this.canvas[y].length)){
if (this.canvas[y][x] == null) {
continue;
}
return {x: x, y: y};
}
startX = 0;
}
return null;
}
public function next():{key:Pos, value:Pixel} {
var rtn = {
key: this.index,
value: this.canvas[this.index.y][this.index.x]
};
this.index = this.nextIndex;
this.nextIndex = nextValidPixel();
return rtn;
}
}