mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 10:38:17 +08:00
180 lines
4.5 KiB
Dart
180 lines
4.5 KiB
Dart
import 'dart:async';
|
|
import 'dart:ui';
|
|
import 'dart:math' as math;
|
|
import 'package:flutter/widgets.dart' as widgets;
|
|
|
|
import 'component.dart';
|
|
import 'resizable.dart';
|
|
import '../text_config.dart';
|
|
import '../palette.dart';
|
|
import '../position.dart';
|
|
|
|
class TextBoxConfig {
|
|
final double maxWidth;
|
|
final double margin;
|
|
final double timePerChar;
|
|
final double dismissDelay;
|
|
|
|
const TextBoxConfig({
|
|
this.maxWidth: 200.0,
|
|
this.margin: 8.0,
|
|
this.timePerChar: 0.0,
|
|
this.dismissDelay: 0.0,
|
|
});
|
|
}
|
|
|
|
class TextBoxComponent extends PositionComponent with Resizable {
|
|
Position p = Position.empty();
|
|
|
|
String _text;
|
|
TextConfig _config;
|
|
TextBoxConfig _boxConfig;
|
|
|
|
List<String> _lines;
|
|
double _maxLineWidth = 0.0;
|
|
double _lineHeight;
|
|
|
|
double _lifeTime = 0.0;
|
|
Image _cache;
|
|
|
|
String get text => _text;
|
|
|
|
TextConfig get config => _config;
|
|
|
|
TextBoxConfig get boxConfig => _boxConfig;
|
|
|
|
TextBoxComponent(String text,
|
|
{TextConfig config = const TextConfig(),
|
|
TextBoxConfig boxConfig = const TextBoxConfig()}) {
|
|
_boxConfig = boxConfig;
|
|
_config = config;
|
|
_text = text;
|
|
_lines = [''];
|
|
text.split(' ').forEach((word) {
|
|
String possibleLine = _lines.last + ' ' + word;
|
|
widgets.TextPainter p = config.toTextPainter(possibleLine);
|
|
if (_lineHeight == null) {
|
|
_lineHeight = p.height;
|
|
}
|
|
if (p.width <= _boxConfig.maxWidth - 2 * _boxConfig.margin) {
|
|
_lines.last = possibleLine;
|
|
_updateMaxWidth(p.width);
|
|
} else {
|
|
_lines.add(word);
|
|
_updateMaxWidth(config.toTextPainter(word).width);
|
|
}
|
|
});
|
|
|
|
redrawLater();
|
|
}
|
|
|
|
void _updateMaxWidth(double w) {
|
|
if (w > _maxLineWidth) {
|
|
_maxLineWidth = w;
|
|
}
|
|
}
|
|
|
|
double get totalCharTime => _text.length * _boxConfig.timePerChar;
|
|
|
|
bool get finished => _lifeTime > totalCharTime + _boxConfig.dismissDelay;
|
|
|
|
int get currentChar => _boxConfig.timePerChar == 0.0
|
|
? _text.length - 1
|
|
: math.min(_lifeTime ~/ _boxConfig.timePerChar, _text.length - 1);
|
|
|
|
int get currentLine {
|
|
int totalCharCount = 0;
|
|
int _currentChar = currentChar;
|
|
for (int i = 0; i < _lines.length; i++) {
|
|
totalCharCount += _lines[i].length;
|
|
if (totalCharCount > _currentChar) {
|
|
return i;
|
|
}
|
|
}
|
|
return _lines.length - 1;
|
|
}
|
|
|
|
double _withMargins(double size) => size + 2 * _boxConfig.margin;
|
|
|
|
@override
|
|
double get width => currentWidth;
|
|
|
|
@override
|
|
double get height => currentHeight;
|
|
|
|
double get totalWidth => _withMargins(_maxLineWidth);
|
|
|
|
double get totalHeight => _withMargins(_lineHeight * _lines.length);
|
|
|
|
double getLineWidth(String line, int charCount) {
|
|
return _withMargins(_config
|
|
.toTextPainter(line.substring(0, math.min(charCount, line.length)))
|
|
.width);
|
|
}
|
|
|
|
double get currentWidth {
|
|
int i = 0;
|
|
int totalCharCount = 0;
|
|
int _currentChar = currentChar;
|
|
int _currentLine = currentLine;
|
|
return _lines.sublist(0, _currentLine + 1).map((line) {
|
|
int charCount =
|
|
(i < _currentLine) ? line.length : (_currentChar - totalCharCount);
|
|
totalCharCount += line.length;
|
|
i++;
|
|
return getLineWidth(line, charCount);
|
|
}).reduce(math.max);
|
|
}
|
|
|
|
double get currentHeight => _withMargins((currentLine + 1) * _lineHeight);
|
|
|
|
void render(Canvas c) {
|
|
if (_cache == null) {
|
|
return;
|
|
}
|
|
prepareCanvas(c);
|
|
c.drawImage(_cache, Offset.zero, BasicPalette.white.paint);
|
|
}
|
|
|
|
Future<Image> _redrawCache() {
|
|
PictureRecorder recorder = PictureRecorder();
|
|
Canvas c = Canvas(
|
|
recorder, Rect.fromLTWH(0.0, 0.0, width.toDouble(), height.toDouble()));
|
|
_fullRender(c);
|
|
return recorder.endRecording().toImage(width.toInt(), height.toInt());
|
|
}
|
|
|
|
void drawBackground(Canvas c) {}
|
|
|
|
void _fullRender(Canvas c) {
|
|
drawBackground(c);
|
|
|
|
int _currentLine = currentLine;
|
|
int charCount = 0;
|
|
double dy = _boxConfig.margin;
|
|
for (int line = 0; line < _currentLine; line++) {
|
|
charCount += _lines[line].length;
|
|
_config
|
|
.toTextPainter(_lines[line])
|
|
.paint(c, Offset(_boxConfig.margin, dy));
|
|
dy += _lineHeight;
|
|
}
|
|
int max = math.min(currentChar - charCount, _lines[_currentLine].length);
|
|
_config
|
|
.toTextPainter(_lines[_currentLine].substring(0, max))
|
|
.paint(c, Offset(_boxConfig.margin, dy));
|
|
}
|
|
|
|
void redrawLater() async {
|
|
_cache = await _redrawCache();
|
|
}
|
|
|
|
void update(double dt) {
|
|
int prevCurrentChar = currentChar;
|
|
_lifeTime += dt;
|
|
if (prevCurrentChar != currentChar) {
|
|
redrawLater();
|
|
}
|
|
}
|
|
}
|