diff --git a/doc/text.md b/doc/text.md index a4dd621e8..f214885c3 100644 --- a/doc/text.md +++ b/doc/text.md @@ -2,15 +2,28 @@ Flame has some dedicated classes to help you render text. -## TextConfig +## TextRenderer -A Text Config contains all typographical information required to render text; i.e., font size and -color, family, etc. +`TextRenderer` is the abstract class used by Flame to render text. Flame provides one +implementation for this called `TextPaint` but anyone can implement this abstraction +and create a custom way to render text. + +## TextPaint + +A Text Paint is the built in implementation of text rendering on Flame, it is based on top of +Flutter's `TextPainter` class (hence the name), it can be configured by its config class +`TextPaintConfig` which contains all typographical information required to render text; i.e., font +size and color, family, etc. Example usage: ```dart -const TextConfig config = TextConfig(fontSize: 48.0, fontFamily: 'Awesome Font'); +const TextPaint textPaint = TextPaint( + config: TextPaintConfig( + fontSize: 48.0, + fontFamily: 'Awesome Font', + ), +); ``` - `fontFamily`: a commonly available font, like Arial (default), or a custom font added in your @@ -22,17 +35,17 @@ const TextConfig config = TextConfig(fontSize: 48.0, fontFamily: 'Awesome Font') For more information regarding colors and how to create then, see the [Colors and the Palette](palette.md) guide. -After the creation of the config you can use its `render` method to draw some string on a canvas: +After the creation of the text paint you can use its `render` method to draw some string on a canvas: ```dart -config.render(canvas, "Flame is awesome", Position(10, 10)); +textPaint.render(canvas, "Flame is awesome", Vector2(10, 10)); ``` If you want to set the anchor of the text you can also do that in the render call, with the optional `anchor` parameter: ```dart -config.render(canvas, 'Flame is awesome', Vector2(10, 10), anchor: Anchor.topCenter); +textPaint.render(canvas, 'Flame is awesome', Vector2(10, 10), anchor: Anchor.topCenter); ``` ## Text Components @@ -47,12 +60,12 @@ Flame provides two text components that make it even easier to render text in yo Example usage: ```dart -TextConfig regular = TextConfig(color: BasicPalette.white.color); +TextPaint regular = TextPaint(color: BasicPalette.white.color); class MyGame extends BaseGame { @override Future onLoad() async { - add(TextComponent('Hello, Flame', config: regular) + add(TextComponent('Hello, Flame', textRenderer: regular) ..anchor = Anchor.topCenter ..x = size.width / 2 // size is a property from game ..y = 32.0); @@ -74,15 +87,15 @@ Example usage: ```dart class MyTextBox extends TextBoxComponent { - MyTextBox(String text) : super(text, config: tiny, boxConfig: TextBoxConfig(timePerChar: 0.05)); + MyTextBox(String text) : super(text, textRenderer: tiny, boxConfig: TextBoxConfig(timePerChar: 0.05)); @override void drawBackground(Canvas c) { Rect rect = Rect.fromLTWH(0, 0, width, height); - c.drawRect(rect, new Paint()..color = Color(0xFFFF00FF)); + c.drawRect(rect, Paint()..color = Color(0xFFFF00FF)); c.drawRect( rect.deflate(boxConfig.margin), - new Paint() + Paint() ..color = BasicPalette.black.color ..style = PaintingStyle.stroke); } diff --git a/examples/lib/stories/camera_and_viewport/follow_object.dart b/examples/lib/stories/camera_and_viewport/follow_object.dart index e6740c706..69ec09518 100644 --- a/examples/lib/stories/camera_and_viewport/follow_object.dart +++ b/examples/lib/stories/camera_and_viewport/follow_object.dart @@ -14,7 +14,11 @@ final R = Random(); class MovableSquare extends SquareComponent with Hitbox, Collidable, HasGameRef { static const double speed = 300; - static final TextConfig config = TextConfig(fontSize: 12); + static final TextPaint textRenderer = TextPaint( + config: const TextPaintConfig( + fontSize: 12, + ), + ); final Vector2 velocity = Vector2.zero(); late Timer timer; @@ -41,7 +45,7 @@ class MovableSquare extends SquareComponent void render(Canvas c) { super.render(c); final text = '(${x.toInt()}, ${y.toInt()})'; - config.render(c, text, size / 2, anchor: Anchor.center); + textRenderer.render(c, text, size / 2, anchor: Anchor.center); } @override diff --git a/examples/lib/stories/collision_detection/multiple_shapes.dart b/examples/lib/stories/collision_detection/multiple_shapes.dart index f26023cdf..20edf2852 100644 --- a/examples/lib/stories/collision_detection/multiple_shapes.dart +++ b/examples/lib/stories/collision_detection/multiple_shapes.dart @@ -167,8 +167,10 @@ class CollidableSnowman extends MyCollidable { class MultipleShapes extends BaseGame with HasCollidables, HasDraggableComponents { - final TextConfig fpsTextConfig = TextConfig( - color: BasicPalette.white.color, + final TextPaint fpsTextPaint = TextPaint( + config: TextPaintConfig( + color: BasicPalette.white.color, + ), ); @override @@ -229,7 +231,7 @@ class MultipleShapes extends BaseGame @override void render(Canvas canvas) { super.render(canvas); - fpsTextConfig.render( + fpsTextPaint.render( canvas, '${fps(120).toStringAsFixed(2)}fps', Vector2(0, size.y - 24), diff --git a/examples/lib/stories/components/debug.dart b/examples/lib/stories/components/debug.dart index 9f51e4b01..5c23654c1 100644 --- a/examples/lib/stories/components/debug.dart +++ b/examples/lib/stories/components/debug.dart @@ -34,7 +34,11 @@ class LogoCompomnent extends SpriteComponent with HasGameRef { } class DebugGame extends BaseGame { - static final fpsTextConfig = TextConfig(color: const Color(0xFFFFFFFF)); + static final fpsTextPaint = TextPaint( + config: const TextPaintConfig( + color: Color(0xFFFFFFFF), + ), + ); @override bool debugMode = true; @@ -67,7 +71,7 @@ class DebugGame extends BaseGame { super.render(canvas); if (debugMode) { - fpsTextConfig.render(canvas, fps(120).toString(), Vector2(0, 50)); + fpsTextPaint.render(canvas, fps(120).toString(), Vector2(0, 50)); } } } diff --git a/examples/lib/stories/rendering/text.dart b/examples/lib/stories/rendering/text.dart index bb1257a5c..df134d94d 100644 --- a/examples/lib/stories/rendering/text.dart +++ b/examples/lib/stories/rendering/text.dart @@ -5,8 +5,9 @@ import 'package:flame/game.dart'; import 'package:flame/palette.dart'; import 'package:flutter/material.dart'; -final _regular = TextConfig(color: BasicPalette.white.color); -final _tiny = _regular.withFontSize(12.0); +final _regularTextConfig = TextPaintConfig(color: BasicPalette.white.color); +final _regular = TextPaint(config: _regularTextConfig); +final _tiny = TextPaint(config: _regularTextConfig.withFontSize(12.0)); final _white = Paint() ..color = BasicPalette.white.color @@ -16,7 +17,7 @@ class MyTextBox extends TextBoxComponent { MyTextBox(String text) : super( text, - config: _tiny, + textRenderer: _tiny, boxConfig: TextBoxConfig( timePerChar: 0.05, growingBox: true, @@ -43,20 +44,20 @@ class TextGame extends BaseGame { @override Future onLoad() async { add( - TextComponent('Hello, Flame', config: _regular) + TextComponent('Hello, Flame', textRenderer: _regular) ..anchor = Anchor.topCenter ..x = size.x / 2 ..y = 32.0, ); add( - TextComponent('center', config: _tiny) + TextComponent('center', textRenderer: _tiny) ..anchor = Anchor.center ..position.setFrom(size / 2), ); add( - TextComponent('bottomRight', config: _tiny) + TextComponent('bottomRight', textRenderer: _tiny) ..anchor = Anchor.bottomRight ..position.setFrom(size), ); diff --git a/examples/lib/stories/utils/particles.dart b/examples/lib/stories/utils/particles.dart index 27c0772cc..07b565a6d 100644 --- a/examples/lib/stories/utils/particles.dart +++ b/examples/lib/stories/utils/particles.dart @@ -21,8 +21,10 @@ class ParticlesGame extends BaseGame { final Random rnd = Random(); final StepTween steppedTween = StepTween(begin: 0, end: 5); final trafficLight = TrafficLightComponent(); - final TextConfig fpsTextConfig = TextConfig( - color: const Color(0xFFFFFFFF), + final TextPaint fpsTextPaint = TextPaint( + config: const TextPaintConfig( + color: Color(0xFFFFFFFF), + ), ); /// Defines the lifespan of all the particles in these examples @@ -474,7 +476,7 @@ class ParticlesGame extends BaseGame { super.render(canvas); if (debugMode) { - fpsTextConfig.render( + fpsTextPaint.render( canvas, '${fps(120).toStringAsFixed(2)}fps', Vector2(0, size.y - 24), diff --git a/examples/lib/stories/utils/timer.dart b/examples/lib/stories/utils/timer.dart index 12d3d4ec2..dd9056c92 100644 --- a/examples/lib/stories/utils/timer.dart +++ b/examples/lib/stories/utils/timer.dart @@ -4,7 +4,11 @@ import 'package:flame/timer.dart'; import 'package:flame/gestures.dart'; class TimerGame extends Game with TapDetector { - final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF)); + final TextPaint textConfig = TextPaint( + config: const TextPaintConfig( + color: Color(0xFFFFFFFF), + ), + ); late Timer countdown; late Timer interval; diff --git a/examples/lib/stories/utils/timer_component.dart b/examples/lib/stories/utils/timer_component.dart index 743fadeb3..c59e01558 100644 --- a/examples/lib/stories/utils/timer_component.dart +++ b/examples/lib/stories/utils/timer_component.dart @@ -4,13 +4,17 @@ import 'package:flame/timer.dart'; import 'package:flame/gestures.dart'; class RenderedTimeComponent extends TimerComponent { - final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF)); + final TextPaint textPaint = TextPaint( + config: const TextPaintConfig( + color: Color(0xFFFFFFFF), + ), + ); RenderedTimeComponent(Timer timer) : super(timer); @override void render(Canvas canvas) { - textConfig.render( + textPaint.render( canvas, 'Elapsed time: ${timer.current}', Vector2(10, 150), diff --git a/packages/flame/CHANGELOG.md b/packages/flame/CHANGELOG.md index fdd34c5a8..3d9acaf81 100644 --- a/packages/flame/CHANGELOG.md +++ b/packages/flame/CHANGELOG.md @@ -9,6 +9,7 @@ - Add a new renderRect method to Sprite - Addresses the TODO to change the camera public APIs to take Anchors for relativePositions - Adds methods to support moving the camera relative to its current position + - Abstracting the text api to allow custom text renderers on the framework ## [1.0.0-rc9] - Fix input bug with other anchors than center diff --git a/packages/flame/lib/components.dart b/packages/flame/lib/components.dart index 4c26f96d0..97153bb86 100644 --- a/packages/flame/lib/components.dart +++ b/packages/flame/lib/components.dart @@ -21,5 +21,5 @@ export 'src/components/sprite_component.dart'; export 'src/components/text_box_component.dart'; export 'src/components/text_component.dart'; export 'src/extensions/vector2.dart'; -export 'src/text_config.dart'; +export 'src/text.dart'; export 'src/timer.dart'; diff --git a/packages/flame/lib/game.dart b/packages/flame/lib/game.dart index bbfe74dbf..aaee02417 100644 --- a/packages/flame/lib/game.dart +++ b/packages/flame/lib/game.dart @@ -6,4 +6,4 @@ export 'src/game/game.dart'; export 'src/game/game_widget/game_widget.dart'; export 'src/game/projector.dart'; export 'src/game/viewport.dart'; -export 'src/text_config.dart'; +export 'src/text.dart'; diff --git a/packages/flame/lib/src/components/base_component.dart b/packages/flame/lib/src/components/base_component.dart index 3c108f8b4..3933eac90 100644 --- a/packages/flame/lib/src/components/base_component.dart +++ b/packages/flame/lib/src/components/base_component.dart @@ -9,7 +9,7 @@ import '../../game.dart'; import '../effects/effects.dart'; import '../effects/effects_handler.dart'; import '../extensions/vector2.dart'; -import '../text_config.dart'; +import '../text.dart'; import 'component.dart'; import 'mixins/has_game_ref.dart'; @@ -50,7 +50,12 @@ abstract class BaseComponent extends Component { ..strokeWidth = 1 ..style = PaintingStyle.stroke; - TextConfig get debugTextConfig => TextConfig(color: debugColor, fontSize: 12); + TextPaint get debugTextPaint => TextPaint( + config: TextPaintConfig( + color: debugColor, + fontSize: 12, + ), + ); /// This method is called periodically by the game engine to request that your component updates itself. /// diff --git a/packages/flame/lib/src/components/position_component.dart b/packages/flame/lib/src/components/position_component.dart index 3be5240a2..d393abfa0 100644 --- a/packages/flame/lib/src/components/position_component.dart +++ b/packages/flame/lib/src/components/position_component.dart @@ -156,7 +156,7 @@ abstract class PositionComponent extends BaseComponent { (this as Hitbox).renderShapes(canvas); } canvas.drawRect(size.toRect(), debugPaint); - debugTextConfig.render( + debugTextPaint.render( canvas, 'x: ${x.toStringAsFixed(2)} y:${y.toStringAsFixed(2)}', Vector2(-50, -15), @@ -165,7 +165,7 @@ abstract class PositionComponent extends BaseComponent { final rect = toRect(); final dx = rect.right; final dy = rect.bottom; - debugTextConfig.render( + debugTextPaint.render( canvas, 'x:${dx.toStringAsFixed(2)} y:${dy.toStringAsFixed(2)}', Vector2(width - 50, height), diff --git a/packages/flame/lib/src/components/text_box_component.dart b/packages/flame/lib/src/components/text_box_component.dart index a12a9df32..cf1aea601 100644 --- a/packages/flame/lib/src/components/text_box_component.dart +++ b/packages/flame/lib/src/components/text_box_component.dart @@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart' hide Image; import '../extensions/vector2.dart'; import '../palette.dart'; -import '../text_config.dart'; +import '../text.dart'; import 'position_component.dart'; class TextBoxConfig { @@ -31,7 +31,7 @@ class TextBoxComponent extends PositionComponent { Vector2 _gameSize = Vector2.zero(); final String _text; - final TextConfig _config; + final TextRenderer _textRenderer; final TextBoxConfig _boxConfig; late List _lines; @@ -45,37 +45,37 @@ class TextBoxComponent extends PositionComponent { String get text => _text; - TextConfig get config => _config; + TextRenderer get renderer => _textRenderer; TextBoxConfig get boxConfig => _boxConfig; TextBoxComponent( String text, { - TextConfig? config, + TextRenderer? textRenderer, TextBoxConfig? boxConfig, Vector2? position, Vector2? size, }) : _text = text, _boxConfig = boxConfig ?? TextBoxConfig(), - _config = config ?? TextConfig(), + _textRenderer = textRenderer ?? TextPaint(), super(position: position, size: size) { _lines = []; double? lineHeight; text.split(' ').forEach((word) { final possibleLine = _lines.isEmpty ? word : '${_lines.last} $word'; - final painter = _config.toTextPainter(possibleLine); - lineHeight ??= painter.height; - if (painter.width <= - _boxConfig.maxWidth - _boxConfig.margins.horizontal) { + lineHeight ??= _textRenderer.measureTextHeight(possibleLine); + + final textWidth = _textRenderer.measureTextWidth(possibleLine); + if (textWidth <= _boxConfig.maxWidth - _boxConfig.margins.horizontal) { if (_lines.isNotEmpty) { _lines.last = possibleLine; } else { _lines.add(possibleLine); } - _updateMaxWidth(painter.width); + _updateMaxWidth(textWidth); } else { _lines.add(word); - _updateMaxWidth(_config.toTextPainter(word).width); + _updateMaxWidth(textWidth); } }); _totalLines = _lines.length; @@ -112,9 +112,12 @@ class TextBoxComponent extends PositionComponent { Vector2 get size => Vector2(width, height); double getLineWidth(String line, int charCount) { - return _config - .toTextPainter(line.substring(0, math.min(charCount, line.length))) - .width; + return _textRenderer.measureTextWidth( + line.substring( + 0, + math.min(charCount, line.length), + ), + ); } double? _cachedWidth; @@ -192,7 +195,7 @@ class TextBoxComponent extends PositionComponent { } void _drawLine(Canvas c, String line, double dy) { - _config.toTextPainter(line).paint(c, Offset(_boxConfig.margins.left, dy)); + _textRenderer.render(c, line, Vector2(_boxConfig.margins.left, dy)); } void redrawLater() async { diff --git a/packages/flame/lib/src/components/text_component.dart b/packages/flame/lib/src/components/text_component.dart index 296c3f0f6..371583336 100644 --- a/packages/flame/lib/src/components/text_component.dart +++ b/packages/flame/lib/src/components/text_component.dart @@ -4,14 +4,12 @@ import 'package:flutter/painting.dart'; import 'package:meta/meta.dart'; import '../extensions/vector2.dart'; -import '../text_config.dart'; +import '../text.dart'; import 'position_component.dart'; class TextComponent extends PositionComponent { String _text; - TextConfig _config; - - late TextPainter _tp; + TextRenderer _textRenderer; String get text => _text; @@ -22,32 +20,32 @@ class TextComponent extends PositionComponent { } } - TextConfig get config => _config; + TextRenderer get textRenderer => _textRenderer; - set config(TextConfig config) { - _config = config; + set textRenderer(TextRenderer textRenderer) { + _textRenderer = textRenderer; _updateBox(); } TextComponent( this._text, { - TextConfig? config, + TextRenderer? textRenderer, Vector2? position, Vector2? size, - }) : _config = config ?? TextConfig(), + }) : _textRenderer = textRenderer ?? TextPaint(), super(position: position, size: size) { _updateBox(); } void _updateBox() { - _tp = config.toTextPainter(_text); - size.setValues(_tp.width, _tp.height); + final size = textRenderer.measureText(_text); + size.setValues(size.x, size.y); } @mustCallSuper @override - void render(Canvas c) { - super.render(c); - _tp.paint(c, Offset.zero); + void render(Canvas canvas) { + super.render(canvas); + _textRenderer.render(canvas, text, Vector2.zero()); } } diff --git a/packages/flame/lib/src/text_config.dart b/packages/flame/lib/src/text.dart similarity index 57% rename from packages/flame/lib/src/text_config.dart rename to packages/flame/lib/src/text.dart index 5a28ad041..20db55fe5 100644 --- a/packages/flame/lib/src/text_config.dart +++ b/packages/flame/lib/src/text.dart @@ -8,22 +8,76 @@ import 'extensions/size.dart'; import 'extensions/vector2.dart'; import 'memory_cache.dart'; -/// A Text Config contains all typographical information required to render texts; i.e., font size and color, family, etc. +/// [TextRenderer] is the abstract API that Flame uses for rendering text in its features +/// this class can be extended to provide an implementation of text rendering in the engine. /// -/// It does not hold information regarding the position of the text to be render neither the text itself (the string). -/// To hold all those information, use the Text component. -/// -/// It is used by [TextComponent]. -class TextConfig { +/// See [TextPaint] for the default implementation offered by Flame +abstract class TextRenderer { + final T config; + + TextRenderer({required this.config}); + + /// Renders a given [text] in a given position [position] using the provided [canvas] and [anchor]. + /// + /// Renders it in the given position, considering the [anchor] specified. + /// For example, if [Anchor.center] is specified, it's going to be drawn centered around [position]. + /// + /// Example usage (Using TextPaint implementation): + /// + /// const TextPaint config = TextPaint(fontSize: 48.0, fontFamily: 'Awesome Font'); + /// config.render(canvas, Vector2(size.x - 10, size.y - 10, anchor: Anchor.bottomRight); + void render( + Canvas canvas, + String text, + Vector2 position, { + Anchor anchor = Anchor.topLeft, + }); + + /// Given a [text] String, returns the width of that [text]. + double measureTextWidth(String text); + + /// Given a [text] String, returns the height of that [text]. + double measureTextHeight(String text); + + /// Given a [text] String, returns a Vector2 with the size of that [text] has. + Vector2 measureText(String text) { + return Vector2( + measureTextWidth(text), + measureTextHeight(text), + ); + } +} + +/// A Text Config contains all typographical information required to render texts; i.e., font size, text direction, etc. +abstract class BaseTextConfig { /// The font size to be used, in points. final double fontSize; + /// The direction to render this text (left to right or right to left). + /// + /// Normally, leave this as is for most languages. + /// For proper fonts of languages like Hebrew or Arabic, replace this with [TextDirection.rtl]. + final TextDirection textDirection; + + /// The height of line, as a multiple of font size. + final double? lineHeight; + + const BaseTextConfig({ + this.fontSize = 24.0, + this.textDirection = TextDirection.ltr, + this.lineHeight, + }); +} + +/// An extension of the BaseTextConfig which includes more configs supported by +/// TextPaint +class TextPaintConfig extends BaseTextConfig { /// The font color to be used. /// /// Dart's [Color] class is just a plain wrapper on top of ARGB color (0xAARRGGBB). /// For example, /// - /// const TextConfig config = TextConfig(color: const Color(0xFF00FF00)); // green + /// const TextPaint config = TextPaint(color: const Color(0xFF00FF00)); // green /// /// You can also use your Palette class to access colors used in your game. final Color color; @@ -44,45 +98,97 @@ class TextConfig { /// The name you choose for the font family can be any name (it's not inside the TTF file and the filename doesn't need to match). final String fontFamily; - /// The [TextAlign] to be used when creating the [material.TextPainter]. + /// Creates a constant [TextPaint] with sensible defaults. /// - /// Beware: it's recommended to leave this with the default value of [TextAlign.left]. - /// Use the anchor parameter to [render] to specify a proper relative position. - final TextAlign textAlign; + /// Every parameter can be specified. + const TextPaintConfig({ + this.color = const Color(0xFF000000), + this.fontFamily = 'Arial', + double fontSize = 24.0, + TextDirection textDirection = TextDirection.rtl, + double? lineHeight, + }) : super( + fontSize: fontSize, + textDirection: textDirection, + lineHeight: lineHeight, + ); - /// The direction to render this text (left to right or right to left). + /// Creates a new [TextPaintConfig] changing only the [fontSize]. /// - /// Normally, leave this as is for most languages. - /// For proper fonts of languages like Hebrew or Arabic, replace this with [TextDirection.rtl]. - final TextDirection textDirection; + /// This does not change the original (as it's immutable). + TextPaintConfig withFontSize(double fontSize) { + return TextPaintConfig( + fontSize: fontSize, + color: color, + fontFamily: fontFamily, + textDirection: textDirection, + ); + } - /// The height of line, as a multiple of font size. - final double? lineHeight; + /// Creates a new [TextPaintConfig] changing only the [color]. + /// + /// This does not change the original (as it's immutable). + TextPaintConfig withColor(Color color) { + return TextPaintConfig( + fontSize: fontSize, + color: color, + fontFamily: fontFamily, + textDirection: textDirection, + ); + } + /// Creates a new [TextPaintConfig] changing only the [fontFamily]. + /// + /// This does not change the original (as it's immutable). + TextPaintConfig withFontFamily(String fontFamily) { + return TextPaintConfig( + fontSize: fontSize, + color: color, + fontFamily: fontFamily, + textDirection: textDirection, + ); + } + + /// Creates a new [TextPaintConfig] changing only the [textAlign]. + /// + /// This does not change the original (as it's immutable). + TextPaintConfig withTextAlign(TextAlign textAlign) { + return TextPaintConfig( + fontSize: fontSize, + color: color, + fontFamily: fontFamily, + textDirection: textDirection, + ); + } + + /// Creates a new [TextPaintConfig] changing only the [textDirection]. + /// + /// This does not change the original (as it's immutable). + TextPaintConfig withTextDirection(TextDirection textDirection) { + return TextPaintConfig( + fontSize: fontSize, + color: color, + fontFamily: fontFamily, + textDirection: textDirection, + ); + } +} + +/// A Text Config contains all typographical information required to render texts; i.e., font size and color, family, etc. +/// +/// It does not hold information regarding the position of the text to be render neither the text itself (the string). +/// To hold all those information, use the Text component. +/// +/// It is used by [TextComponent]. +class TextPaint extends TextRenderer { final MemoryCache _textPainterCache = MemoryCache(); - /// Creates a constant [TextConfig] with sensible defaults. - /// - /// Every parameter can be specified. - TextConfig({ - this.fontSize = 24.0, - this.color = const Color(0xFF000000), - this.fontFamily = 'Arial', - this.textAlign = TextAlign.left, - this.textDirection = TextDirection.ltr, - this.lineHeight, - }); + TextPaint({ + TextPaintConfig config = const TextPaintConfig(), + }) : super(config: config); - /// Renders a given [text] in a given position [p] using the provided [canvas] and [anchor]. - /// - /// It creates a [material.TextPainter] instance using the [toTextPainter] method, and renders it in the given position, considering the [anchor] specified. - /// For example, if [Anchor.center] is specified, it's going to be drawn centered around [p]. - /// - /// Example usage: - /// - /// const TextConfig config = TextConfig(fontSize: 48.0, fontFamily: 'Awesome Font'); - /// config.render(c, Offset(size.width - 10, size.height - 10, anchor: Anchor.bottomRight); + @override void render( Canvas canvas, String text, @@ -94,13 +200,23 @@ class TextConfig { tp.paint(canvas, translatedPosition.toOffset()); } + @override + double measureTextWidth(String text) { + return toTextPainter(text).width; + } + + @override + double measureTextHeight(String text) { + return toTextPainter(text).height; + } + /// Returns a [material.TextPainter] that allows for text rendering and size measuring. /// /// A [material.TextPainter] has three important properties: paint, width and height (or size). /// /// Example usage: /// - /// const TextConfig config = TextConfig(fontSize: 48.0, fontFamily: 'Awesome Font'); + /// const TextPaint config = TextPaint(fontSize: 48.0, fontFamily: 'Awesome Font'); /// final tp = config.toTextPainter('Score: $score'); /// tp.paint(c, Offset(size.width - p.width - 10, size.height - p.height - 10)); /// @@ -109,10 +225,10 @@ class TextConfig { material.TextPainter toTextPainter(String text) { if (!_textPainterCache.containsKey(text)) { final style = material.TextStyle( - color: color, - fontSize: fontSize, - fontFamily: fontFamily, - height: lineHeight, + color: config.color, + fontSize: config.fontSize, + fontFamily: config.fontFamily, + height: config.lineHeight, ); final span = material.TextSpan( style: style, @@ -120,8 +236,7 @@ class TextConfig { ); final tp = material.TextPainter( text: span, - textAlign: textAlign, - textDirection: textDirection, + textDirection: config.textDirection, ); tp.layout(); @@ -129,69 +244,4 @@ class TextConfig { } return _textPainterCache.getValue(text)!; } - - /// Creates a new [TextConfig] changing only the [fontSize]. - /// - /// This does not change the original (as it's immutable). - TextConfig withFontSize(double fontSize) { - return TextConfig( - fontSize: fontSize, - color: color, - fontFamily: fontFamily, - textAlign: textAlign, - textDirection: textDirection, - ); - } - - /// Creates a new [TextConfig] changing only the [color]. - /// - /// This does not change the original (as it's immutable). - TextConfig withColor(Color color) { - return TextConfig( - fontSize: fontSize, - color: color, - fontFamily: fontFamily, - textAlign: textAlign, - textDirection: textDirection, - ); - } - - /// Creates a new [TextConfig] changing only the [fontFamily]. - /// - /// This does not change the original (as it's immutable). - TextConfig withFontFamily(String fontFamily) { - return TextConfig( - fontSize: fontSize, - color: color, - fontFamily: fontFamily, - textAlign: textAlign, - textDirection: textDirection, - ); - } - - /// Creates a new [TextConfig] changing only the [textAlign]. - /// - /// This does not change the original (as it's immutable). - TextConfig withTextAlign(TextAlign textAlign) { - return TextConfig( - fontSize: fontSize, - color: color, - fontFamily: fontFamily, - textAlign: textAlign, - textDirection: textDirection, - ); - } - - /// Creates a new [TextConfig] changing only the [textDirection]. - /// - /// This does not change the original (as it's immutable). - TextConfig withTextDirection(TextDirection textDirection) { - return TextConfig( - fontSize: fontSize, - color: color, - fontFamily: fontFamily, - textAlign: textAlign, - textDirection: textDirection, - ); - } }