mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 19:12:31 +08:00
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:
committed by
renancaraujo
parent
5dc93e1987
commit
d09d2bd449
@ -1,6 +1,8 @@
|
||||
# CHANGELOG
|
||||
|
||||
## [next]
|
||||
- Fix TextBoxComponent rendering
|
||||
- Add TextBoxConfig options; margins and growingBox
|
||||
|
||||
## 1.0.0-rc2
|
||||
- Improve IsometricTileMap and Spritesheet classes
|
||||
|
||||
@ -60,14 +60,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
setState(() => _position += Vector2.all(10));
|
||||
}
|
||||
|
||||
void _clickFab(GlobalKey<ScaffoldState> key) {
|
||||
key.currentState.showSnackBar(
|
||||
const SnackBar(
|
||||
content: const Text('You clicked the FAB!'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final key = GlobalKey<ScaffoldState>();
|
||||
@ -102,7 +94,11 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _clickFab(key),
|
||||
onPressed: () => ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: const Text('You clicked the FAB!'),
|
||||
),
|
||||
),
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: animation_widget
|
||||
description: A sample Flame project to showcase the SpriteAnimationWidget widget.
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.1.0
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: animations
|
||||
description: Flame sample game showcasing animations features
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: aseprite
|
||||
description: Flame sample for using Aseprite animations
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: debug
|
||||
description: Flame sample for using debug features
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: combined_effects
|
||||
description: Flame sample game showcasing combined effects
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: infinite_effects
|
||||
description: Flame sample game showcasing infinite effects
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: sequence_effect
|
||||
description: Flame sample game showcasing the sequence effect
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: effects
|
||||
description: A Flame game showcasing the use of the effects api
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: gestures
|
||||
description: A flame game showcasing the use of gestures callbacks
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: go_desktop
|
||||
description: Flame sample game on using the go flutter desktop framework
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: isometric
|
||||
description: Example of isometric tilemap using Flame
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.1.0
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: joystick
|
||||
description: A flame game showcasing the use of joystick
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: keyboard
|
||||
description: Simple Flame project showcasing how to use the Keyboard events
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: nine_tile_box
|
||||
description: Flame sample for using the nine_tile_box feature
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: parallax
|
||||
description: Flame sample game showcasing the parallax features
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: particles
|
||||
description: Flame sample game showcasing particle effects
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: render_flip
|
||||
description: Flame sample game showcasing animations features
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: sprite_batch
|
||||
description: Showcasing SpriteBatch features
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: sprites
|
||||
description: Flame sample game showcasing rendering 500 sprites
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: spritesheet
|
||||
description: Flame sample game showcasing spritesheet features
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -20,16 +20,31 @@ TextConfig tiny = regular.withFontSize(12.0);
|
||||
|
||||
class MyTextBox extends TextBoxComponent {
|
||||
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
|
||||
void drawBackground(Canvas c) {
|
||||
final Rect rect = Rect.fromLTWH(0, 0, width, height);
|
||||
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(
|
||||
rect.deflate(boxConfig.margin),
|
||||
innerRect,
|
||||
Paint()
|
||||
..color = BasicPalette.black.color
|
||||
..color = BasicPalette.white.color
|
||||
..style = PaintingStyle.stroke);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: text
|
||||
description: A sample Flame project that renders texts.
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.1.0
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: timer
|
||||
description: Example app using Timer class
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: widgets
|
||||
description: A Flutter project showcasing Flame's widgets.
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: with_widgets_overlay
|
||||
description: A Flame game showcasing how to use the WithWidgetsOverlay feature
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@ -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.
|
||||
|
||||
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:
|
||||
|
||||
```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)
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
name: position_component
|
||||
description: A new Flutter project.
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.1.0
|
||||
|
||||
environment:
|
||||
|
||||
@ -2,25 +2,27 @@ import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
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 '../text_config.dart';
|
||||
import '../extensions/vector2.dart';
|
||||
import 'mixins/resizable.dart';
|
||||
import 'position_component.dart';
|
||||
|
||||
class TextBoxConfig {
|
||||
final double maxWidth;
|
||||
final double margin;
|
||||
final EdgeInsets margins;
|
||||
final double timePerChar;
|
||||
final double dismissDelay;
|
||||
final bool growingBox;
|
||||
|
||||
TextBoxConfig({
|
||||
this.maxWidth = 200.0,
|
||||
this.margin = 8.0,
|
||||
this.margins = const EdgeInsets.all(8.0),
|
||||
this.timePerChar = 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
|
||||
..filterQuality = FilterQuality.high;
|
||||
|
||||
Vector2 p = Vector2.zero();
|
||||
|
||||
String _text;
|
||||
TextConfig _config;
|
||||
TextBoxConfig _boxConfig;
|
||||
@ -37,9 +37,11 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
List<String> _lines;
|
||||
double _maxLineWidth = 0.0;
|
||||
double _lineHeight;
|
||||
int _totalLines;
|
||||
|
||||
double _lifeTime = 0.0;
|
||||
Image _cache;
|
||||
int _previousChar;
|
||||
|
||||
String get text => _text;
|
||||
|
||||
@ -57,24 +59,23 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
_text = text;
|
||||
_lines = [];
|
||||
text.split(' ').forEach((word) {
|
||||
final String possibleLine =
|
||||
_lines.isEmpty ? word : _lines.last + ' ' + word;
|
||||
final widgets.TextPainter p = config.toTextPainter(possibleLine);
|
||||
_lineHeight ??= p.height;
|
||||
if (p.width <= _boxConfig.maxWidth - 2 * _boxConfig.margin) {
|
||||
final possibleLine = _lines.isEmpty ? word : _lines.last + ' ' + word;
|
||||
final painter = config.toTextPainter(possibleLine);
|
||||
_lineHeight ??= painter.height;
|
||||
if (painter.width <=
|
||||
_boxConfig.maxWidth - _boxConfig.margins.horizontal) {
|
||||
if (_lines.isNotEmpty) {
|
||||
_lines.last = possibleLine;
|
||||
} else {
|
||||
_lines.add(possibleLine);
|
||||
}
|
||||
_updateMaxWidth(p.width);
|
||||
_updateMaxWidth(painter.width);
|
||||
} else {
|
||||
_lines.add(word);
|
||||
_updateMaxWidth(config.toTextPainter(word).width);
|
||||
}
|
||||
});
|
||||
|
||||
redrawLater();
|
||||
_totalLines = _lines.length;
|
||||
}
|
||||
|
||||
void _updateMaxWidth(double w) {
|
||||
@ -103,39 +104,49 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
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);
|
||||
Vector2 get size => Vector2(width, height);
|
||||
|
||||
double getLineWidth(String line, int charCount) {
|
||||
return _withMargins(_config
|
||||
return _config
|
||||
.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 totalCharCount = 0;
|
||||
final int _currentChar = currentChar;
|
||||
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 =
|
||||
(i < _currentLine) ? line.length : (_currentChar - totalCharCount);
|
||||
totalCharCount += line.length;
|
||||
i++;
|
||||
return getLineWidth(line, charCount);
|
||||
}).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
|
||||
void render(Canvas c) {
|
||||
@ -160,19 +171,19 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
|
||||
final int _currentLine = currentLine;
|
||||
int charCount = 0;
|
||||
double dy = _boxConfig.margin;
|
||||
double dy = _boxConfig.margins.top;
|
||||
for (int line = 0; line < _currentLine; line++) {
|
||||
charCount += _lines[line].length;
|
||||
_config
|
||||
.toTextPainter(_lines[line])
|
||||
.paint(c, Offset(_boxConfig.margin, dy));
|
||||
_drawLine(c, _lines[line], dy);
|
||||
dy += _lineHeight;
|
||||
}
|
||||
final int max =
|
||||
math.min(currentChar - charCount, _lines[_currentLine].length);
|
||||
_config
|
||||
.toTextPainter(_lines[_currentLine].substring(0, max))
|
||||
.paint(c, Offset(_boxConfig.margin, dy));
|
||||
_drawLine(c, _lines[_currentLine].substring(0, max), dy);
|
||||
}
|
||||
|
||||
void _drawLine(Canvas c, String line, double dy) {
|
||||
_config.toTextPainter(line).paint(c, Offset(_boxConfig.margins.left, dy));
|
||||
}
|
||||
|
||||
void redrawLater() async {
|
||||
@ -182,10 +193,11 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
final int prevCurrentChar = currentChar;
|
||||
_lifeTime += dt;
|
||||
if (prevCurrentChar != currentChar) {
|
||||
if (_previousChar != currentChar) {
|
||||
_cachedWidth = null;
|
||||
redrawLater();
|
||||
}
|
||||
_previousChar = currentChar;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user