Difference between revisions of "ASCII-Render Tree for Layouting in Dart"
(initial) |
|||
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
== Dart Implementation of a ASCII-Render Tree == | == Dart Implementation of a ASCII-Render Tree == | ||
The code below demonstrates a render tree using an ASCII-Widget. | The code below demonstrates a render tree using an ASCII-Widget. An ASCII-Widget has other ASCII-Widget children, forming a tree. Rendering the root ACII-Widget returns a String. Which can be printed out on a console; or used as a source for rendering sprites in other UIs. | ||
An ASCII-Widget has other ASCII-Widget children, forming a tree. | |||
Rendering the root ACII-Widget returns a String. Which can be printed out on a console; or used as a source for rendering sprites in other UIs. | |||
This apporach uses three classes: | This apporach uses three classes: | ||
Line 15: | Line 11: | ||
2. <code>MatrixWidget</code>: An abstract class extending MatrixCanvas. It renders itself and its children. | 2. <code>MatrixWidget</code>: An abstract class extending MatrixCanvas. It renders itself and its children. | ||
Extend <code>MatrixWidget</code> to create custom Widgets. | Extend <code>MatrixWidget</code> to create custom Widgets and implement its method <code>void renderSelf()</code> to draw its ASCII content. | ||
== Example Result == | == Example Result == | ||
Line 46: | Line 42: | ||
== Usage == | == Usage == | ||
The code below generates the example depicted above. It creates custom Widgets that extend <code>MatrixWidget</code>. | The code below generates the example depicted above. It creates custom Widgets that extend <code>MatrixWidget</code>. The method <code>void renderSelf()</code> simply fill the Widget with one character. | ||
<syntaxhighlight lang="dart" line> | <syntaxhighlight lang="dart" line> | ||
Line 52: | Line 48: | ||
main() { | main() { | ||
final | final screenWidget = ScreenWidget(width: 40, height: 20, children: [ | ||
LeftWidget(width: 30, children: [ | |||
TopLineWidget(height: 1), | |||
BottomLineWidget(y: 20 - 1, height: 1) | |||
]), | |||
InventoryWidget(x: 30, width: 10, children: [ | |||
InventoryBelowWidget(y: 15, height: 5), | |||
]) | |||
]); | |||
// -------------------------------------------------------------------------- | // -------------------------------------------------------------------------- | ||
Line 105: | Line 66: | ||
// ============================================================================ | // ============================================================================ | ||
// | // WIDGETS | ||
// ============================================================================ | // ============================================================================ | ||
class ScreenWidget extends MatrixWidget { | class ScreenWidget extends MatrixWidget { | ||
ScreenWidget(super.x, super.y, super.width, super.height, | ScreenWidget({super.x, super.y, super.width, super.height, super.children}); | ||
@override | @override | ||
void renderSelf() => write(' ' * width * height); | void renderSelf() => write(' ' * width * height); | ||
Line 115: | Line 76: | ||
class LeftWidget extends MatrixWidget { | class LeftWidget extends MatrixWidget { | ||
LeftWidget(super.x, super.y, super.width, super.height, | LeftWidget({super.x, super.y, super.width, super.height, super.children}); | ||
@override | @override | ||
void renderSelf() => write(' ' * width * height); | void renderSelf() => write(' ' * width * height); | ||
Line 121: | Line 82: | ||
class TopLineWidget extends MatrixWidget { | class TopLineWidget extends MatrixWidget { | ||
TopLineWidget(super.x, super.y, super.width, super.height, | TopLineWidget({super.x, super.y, super.width, super.height, super.children}); | ||
@override | @override | ||
void renderSelf() => write('T' * width * height); | void renderSelf() => write('T' * width * height); | ||
Line 127: | Line 88: | ||
class InventoryWidget extends MatrixWidget { | class InventoryWidget extends MatrixWidget { | ||
InventoryWidget(super.x, super.y, super.width, super.height, | InventoryWidget( | ||
{super.x, super.y, super.width, super.height, super.children}); | |||
@override | @override | ||
void renderSelf() => write('I' * width * height); | void renderSelf() => write('I' * width * height); | ||
Line 134: | Line 95: | ||
class InventoryBelowWidget extends MatrixWidget { | class InventoryBelowWidget extends MatrixWidget { | ||
InventoryBelowWidget(super.x, super.y, super.width, super.height, | InventoryBelowWidget( | ||
{super.x, super.y, super.width, super.height, super.children}); | |||
@override | @override | ||
void renderSelf() => write('X' * width * height); | void renderSelf() => write('X' * width * height); | ||
Line 141: | Line 102: | ||
class BottomLineWidget extends MatrixWidget { | class BottomLineWidget extends MatrixWidget { | ||
BottomLineWidget(super.x, super.y, super.width, super.height, | BottomLineWidget( | ||
{super.x, super.y, super.width, super.height, super.children}); | |||
@override | @override | ||
void renderSelf() => write('B' * width * height); | void renderSelf() => write('B' * width * height); | ||
Line 163: | Line 124: | ||
final int y; | final int y; | ||
MatrixWidget(this.x, this.y, int width, int height, | MatrixWidget({ | ||
this.x = 0, | |||
this.y = 0, | |||
int width = 0, | |||
int height = 0, | |||
this.children = const [], | |||
}) : super(width, height); | |||
String renderWidgetTree() { | String renderWidgetTree() { | ||
super.resetCursorPosition(); | |||
renderSelf(); | renderSelf(); | ||
for (final child in children) { | for (final child in children) { | ||
if (child.width == 0) { | |||
child.width = width; | |||
} | |||
if (child.height == 0) { | |||
child.height = height; | |||
} | |||
child.renderWidgetTree(); | child.renderWidgetTree(); | ||
copyInto(child, child.x, child.y); | copyInto(child, child.x, child.y); | ||
Line 204: | Line 177: | ||
int _row = 0; | int _row = 0; | ||
int _col = 0; | int _col = 0; | ||
int _width; | |||
int _height; | |||
MatrixCanvas( | MatrixCanvas(this._width, this._height, {this.defaultTile = ' '}) { | ||
clearScreen(); | |||
} | } | ||
Line 213: | Line 188: | ||
// -------------------------------------------------------------------------- | // -------------------------------------------------------------------------- | ||
int get width => | int get width => _width; | ||
int get height => | int get height => _height; | ||
set width(int value) { | |||
_width = value; | |||
clearScreen(); | |||
} | |||
set height(int value) { | |||
_height = value; | |||
clearScreen(); | |||
} | |||
void resetCursorPosition() => _row = _col = 0; | |||
// -------------------------------------------------------------------------- | // -------------------------------------------------------------------------- | ||
Line 223: | Line 209: | ||
String paintMultipleLines() { | String paintMultipleLines() { | ||
final StringBuffer buffer = StringBuffer(); | final StringBuffer buffer = StringBuffer(); | ||
for (int y = 0; y < height; y++) { | for (int y = 0; y < height; y++) { | ||
Line 244: | Line 228: | ||
/// Copies [source] into this [MatriCanvas] at [x0] and [y0]. | /// Copies [source] into this [MatriCanvas] at [x0] and [y0]. | ||
void copyInto(MatrixCanvas source, int x0, int y0) { | void copyInto(MatrixCanvas source, int x0, int y0) { | ||
final | final sourceHeight = source._height; | ||
final | final sourceWidth = source.width; | ||
for (int y = 0; y < | for (int y = 0; y < sourceHeight; y++) { | ||
for (int x = 0; x < | for (int x = 0; x < sourceWidth; x++) { | ||
final tile = source. | final tile = source._map[y][x]; | ||
_map[y0 + y][x0 + x] = MatrixCanvasTile(tile.tile); | _map[y0 + y][x0 + x] = MatrixCanvasTile(tile.tile); | ||
} | } | ||
} | } | ||
} | } | ||
/// Recreates the matrix. | /// Recreates the matrix. | ||
void | void clearScreen() { | ||
_map.clear(); | _map.clear(); | ||
for (int y = 0; y < | for (int y = 0; y < _height; y++) { | ||
List<MatrixCanvasTile> tmp = []; | List<MatrixCanvasTile> tmp = []; | ||
for (int x = 0; x < width; x++) { | for (int x = 0; x < width; x++) { |
Latest revision as of 10:10, 5 November 2023
Dart Implementation of a ASCII-Render Tree
The code below demonstrates a render tree using an ASCII-Widget. An ASCII-Widget has other ASCII-Widget children, forming a tree. Rendering the root ACII-Widget returns a String. Which can be printed out on a console; or used as a source for rendering sprites in other UIs.
This apporach uses three classes:
1. MatrixCanvas
: A class being a two-dimensional map holding `MatrixCanvasTile`.
2. MatrixCanvasTile
: A class holding pure ASCII (and other info like colors).
2. MatrixWidget
: An abstract class extending MatrixCanvas. It renders itself and its children.
Extend MatrixWidget
to create custom Widgets and implement its method void renderSelf()
to draw its ASCII content.
Example Result
A typical layout with camera on the left, a top line, a bottom line and an inventory on the right.
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTIIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
IIIIIIIIII
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBXXXXXXXXXX
Usage
The code below generates the example depicted above. It creates custom Widgets that extend MatrixWidget
. The method void renderSelf()
simply fill the Widget with one character.
import 'democanvas.dart';
main() {
final screenWidget = ScreenWidget(width: 40, height: 20, children: [
LeftWidget(width: 30, children: [
TopLineWidget(height: 1),
BottomLineWidget(y: 20 - 1, height: 1)
]),
InventoryWidget(x: 30, width: 10, children: [
InventoryBelowWidget(y: 15, height: 5),
])
]);
// --------------------------------------------------------------------------
// PAINT ALL
// --------------------------------------------------------------------------
print(screenWidget.renderWidgetTree());
}
// ============================================================================
// WIDGETS
// ============================================================================
class ScreenWidget extends MatrixWidget {
ScreenWidget({super.x, super.y, super.width, super.height, super.children});
@override
void renderSelf() => write(' ' * width * height);
}
class LeftWidget extends MatrixWidget {
LeftWidget({super.x, super.y, super.width, super.height, super.children});
@override
void renderSelf() => write(' ' * width * height);
}
class TopLineWidget extends MatrixWidget {
TopLineWidget({super.x, super.y, super.width, super.height, super.children});
@override
void renderSelf() => write('T' * width * height);
}
class InventoryWidget extends MatrixWidget {
InventoryWidget(
{super.x, super.y, super.width, super.height, super.children});
@override
void renderSelf() => write('I' * width * height);
}
class InventoryBelowWidget extends MatrixWidget {
InventoryBelowWidget(
{super.x, super.y, super.width, super.height, super.children});
@override
void renderSelf() => write('X' * width * height);
}
class BottomLineWidget extends MatrixWidget {
BottomLineWidget(
{super.x, super.y, super.width, super.height, super.children});
@override
void renderSelf() => write('B' * width * height);
}
Dart Implementation
The class definitions used above.
// ============================================================================
// MATRIX WIDGET
// ============================================================================
/// Extend to build custom widget
abstract class MatrixWidget extends MatrixCanvas {
final List<MatrixWidget> children;
final int x;
final int y;
MatrixWidget({
this.x = 0,
this.y = 0,
int width = 0,
int height = 0,
this.children = const [],
}) : super(width, height);
String renderWidgetTree() {
super.resetCursorPosition();
renderSelf();
for (final child in children) {
if (child.width == 0) {
child.width = width;
}
if (child.height == 0) {
child.height = height;
}
child.renderWidgetTree();
copyInto(child, child.x, child.y);
}
return paintMultipleLines();
}
void renderSelf();
}
// ============================================================================
// MATRIX TILE
// ============================================================================
/// A data-holder for a single tile.
/// Add other attributes as needed like a color.
class MatrixCanvasTile {
String tile;
MatrixCanvasTile(this.tile);
String paint() => tile;
}
// ============================================================================
// MATRIX CANVAS
// ============================================================================
/// A 2-dimensional canvas to be painted on using [write];
class MatrixCanvas {
final List<List<MatrixCanvasTile>> _map = [];
final String defaultTile;
int _row = 0;
int _col = 0;
int _width;
int _height;
MatrixCanvas(this._width, this._height, {this.defaultTile = ' '}) {
clearScreen();
}
// --------------------------------------------------------------------------
//
// --------------------------------------------------------------------------
int get width => _width;
int get height => _height;
set width(int value) {
_width = value;
clearScreen();
}
set height(int value) {
_height = value;
clearScreen();
}
void resetCursorPosition() => _row = _col = 0;
// --------------------------------------------------------------------------
//
// --------------------------------------------------------------------------
String paintMultipleLines() {
final StringBuffer buffer = StringBuffer();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
buffer.write(_map[y][x].paint());
}
if (y < height - 1) {
buffer.write('\n');
}
}
return buffer.toString();
}
// --------------------------------------------------------------------------
//
// --------------------------------------------------------------------------
/// Copies [source] into this [MatriCanvas] at [x0] and [y0].
void copyInto(MatrixCanvas source, int x0, int y0) {
final sourceHeight = source._height;
final sourceWidth = source.width;
for (int y = 0; y < sourceHeight; y++) {
for (int x = 0; x < sourceWidth; x++) {
final tile = source._map[y][x];
_map[y0 + y][x0 + x] = MatrixCanvasTile(tile.tile);
}
}
}
/// Recreates the matrix.
void clearScreen() {
_map.clear();
for (int y = 0; y < _height; y++) {
List<MatrixCanvasTile> tmp = [];
for (int x = 0; x < width; x++) {
tmp.add(MatrixCanvasTile(defaultTile));
}
_map.add(tmp);
}
}
// --------------------------------------------------------------------------
//
// --------------------------------------------------------------------------
void cursorLeft() {
_col--;
if (_col < 0) {
cursorUp();
_col = width - 1;
}
}
void cursorRight() {
_col++;
if (_col >= width) {
cursorDown();
_col = 0;
}
}
void cursorUp() => _row--;
void cursorDown() => _row++;
// --------------------------------------------------------------------------
//
// --------------------------------------------------------------------------
void write(String text) {
final chars = text.split('');
final length = chars.length;
for (int index = 0; index < length; index++) {
_map[_row][_col].tile = chars[index];
cursorRight();
}
}
}