package lib.ui; import kernel.ui.WindowContext; import lib.Pos; import lib.Rect; import kernel.ui.Pixel; abstract Canvas(Array>) to Array> from Array>{ 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 { if (this[i.y] == null) { return null; } return this[i.y][i.x]; } public function keyValueIterator():KeyValueIterator { 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> ; private var index:Null = {x: 0, y: 0}; private var nextIndex:Null = null; @:allow(lib.ui.Canvas) private function new(canvas:Array>) { 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 { 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; } }