dartterm/lib/terminal.dart

142 lines
3.4 KiB
Dart
Raw Permalink Normal View History

2023-09-11 02:00:06 +00:00
import 'dart:async';
2023-09-10 03:26:30 +00:00
import 'dart:developer';
2023-09-11 02:00:06 +00:00
import 'dart:math' as math;
import 'dart:ui' as ui;
2023-09-10 03:26:30 +00:00
2023-09-11 02:00:06 +00:00
import 'package:dartterm/assets.dart';
2023-09-06 23:38:53 +00:00
import 'package:dartterm/colors.dart';
2023-09-06 03:11:15 +00:00
import 'package:dartterm/cp437.dart';
import 'package:dartterm/fonts.dart';
2023-09-10 03:26:30 +00:00
import 'package:dartterm/input.dart';
2023-09-21 03:56:30 +00:00
import 'package:dartterm/skreek.dart';
2023-09-10 03:26:30 +00:00
import 'package:flutter/gestures.dart';
2023-09-06 03:11:15 +00:00
import 'package:flutter/material.dart';
2023-09-11 02:00:06 +00:00
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,
}
2023-09-06 03:11:15 +00:00
class Terminal {
static const int width = 88;
2023-09-06 23:45:34 +00:00
static const int height = 50;
2023-09-06 03:11:15 +00:00
static const int nTiles = width * height;
2023-09-11 02:00:06 +00:00
late List<Tile> _tiles;
(int, int)? _lastSeenMouse;
ScreenDimensions? _screenDimensions;
final StreamController<Input> _inputSink =
StreamController<Input>.broadcast();
late final Stream<Input> _inputSource = _inputSink.stream.asBroadcastStream();
// ignore: prefer_final_fields
_State _state = _State.baseState;
2023-09-06 03:11:15 +00:00
Terminal() {
2023-09-11 02:00:06 +00:00
_tiles = [for (var i = 0; i < nTiles; i += 1) Tile()];
2023-09-06 03:11:15 +00:00
}
2023-09-10 03:26:30 +00:00
Widget toWidget(BuildContext context) {
2023-09-06 03:11:15 +00:00
var scalingFactor = MediaQuery.devicePixelRatioOf(context);
2023-09-10 03:26:30 +00:00
return MouseRegion(
onExit: _onPointerExit,
child: Listener(
onPointerDown: _onPointerDown,
onPointerHover: _onPointerHover,
child: CustomPaint(
painter: TerminalCustomPainter(this, scalingFactor))));
2023-09-06 03:11:15 +00:00
}
2023-09-11 02:00:06 +00:00
(int, int)? _toXY(int i) {
2023-09-06 03:11:15 +00:00
if (i < 0 || i > nTiles) {
return null;
}
return (i % width, i ~/ width);
}
2023-09-11 02:00:06 +00:00
int? _fromXY(int x, int y) {
2023-09-06 03:11:15 +00:00
if (x < 0 || x >= width) {
return null;
}
if (y < 0 || y >= height) {
return null;
}
return x + y * width;
}
2023-09-10 03:26:30 +00:00
2023-09-11 02:00:06 +00:00
void _notifyInput(Input i) {
2023-09-21 03:56:30 +00:00
skreek("Input: $i $_lastSeenMouse");
2023-09-11 02:00:06 +00:00
_inputSink.add(i);
2023-09-10 03:26:30 +00:00
}
2023-09-11 02:00:06 +00:00
void _notifyScreenDimensions(ScreenDimensions sd) {
_screenDimensions = sd;
}
Stream<Input> rawInput() {
return _inputSource;
2023-09-10 03:26:30 +00:00
}
bool _onPointerDown(PointerDownEvent me) {
2023-09-11 02:00:06 +00:00
final Button button;
2023-09-10 03:26:30 +00:00
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);
2023-09-11 02:00:06 +00:00
_lastSeenMouse = xy;
2023-09-10 03:26:30 +00:00
if (xy != null) {
final (x, y) = xy;
2023-09-11 02:00:06 +00:00
_notifyInput(Click(button, x, y));
2023-09-10 03:26:30 +00:00
}
return true;
}
bool _onPointerExit(PointerExitEvent me) {
2023-09-11 02:00:06 +00:00
_lastSeenMouse = null;
2023-09-10 03:26:30 +00:00
return true;
}
bool _onPointerHover(PointerHoverEvent me) {
var localPosition = me.localPosition;
2023-09-11 02:00:06 +00:00
_lastSeenMouse = _relativizeMouse(localPosition.dx, localPosition.dy);
2023-09-10 03:26:30 +00:00
return true;
}
(int, int)? _relativizeMouse(double localX, double localY) {
2023-09-11 02:00:06 +00:00
final sd = _screenDimensions;
2023-09-10 03:26:30 +00:00
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});
2023-09-06 03:11:15 +00:00
}