malison 0.11.3

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 76

Malison is a small Dart library for drawing old school ASCII terminals in the browser. I harvested it from my roguelike game, Hauberk and it's aimed primarily at web games ASCII graphics. Think of it like curses for the web.

Using it #

Add it to your package's pubspec:

dependencies:
  malison: any

Then use the library:

import 'dart:html';

import 'package:malison/malison.dart';

void main() {
  // Create or query a <canvas> element to bind it to.
  var canvas = new CanvasElement();
  document.body.children.add(canvas);

  // Create a new terminal. CanvasTerminal uses your browser's fonts.
  // RetroTerminal uses a built in DOS-style Code Page 437 font.
  var terminal = new RetroTerminal.dos(80, 40, canvas);

  // You can draw strings at given positions.
  terminal.writeAt(0, 0, "This is a terminal!");

  // You can control the foreground and background color.
  terminal.writeAt(0, 1, "This is blue on green", Color.blue, Color.green);

  // You can also draw individual glyphs -- character+color units.
  terminal.drawGlyph(3, 4, new Glyph.fromCharCode(CharCode.blackHeartSuit,
      Color.red, Color.white));

  // When you're done drawing, tell it to render all of the changes. It renders
  // in batches for performance.
  terminal.render();
}

0.11.3 #

  • Fix concurrent modification error when screens are pushed or popped from within update().

0.11.2 #

  • Add Screen.resize() and call it when the UserInterface gets bound to a terminal with a different size.

0.11.1 #

  • Add more key codes to KeyCode.

0.11.0 #

  • Don't resize the canvas given to RetroTerminal.

  • Allow controlling the scaling for RetroTerminal.

  • Don't require double-sized source images for RetroTerminal. Instead, it uses imageSmoothingEnabled to ensure pixels don't get fuzzy when drawing from the font canvas.

    This is a breaking change because it means custom font images, which used to have to be double-sized, should no longer be.

0.10.0 #

  • Update to Dart 2. Remove new and const keywords.

0.9.0 #

  • keyUp() events.

0.8.0 #

  • Change Color.blend() to take a value from (0, 1.0) instead of a percent.
  • Add Color.add().

0.7.0 #

  • Fill in the rest of the character codes to CharCode.
  • Add Color.blend() and store colors as RGB.
  • Add foreColor, backColor, and fill() to Terminal.

0.6.0 #

  • Split into two libraries. The core malison library does not import "dart:html" and can be used in command-line applications. It's not useful outside of the web, but it lets you test code in a "headless" fashion that uses malison on the standalone VM.

0.5.1 #

  • Get rid of implicit casts and dynamic.

0.5.0 #

  • Add type parameters to UserInterface, KeyBindings, and Screen so that the bound objects can be precisely typed.

0.4.3 #

  • Make strong mode clean.

0.4.2 #

  • Widen constraint on piecemeal.

0.4.1 #

  • Fix semicolon handling on Firefox.

0.4.0 #

  • Add constants to CharCode for every character in code page 437.
  • Make constants lowerCamelCase.

0.3.0 #

  • Automatically create a canvas if not given one.
  • Allow creating a UserInterface without an initial terminal.
  • Add an example app.

0.2.1 #

  • Fix bad assert in Screen._bind().

0.2.0 #

  • Redo key input handling. Instead of a (mostly broken) Keyboard class, it exposes raw key down events as they happen and also allows user-defined key bindings.

  • Refactor UserInterface and clean up how opaque/transparent screens are handled.

  • Remove (useless) write() method.

  • Handle semicolon keyCode difference between Firefox (59) and Chrome (186).

example/main.dart

import 'dart:html' as html;
import 'dart:math' as math;

import 'package:malison/malison.dart';
import 'package:malison/malison_web.dart';

const width = 80;
const height = 30;

final ui = UserInterface<String>();

/// A few different terminals to choose from.
final terminals = <RenderableTerminal Function()>[
  () => RetroTerminal.dos(width, height),
  () => RetroTerminal.shortDos(width, height),
  () => CanvasTerminal(width, height,
      Font('Menlo, Consolas', size: 12, w: 8, h: 14, x: 1, y: 11)),
  () => CanvasTerminal(
      width, height, Font('Courier', size: 13, w: 10, h: 15, x: 1, y: 11)),
  () => CanvasTerminal(
      width, height, Font('Courier', size: 12, w: 8, h: 14, x: 1, y: 10))
];

/// Index of the current terminal in [terminals].
int terminalIndex = 0;

void main() {
  // Set up the keybindings.
  ui.keyPress.bind("next terminal", KeyCode.tab);
  ui.keyPress.bind("prev terminal", KeyCode.tab, shift: true);
  ui.keyPress.bind("animate", KeyCode.space);
  ui.keyPress.bind("profile", KeyCode.p);

  updateTerminal();

  ui.push(MainScreen());

  ui.handlingInput = true;
  ui.running = true;
}

void updateTerminal() {
  html.document.body.children.clear();
  ui.setTerminal(terminals[terminalIndex]());
}

class MainScreen extends Screen<String> {
  final List<Ball> balls = [];

  MainScreen() {
    var colors = [
      Color.red,
      Color.orange,
      Color.gold,
      Color.yellow,
      Color.green,
      Color.aqua,
      Color.blue,
      Color.purple
    ];

    var random = math.Random();

    for (var char in "0123456789".codeUnits) {
      for (var color in colors) {
        balls.add(Ball(
            color,
            char,
            random.nextDouble() * Ball.pitWidth,
            random.nextDouble() * (Ball.pitHeight / 2.0),
            random.nextDouble() + 0.2,
            0.0));
      }
    }
  }

  bool handleInput(String input) {
    switch (input) {
      case "next terminal":
        terminalIndex = (terminalIndex + 1) % terminals.length;
        updateTerminal();
        ui.refresh();
        break;

      case "prev terminal":
        terminalIndex = (terminalIndex - 1) % terminals.length;
        updateTerminal();
        ui.refresh();
        break;

      case "animate":
        ui.running = !ui.running;
        break;

      case "profile":
        profile();
        break;

      default:
        return false;
    }

    return true;
  }

  void profile() {
    ui.running = true;
    for (var i = 0; i < 1000; i++) {
      update();
      ui.refresh();
    }
  }

  void update() {
    for (var ball in balls) {
      ball.update();
    }

    dirty();
  }

  void render(Terminal terminal) {
    terminal.clear();

    void colorBar(int y, String name, Color light, Color medium, Color dark) {
      terminal.writeAt(2, y, name, Color.gray);
      terminal.writeAt(10, y, "light", light);
      terminal.writeAt(16, y, "medium", medium);
      terminal.writeAt(23, y, "dark", dark);

      terminal.writeAt(28, y, " light ", Color.black, light);
      terminal.writeAt(35, y, " medium ", Color.black, medium);
      terminal.writeAt(43, y, " dark ", Color.black, dark);
    }

    terminal.writeAt(0, 0, "Predefined colors:");
    terminal.writeAt(59, 0, "switch terminal [tab]", Color.darkGray);
    terminal.writeAt(75, 0, "[tab]", Color.lightGray);
    colorBar(1, "gray", Color.lightGray, Color.gray, Color.darkGray);
    colorBar(2, "red", Color.lightRed, Color.red, Color.darkRed);
    colorBar(3, "orange", Color.lightOrange, Color.orange, Color.darkOrange);
    colorBar(4, "gold", Color.lightGold, Color.gold, Color.darkGold);
    colorBar(5, "yellow", Color.lightYellow, Color.yellow, Color.darkYellow);
    colorBar(6, "green", Color.lightGreen, Color.green, Color.darkGreen);
    colorBar(7, "aqua", Color.lightAqua, Color.aqua, Color.darkAqua);
    colorBar(8, "blue", Color.lightBlue, Color.blue, Color.darkBlue);
    colorBar(9, "purple", Color.lightPurple, Color.purple, Color.darkPurple);
    colorBar(10, "brown", Color.lightBrown, Color.brown, Color.darkBrown);

    terminal.writeAt(0, 12, "Code page 437:");
    var lines = [
      " ☺☻♥♦♣♠•◘○◙♂♀♪♫☼",
      "►◄↕‼¶§▬↨↑↓→←∟↔▲▼",
      " !\"#\$%&'()*+,-./",
      "0123456789:;<=>?",
      "@ABCDEFGHIJKLMNO",
      "PQRSTUVWXYZ[\\]^_",
      "`abcdefghijklmno",
      "pqrstuvwxyz{|}~⌂",
      "ÇüéâäàåçêëèïîìÄÅ",
      "ÉæÆôöòûùÿÖÜ¢£¥₧ƒ",
      "áíóúñѪº¿⌐¬½¼¡«»",
      "░▒▓│┤╡╢╖╕╣║╗╝╜╛┐",
      "└┴┬├─┼╞╟╚╔╩╦╠═╬╧",
      "╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀",
      "αßΓπΣσµτΦΘΩδ∞φε∩",
      "≡±≥≤⌠⌡÷≈°∙·√ⁿ²■"
    ];

    var y = 13;
    for (var line in lines) {
      terminal.writeAt(3, y++, line, Color.lightGray);
    }

    terminal.writeAt(22, 12, "Simple game loop:");
    terminal.writeAt(66, 12, "toggle [space]", Color.darkGray);
    terminal.writeAt(73, 12, "[space]", Color.lightGray);

    for (var ball in balls) {
      ball.render(terminal);
    }
  }
}

class Ball {
  static const pitWidth = 56.0;
  static const pitHeight = 17.0;

  final Color color;
  final int charCode;

  double x, y, h, v;

  Ball(this.color, this.charCode, this.x, this.y, this.h, this.v);

  void update() {
    x += h;
    if (x < 0.0) {
      x = -x;
      h = -h;
    } else if (x > pitWidth) {
      x = pitWidth - x + pitWidth;
      h = -h;
    }

    v += 0.03;
    y += v;
    if (y > pitHeight) {
      y = pitHeight - y + pitHeight;
      v = -v;
    }
  }

  void render(Terminal terminal) {
    terminal.drawChar(24 + x.toInt(), 13 + y.toInt(), charCode, color);
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  malison: ^0.11.3

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter packages get

Alternatively, your editor might support pub get or flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:malison/malison.dart';
  
Version Uploaded Documentation Archive
0.11.3 Jan 5, 2019 Go to the documentation of malison 0.11.3 Download malison 0.11.3 archive
0.11.2 Dec 17, 2018 Go to the documentation of malison 0.11.2 Download malison 0.11.2 archive
0.11.1 Dec 15, 2018 Go to the documentation of malison 0.11.1 Download malison 0.11.1 archive
0.11.0 Dec 12, 2018 Go to the documentation of malison 0.11.0 Download malison 0.11.0 archive
0.10.1+1 Oct 22, 2018 Go to the documentation of malison 0.10.1+1 Download malison 0.10.1+1 archive
0.10.1 Aug 5, 2018 Go to the documentation of malison 0.10.1 Download malison 0.10.1 archive
0.10.0 Jun 13, 2018 Go to the documentation of malison 0.10.0 Download malison 0.10.0 archive
0.9.0 Jun 4, 2018 Go to the documentation of malison 0.9.0 Download malison 0.9.0 archive
0.8.0 Dec 16, 2017 Go to the documentation of malison 0.8.0 Download malison 0.8.0 archive
0.7.0 Sep 16, 2017 Go to the documentation of malison 0.7.0 Download malison 0.7.0 archive

All 23 versions...

Popularity:
Describes how popular the package is relative to other packages. [more]
58
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
85
Overall:
Weighted score of the above. [more]
76
Learn more about scoring.

We analyzed this package on Apr 18, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.2.0
  • pana: 0.12.14

Platforms

Detected platforms: Flutter, web, other

No platform restriction found in primary library package:malison/malison.dart.

Health suggestions

Fix lib/src/retro_terminal.dart. (-0.50 points)

Analysis of lib/src/retro_terminal.dart reported 1 hint:

line 18 col 28: The value of the field '_canvas' isn't used.

Maintenance suggestions

The package description is too short. (-15 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0 <3.0.0
piecemeal >=0.3.7 <0.5.0 0.4.2
Dev dependencies
build_runner any
build_web_compilers any