Add TextBoxConfig options and fix TextBoxComponent bugs (#534)

* Add TextBoxConfig options and fix TextBxoComponent bugs

* Add changelog entry

* Fix snackbar deprecation

* All examples to have publish to none

* One argument per line

* No explicit types for local variables

* Cache the width

* Fix formatting
This commit is contained in:
Lukas Klingsbo
2020-11-22 21:45:10 +01:00
committed by renancaraujo
parent 5dc93e1987
commit d09d2bd449
30 changed files with 140 additions and 61 deletions

View File

@ -1,6 +1,8 @@
# CHANGELOG # CHANGELOG
## [next] ## [next]
- Fix TextBoxComponent rendering
- Add TextBoxConfig options; margins and growingBox
## 1.0.0-rc2 ## 1.0.0-rc2
- Improve IsometricTileMap and Spritesheet classes - Improve IsometricTileMap and Spritesheet classes

View File

@ -60,14 +60,6 @@ class _MyHomePageState extends State<MyHomePage> {
setState(() => _position += Vector2.all(10)); setState(() => _position += Vector2.all(10));
} }
void _clickFab(GlobalKey<ScaffoldState> key) {
key.currentState.showSnackBar(
const SnackBar(
content: const Text('You clicked the FAB!'),
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final key = GlobalKey<ScaffoldState>(); final key = GlobalKey<ScaffoldState>();
@ -102,7 +94,11 @@ class _MyHomePageState extends State<MyHomePage> {
), ),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () => _clickFab(key), onPressed: () => ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: const Text('You clicked the FAB!'),
),
),
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),
); );

View File

@ -1,6 +1,8 @@
name: animation_widget name: animation_widget
description: A sample Flame project to showcase the SpriteAnimationWidget widget. description: A sample Flame project to showcase the SpriteAnimationWidget widget.
publish_to: 'none'
version: 0.1.0 version: 0.1.0
environment: environment:

View File

@ -1,6 +1,8 @@
name: animations name: animations
description: Flame sample game showcasing animations features description: Flame sample game showcasing animations features
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: aseprite name: aseprite
description: Flame sample for using Aseprite animations description: Flame sample for using Aseprite animations
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: debug name: debug
description: Flame sample for using debug features description: Flame sample for using debug features
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: combined_effects name: combined_effects
description: Flame sample game showcasing combined effects description: Flame sample game showcasing combined effects
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: infinite_effects name: infinite_effects
description: Flame sample game showcasing infinite effects description: Flame sample game showcasing infinite effects
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: sequence_effect name: sequence_effect
description: Flame sample game showcasing the sequence effect description: Flame sample game showcasing the sequence effect
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: effects name: effects
description: A Flame game showcasing the use of the effects api description: A Flame game showcasing the use of the effects api
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: gestures name: gestures
description: A flame game showcasing the use of gestures callbacks description: A flame game showcasing the use of gestures callbacks
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: go_desktop name: go_desktop
description: Flame sample game on using the go flutter desktop framework description: Flame sample game on using the go flutter desktop framework
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: isometric name: isometric
description: Example of isometric tilemap using Flame description: Example of isometric tilemap using Flame
publish_to: 'none'
version: 0.1.0 version: 0.1.0
environment: environment:

View File

@ -1,6 +1,8 @@
name: joystick name: joystick
description: A flame game showcasing the use of joystick description: A flame game showcasing the use of joystick
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: keyboard name: keyboard
description: Simple Flame project showcasing how to use the Keyboard events description: Simple Flame project showcasing how to use the Keyboard events
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: nine_tile_box name: nine_tile_box
description: Flame sample for using the nine_tile_box feature description: Flame sample for using the nine_tile_box feature
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: parallax name: parallax
description: Flame sample game showcasing the parallax features description: Flame sample game showcasing the parallax features
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: particles name: particles
description: Flame sample game showcasing particle effects description: Flame sample game showcasing particle effects
publish_to: 'none'
version: 1.0.0 version: 1.0.0
environment: environment:

View File

@ -1,6 +1,8 @@
name: render_flip name: render_flip
description: Flame sample game showcasing animations features description: Flame sample game showcasing animations features
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: sprite_batch name: sprite_batch
description: Showcasing SpriteBatch features description: Showcasing SpriteBatch features
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: sprites name: sprites
description: Flame sample game showcasing rendering 500 sprites description: Flame sample game showcasing rendering 500 sprites
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: spritesheet name: spritesheet
description: Flame sample game showcasing spritesheet features description: Flame sample game showcasing spritesheet features
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -20,16 +20,31 @@ TextConfig tiny = regular.withFontSize(12.0);
class MyTextBox extends TextBoxComponent { class MyTextBox extends TextBoxComponent {
MyTextBox(String text) MyTextBox(String text)
: super(text, config: tiny, boxConfig: TextBoxConfig(timePerChar: 0.05)); : super(
text,
config: tiny,
boxConfig: TextBoxConfig(
timePerChar: 0.05,
growingBox: true,
margins: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
),
);
@override @override
void drawBackground(Canvas c) { void drawBackground(Canvas c) {
final Rect rect = Rect.fromLTWH(0, 0, width, height); final Rect rect = Rect.fromLTWH(0, 0, width, height);
c.drawRect(rect, Paint()..color = const Color(0xFFFF00FF)); c.drawRect(rect, Paint()..color = const Color(0xFFFF00FF));
final margin = boxConfig.margins;
final Rect innerRect = Rect.fromLTWH(
margin.left,
margin.top,
width - margin.horizontal,
height - margin.vertical,
);
c.drawRect( c.drawRect(
rect.deflate(boxConfig.margin), innerRect,
Paint() Paint()
..color = BasicPalette.black.color ..color = BasicPalette.white.color
..style = PaintingStyle.stroke); ..style = PaintingStyle.stroke);
} }
} }

View File

@ -1,6 +1,8 @@
name: text name: text
description: A sample Flame project that renders texts. description: A sample Flame project that renders texts.
publish_to: 'none'
version: 0.1.0 version: 0.1.0
environment: environment:

View File

@ -1,6 +1,8 @@
name: timer name: timer
description: Example app using Timer class description: Example app using Timer class
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: widgets name: widgets
description: A Flutter project showcasing Flame's widgets. description: A Flutter project showcasing Flame's widgets.
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -1,6 +1,8 @@
name: with_widgets_overlay name: with_widgets_overlay
description: A Flame game showcasing how to use the WithWidgetsOverlay feature description: A Flame game showcasing how to use the WithWidgetsOverlay feature
publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:

View File

@ -64,6 +64,10 @@ class MyGame extends BaseGame {
`TextBoxComponent` is very similar to `TextComponent`, but as its name suggest it is used to render text inside a bounding box, creating line breaks according to the provided box size. `TextBoxComponent` is very similar to `TextComponent`, but as its name suggest it is used to render text inside a bounding box, creating line breaks according to the provided box size.
You can decide if the box should grow as the text is written or if it should be static by the `growingBox` variable in the `TextBoxConfig`.
If you want to change the margins of the box use the `margins` variable in the `TextBoxConfig`.
Example usage: Example usage:
```dart ```dart
@ -83,4 +87,4 @@ class MyTextBox extends TextBoxComponent {
} }
``` ```
Both components are showcased in a working example [here](https://github.com/luanpotter/flame/tree/master/doc/examples/text) Both components are showcased in an example [here](https://github.com/luanpotter/flame/tree/master/doc/examples/text)

View File

@ -1,6 +1,8 @@
name: position_component name: position_component
description: A new Flutter project. description: A new Flutter project.
publish_to: 'none'
version: 0.1.0 version: 0.1.0
environment: environment:

View File

@ -2,25 +2,27 @@ import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/widgets.dart' as widgets; import 'package:flutter/widgets.dart' hide Image;
import '../extensions/vector2.dart';
import '../palette.dart'; import '../palette.dart';
import '../text_config.dart'; import '../text_config.dart';
import '../extensions/vector2.dart';
import 'mixins/resizable.dart'; import 'mixins/resizable.dart';
import 'position_component.dart'; import 'position_component.dart';
class TextBoxConfig { class TextBoxConfig {
final double maxWidth; final double maxWidth;
final double margin; final EdgeInsets margins;
final double timePerChar; final double timePerChar;
final double dismissDelay; final double dismissDelay;
final bool growingBox;
TextBoxConfig({ TextBoxConfig({
this.maxWidth = 200.0, this.maxWidth = 200.0,
this.margin = 8.0, this.margins = const EdgeInsets.all(8.0),
this.timePerChar = 0.0, this.timePerChar = 0.0,
this.dismissDelay = 0.0, this.dismissDelay = 0.0,
this.growingBox = false,
}); });
} }
@ -28,8 +30,6 @@ class TextBoxComponent extends PositionComponent with Resizable {
static final Paint _imagePaint = BasicPalette.white.paint static final Paint _imagePaint = BasicPalette.white.paint
..filterQuality = FilterQuality.high; ..filterQuality = FilterQuality.high;
Vector2 p = Vector2.zero();
String _text; String _text;
TextConfig _config; TextConfig _config;
TextBoxConfig _boxConfig; TextBoxConfig _boxConfig;
@ -37,9 +37,11 @@ class TextBoxComponent extends PositionComponent with Resizable {
List<String> _lines; List<String> _lines;
double _maxLineWidth = 0.0; double _maxLineWidth = 0.0;
double _lineHeight; double _lineHeight;
int _totalLines;
double _lifeTime = 0.0; double _lifeTime = 0.0;
Image _cache; Image _cache;
int _previousChar;
String get text => _text; String get text => _text;
@ -57,24 +59,23 @@ class TextBoxComponent extends PositionComponent with Resizable {
_text = text; _text = text;
_lines = []; _lines = [];
text.split(' ').forEach((word) { text.split(' ').forEach((word) {
final String possibleLine = final possibleLine = _lines.isEmpty ? word : _lines.last + ' ' + word;
_lines.isEmpty ? word : _lines.last + ' ' + word; final painter = config.toTextPainter(possibleLine);
final widgets.TextPainter p = config.toTextPainter(possibleLine); _lineHeight ??= painter.height;
_lineHeight ??= p.height; if (painter.width <=
if (p.width <= _boxConfig.maxWidth - 2 * _boxConfig.margin) { _boxConfig.maxWidth - _boxConfig.margins.horizontal) {
if (_lines.isNotEmpty) { if (_lines.isNotEmpty) {
_lines.last = possibleLine; _lines.last = possibleLine;
} else { } else {
_lines.add(possibleLine); _lines.add(possibleLine);
} }
_updateMaxWidth(p.width); _updateMaxWidth(painter.width);
} else { } else {
_lines.add(word); _lines.add(word);
_updateMaxWidth(config.toTextPainter(word).width); _updateMaxWidth(config.toTextPainter(word).width);
} }
}); });
_totalLines = _lines.length;
redrawLater();
} }
void _updateMaxWidth(double w) { void _updateMaxWidth(double w) {
@ -103,39 +104,49 @@ class TextBoxComponent extends PositionComponent with Resizable {
return _lines.length - 1; return _lines.length - 1;
} }
double _withMargins(double size) => size + 2 * _boxConfig.margin;
@override @override
double get width => currentWidth; Vector2 get size => Vector2(width, height);
@override
double get height => currentHeight;
double get totalWidth => _withMargins(_maxLineWidth);
double get totalHeight => _withMargins(_lineHeight * _lines.length);
double getLineWidth(String line, int charCount) { double getLineWidth(String line, int charCount) {
return _withMargins(_config return _config
.toTextPainter(line.substring(0, math.min(charCount, line.length))) .toTextPainter(line.substring(0, math.min(charCount, line.length)))
.width); .width;
} }
double get currentWidth { double _cachedWidth;
@override
double get width {
if (_cachedWidth != null) {
return _cachedWidth;
}
if (_boxConfig.growingBox) {
int i = 0; int i = 0;
int totalCharCount = 0; int totalCharCount = 0;
final int _currentChar = currentChar; final int _currentChar = currentChar;
final int _currentLine = currentLine; final int _currentLine = currentLine;
return _lines.sublist(0, _currentLine + 1).map((line) { final double textWidth = _lines.sublist(0, _currentLine + 1).map((line) {
final int charCount = final int charCount =
(i < _currentLine) ? line.length : (_currentChar - totalCharCount); (i < _currentLine) ? line.length : (_currentChar - totalCharCount);
totalCharCount += line.length; totalCharCount += line.length;
i++; i++;
return getLineWidth(line, charCount); return getLineWidth(line, charCount);
}).reduce(math.max); }).reduce(math.max);
_cachedWidth = textWidth + _boxConfig.margins.horizontal;
} else {
_cachedWidth = _boxConfig.maxWidth + _boxConfig.margins.horizontal;
}
return _cachedWidth;
} }
double get currentHeight => _withMargins((currentLine + 1) * _lineHeight); @override
double get height {
if (_boxConfig.growingBox) {
return _lineHeight * _lines.length + _boxConfig.margins.vertical;
} else {
return _lineHeight * _totalLines + _boxConfig.margins.vertical;
}
}
@override @override
void render(Canvas c) { void render(Canvas c) {
@ -160,19 +171,19 @@ class TextBoxComponent extends PositionComponent with Resizable {
final int _currentLine = currentLine; final int _currentLine = currentLine;
int charCount = 0; int charCount = 0;
double dy = _boxConfig.margin; double dy = _boxConfig.margins.top;
for (int line = 0; line < _currentLine; line++) { for (int line = 0; line < _currentLine; line++) {
charCount += _lines[line].length; charCount += _lines[line].length;
_config _drawLine(c, _lines[line], dy);
.toTextPainter(_lines[line])
.paint(c, Offset(_boxConfig.margin, dy));
dy += _lineHeight; dy += _lineHeight;
} }
final int max = final int max =
math.min(currentChar - charCount, _lines[_currentLine].length); math.min(currentChar - charCount, _lines[_currentLine].length);
_config _drawLine(c, _lines[_currentLine].substring(0, max), dy);
.toTextPainter(_lines[_currentLine].substring(0, max)) }
.paint(c, Offset(_boxConfig.margin, dy));
void _drawLine(Canvas c, String line, double dy) {
_config.toTextPainter(line).paint(c, Offset(_boxConfig.margins.left, dy));
} }
void redrawLater() async { void redrawLater() async {
@ -182,10 +193,11 @@ class TextBoxComponent extends PositionComponent with Resizable {
@override @override
void update(double dt) { void update(double dt) {
super.update(dt); super.update(dt);
final int prevCurrentChar = currentChar;
_lifeTime += dt; _lifeTime += dt;
if (prevCurrentChar != currentChar) { if (_previousChar != currentChar) {
_cachedWidth = null;
redrawLater(); redrawLater();
} }
_previousChar = currentChar;
} }
} }