import 'dart:async'; import 'dart:math' as math; import 'dart:ui' as ui; import 'package:dartterm/assets.dart'; import 'package:dartterm/colors.dart'; import 'package:dartterm/cp437.dart'; import 'package:dartterm/fonts.dart'; import 'package:dartterm/input.dart'; import 'package:dartterm/skreek.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; part 'terminal/action_oriented.dart'; part 'terminal/cursor.dart'; part 'terminal/painter.dart'; part 'terminal/reexports.dart'; part 'terminal/tile.dart'; enum _State { baseState, waitMenu, } class Terminal { static const int width = 88; static const int height = 50; static const int nTiles = width * height; late List _tiles; (int, int)? _lastSeenMouse; ScreenDimensions? _screenDimensions; final StreamController _inputSink = StreamController.broadcast(); late final Stream _inputSource = _inputSink.stream.asBroadcastStream(); // ignore: prefer_final_fields _State _state = _State.baseState; Terminal() { _tiles = [for (var i = 0; i < nTiles; i += 1) Tile()]; } Widget toWidget(BuildContext context) { var scalingFactor = MediaQuery.devicePixelRatioOf(context); return MouseRegion( onExit: _onPointerExit, child: Listener( onPointerDown: _onPointerDown, onPointerHover: _onPointerHover, child: CustomPaint( painter: TerminalCustomPainter(this, scalingFactor)))); } (int, int)? _toXY(int i) { if (i < 0 || i > nTiles) { return null; } return (i % width, i ~/ width); } int? _fromXY(int x, int y) { if (x < 0 || x >= width) { return null; } if (y < 0 || y >= height) { return null; } return x + y * width; } void _notifyInput(Input i) { _inputSink.add(i); } void _notifyScreenDimensions(ScreenDimensions sd) { _screenDimensions = sd; } Stream rawInput() { return _inputSource; } bool _onPointerDown(PointerDownEvent me) { final Button button; if (me.buttons & kPrimaryMouseButton != 0) { button = Button.left; } else if (me.buttons & kSecondaryMouseButton != 0) { button = Button.right; } else { return false; } var localPosition = me.localPosition; final xy = _relativizeMouse(localPosition.dx, localPosition.dy); _lastSeenMouse = xy; if (xy != null) { final (x, y) = xy; _notifyInput(Click(button, x, y)); } return true; } bool _onPointerExit(PointerExitEvent me) { _lastSeenMouse = null; return true; } bool _onPointerHover(PointerHoverEvent me) { var localPosition = me.localPosition; _lastSeenMouse = _relativizeMouse(localPosition.dx, localPosition.dy); return true; } (int, int)? _relativizeMouse(double localX, double localY) { final sd = _screenDimensions; if (sd == null) return null; if (sd.xSz == 0 || sd.ySz == 0) return null; final tx = ((localX - sd.x0) * width) ~/ sd.xSz; final ty = ((localY - sd.y0) * height) ~/ sd.ySz; if (tx < 0 || tx >= width) return null; if (ty < 0 || ty >= height) return null; return (tx, ty); } } class ScreenDimensions { final double x0, y0, xSz, ySz; const ScreenDimensions( {required this.x0, required this.y0, required this.xSz, required this.ySz}); }