From ffc33554487f1f4b5f509dbbcd8efff3e3b68408 Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Thu, 18 Apr 2024 10:05:09 -0400 Subject: [PATCH 1/3] feat: Add accessor to determine a TextElement size --- .../components/text_element_component.dart | 11 +-- .../lib/src/text/elements/group_element.dart | 13 +++- .../text/elements/inline_text_element.dart | 3 + .../lib/src/text/elements/rect_element.dart | 3 + .../lib/src/text/elements/rrect_element.dart | 3 + .../lib/src/text/elements/text_element.dart | 3 + .../components/element_component_test.dart | 33 +++++++-- .../flame/test/text/text_element_test.dart | 69 +++++++++++++++++++ 8 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 packages/flame/test/text/text_element_test.dart diff --git a/packages/flame/lib/src/components/text_element_component.dart b/packages/flame/lib/src/components/text_element_component.dart index 6e57e1fe92b..c1e385513fc 100644 --- a/packages/flame/lib/src/components/text_element_component.dart +++ b/packages/flame/lib/src/components/text_element_component.dart @@ -1,15 +1,16 @@ -import 'dart:ui'; - import 'package:flame/components.dart'; +import 'package:flame/extensions.dart'; import 'package:flame/text.dart'; class TextElementComponent extends PositionComponent { + final Vector2? documentSize; TextElement element; TextElementComponent({ required this.element, - super.position, + this.documentSize, super.size, + super.position, super.scale, super.angle, super.anchor, @@ -37,10 +38,12 @@ class TextElementComponent extends PositionComponent { width: effectiveSize.x, height: effectiveSize.y, ); + final box = element.boundingBox; return TextElementComponent( element: element, position: position, - size: effectiveSize, + documentSize: effectiveSize, + size: box.bottomRight.toVector2(), scale: scale, angle: angle, anchor: anchor, diff --git a/packages/flame/lib/src/text/elements/group_element.dart b/packages/flame/lib/src/text/elements/group_element.dart index 0038a69cd3c..c0f51420c6b 100644 --- a/packages/flame/lib/src/text/elements/group_element.dart +++ b/packages/flame/lib/src/text/elements/group_element.dart @@ -1,5 +1,5 @@ +import 'package:flame/extensions.dart'; import 'package:flame/text.dart'; -import 'package:flutter/rendering.dart' hide TextStyle; class GroupElement extends BlockElement { GroupElement({ @@ -19,4 +19,15 @@ class GroupElement extends BlockElement { void draw(Canvas canvas) { children.forEach((child) => child.draw(canvas)); } + + @override + Rect get boundingBox { + return children.fold( + null, + (previousValue, element) { + final box = element.boundingBox; + return previousValue?.expandToInclude(box) ?? box; + }, + ) ?? Rect.zero; + } } diff --git a/packages/flame/lib/src/text/elements/inline_text_element.dart b/packages/flame/lib/src/text/elements/inline_text_element.dart index 157c1cbb87a..5ad2b94aae3 100644 --- a/packages/flame/lib/src/text/elements/inline_text_element.dart +++ b/packages/flame/lib/src/text/elements/inline_text_element.dart @@ -20,4 +20,7 @@ abstract class InlineTextElement extends TextElement { ); draw(canvas); } + + @override + Rect get boundingBox => metrics.toRect(); } diff --git a/packages/flame/lib/src/text/elements/rect_element.dart b/packages/flame/lib/src/text/elements/rect_element.dart index b206e2e9859..a120971beb8 100644 --- a/packages/flame/lib/src/text/elements/rect_element.dart +++ b/packages/flame/lib/src/text/elements/rect_element.dart @@ -18,4 +18,7 @@ class RectElement extends TextElement { void draw(Canvas canvas) { canvas.drawRect(_rect, _paint); } + + @override + Rect get boundingBox => _rect; } diff --git a/packages/flame/lib/src/text/elements/rrect_element.dart b/packages/flame/lib/src/text/elements/rrect_element.dart index 0bc998edb73..4c81145e97a 100644 --- a/packages/flame/lib/src/text/elements/rrect_element.dart +++ b/packages/flame/lib/src/text/elements/rrect_element.dart @@ -22,4 +22,7 @@ class RRectElement extends TextElement { void draw(Canvas canvas) { canvas.drawRRect(_rrect, _paint); } + + @override + Rect get boundingBox => _rrect.outerRect; } diff --git a/packages/flame/lib/src/text/elements/text_element.dart b/packages/flame/lib/src/text/elements/text_element.dart index 2c40faddc7f..1b90f095288 100644 --- a/packages/flame/lib/src/text/elements/text_element.dart +++ b/packages/flame/lib/src/text/elements/text_element.dart @@ -1,4 +1,5 @@ import 'dart:ui'; +import 'package:flame/extensions.dart'; import 'package:flame/text.dart'; /// An [TextElement] is a basic rendering block of a rich-text document. @@ -21,4 +22,6 @@ abstract class TextElement { /// calling the [translate] method, or applying a translation transform to the /// canvas itself. void draw(Canvas canvas); + + Rect get boundingBox; } diff --git a/packages/flame/test/components/element_component_test.dart b/packages/flame/test/components/element_component_test.dart index 3700726704d..3794b57140e 100644 --- a/packages/flame/test/components/element_component_test.dart +++ b/packages/flame/test/components/element_component_test.dart @@ -4,29 +4,35 @@ import 'package:test/test.dart'; void main() { group('ElementComponent', () { - test('size can be specified via the size parameter', () { + test('document size can be specified via the size parameter', () { final c = TextElementComponent.fromDocument( document: DocumentRoot([]), size: Vector2(100, 200), ); - expect(c.size, equals(Vector2(100, 200))); + expect(c.documentSize, equals(Vector2(100, 200))); + expect(c.size, equals(Vector2.zero())); }); - test('size can be specified via the style', () { + + test('document size can be specified via the style', () { final c = TextElementComponent.fromDocument( document: DocumentRoot([]), style: DocumentStyle(width: 100, height: 200), ); - expect(c.size, equals(Vector2(100, 200))); + expect(c.documentSize, equals(Vector2(100, 200))); + expect(c.size, equals(Vector2.zero())); }); - test('size can be super-specified if matching', () { + + test('document size can be super-specified if matching', () { final c = TextElementComponent.fromDocument( document: DocumentRoot([]), style: DocumentStyle(width: 100, height: 200), size: Vector2(100, 200), ); - expect(c.size, equals(Vector2(100, 200))); + expect(c.documentSize, equals(Vector2(100, 200))); + expect(c.size, equals(Vector2.zero())); }); - test('size must be specified', () { + + test('document size must be specified', () { expect( () { TextElementComponent.fromDocument( @@ -42,6 +48,7 @@ void main() { ), ); }); + test('size cannot be over-specified if mismatched', () { expect( () { @@ -60,5 +67,17 @@ void main() { ), ); }); + + test('size is computed from the element bounding box', () { + final c = TextElementComponent.fromDocument( + document: DocumentRoot([ + ParagraphNode.simple('line 1'), + ParagraphNode.simple('line 2'), + ]), + size: Vector2(800, 160), // oversized + ); + expect(c.documentSize, equals(Vector2(800, 160))); + expect(c.size, equals(Vector2(96, 38))); + }); }); } diff --git a/packages/flame/test/text/text_element_test.dart b/packages/flame/test/text/text_element_test.dart new file mode 100644 index 00000000000..d5fc044fac5 --- /dev/null +++ b/packages/flame/test/text/text_element_test.dart @@ -0,0 +1,69 @@ +import 'package:flame/src/text/elements/group_element.dart'; +import 'package:flame/text.dart'; +import 'package:flutter/rendering.dart'; +import 'package:test/test.dart'; + +void main() { + group('text elements', () { + test('bounding box for empty group', () { + final emptyGroup = GroupElement(width: 0, height: 0, children: []); + expect(emptyGroup.boundingBox, Rect.zero); + }); + + test('bounding box for inline elements', () { + final document = DocumentRoot([ + ParagraphNode.group([ + PlainTextNode('Hello'), + ]), + ]); + + final element1 = document.format( + DocumentStyle( + paragraph: const BlockStyle( + margin: EdgeInsets.zero, + padding: EdgeInsets.zero, + ), + ), + width: 80, + height: 16, + ); + const expected = Rect.fromLTWH(0, 0, 80, 16); + + expect(element1.boundingBox, expected); + final element2 = element1.children.single as GroupElement; + expect(element2.boundingBox, expected); + final element3 = element2.children.single as InlineTextElement; + expect(element3.boundingBox, expected); + }); + + + test('bounding box is composed', () { + final document = DocumentRoot([ + ParagraphNode.group([ + PlainTextNode('Hello, '), + PlainTextNode('there'), + ]), + ParagraphNode.group([ + ItalicTextNode.simple('General '), + BoldTextNode.simple('Kenobi'), + ]), + ]); + + final element1 = document.format( + DocumentStyle( + paragraph: const BlockStyle( + margin: EdgeInsets.zero, + padding: EdgeInsets.zero, + ), + ), + width: 600, + height: 400, + ); + expect(element1.boundingBox, const Rect.fromLTWH(0, 0, 224, 32)); + final element2 = element1.children[0] as GroupElement; + expect(element2.boundingBox, const Rect.fromLTWH(0, 0, 192, 16)); + final element3 = element1.children[1] as GroupElement; + expect(element3.boundingBox, const Rect.fromLTWH(0, 16, 224, 16)); + }); + }); +} From c410345024ffc22e8b63ce58cd94f43df72f7214 Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Thu, 18 Apr 2024 10:14:51 -0400 Subject: [PATCH 2/3] fix --- .github/.cspell/flame_dictionary.txt | 1 + .../flame/lib/src/text/elements/group_element.dart | 13 +++++++------ packages/flame/test/text/text_element_test.dart | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/.cspell/flame_dictionary.txt b/.github/.cspell/flame_dictionary.txt index dd5097f531b..81814197d92 100644 --- a/.github/.cspell/flame_dictionary.txt +++ b/.github/.cspell/flame_dictionary.txt @@ -8,6 +8,7 @@ Dashbook # UI development tool for Flutter https://github.com/bluefireteam/dashb emberquest # Ember Quest, our platformer tutorial game Hermione # A character from the book Harry Potter Kawabunga # Word expressing exhilaration, of unclear origins but popularized by the show Teenage Mutant Ninja Turtles +Kenobi # Eminent Jedi Master, General of the Republic Army, Obi-Wan Kenobi Mocktail # A library for Dart that automatically creates mocks for tests https://github.com/felangel/mocktail Nakama # An open-source server designed to power modern games and apps https://github.com/Allan-Nava/nakama-flutter Overmind # A character in the game StarCraft diff --git a/packages/flame/lib/src/text/elements/group_element.dart b/packages/flame/lib/src/text/elements/group_element.dart index c0f51420c6b..da8624dc907 100644 --- a/packages/flame/lib/src/text/elements/group_element.dart +++ b/packages/flame/lib/src/text/elements/group_element.dart @@ -23,11 +23,12 @@ class GroupElement extends BlockElement { @override Rect get boundingBox { return children.fold( - null, - (previousValue, element) { - final box = element.boundingBox; - return previousValue?.expandToInclude(box) ?? box; - }, - ) ?? Rect.zero; + null, + (previousValue, element) { + final box = element.boundingBox; + return previousValue?.expandToInclude(box) ?? box; + }, + ) ?? + Rect.zero; } } diff --git a/packages/flame/test/text/text_element_test.dart b/packages/flame/test/text/text_element_test.dart index d5fc044fac5..b3b842520a9 100644 --- a/packages/flame/test/text/text_element_test.dart +++ b/packages/flame/test/text/text_element_test.dart @@ -36,7 +36,6 @@ void main() { expect(element3.boundingBox, expected); }); - test('bounding box is composed', () { final document = DocumentRoot([ ParagraphNode.group([ From 2f3ea72259e3842a4d193d20a35d6759341c9623 Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Thu, 18 Apr 2024 10:23:28 -0400 Subject: [PATCH 3/3] fix --- packages/flame/test/components/element_component_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flame/test/components/element_component_test.dart b/packages/flame/test/components/element_component_test.dart index 3794b57140e..a974507b649 100644 --- a/packages/flame/test/components/element_component_test.dart +++ b/packages/flame/test/components/element_component_test.dart @@ -77,7 +77,7 @@ void main() { size: Vector2(800, 160), // oversized ); expect(c.documentSize, equals(Vector2(800, 160))); - expect(c.size, equals(Vector2(96, 38))); + expect(c.size, equals(Vector2(96, 44))); }); }); }