diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index 7c06292c00886..d74f68982c04a 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -1470,7 +1470,13 @@ class SkParagraphBuilder { external void pushPaintStyle( SkTextStyle textStyle, SkPaint foreground, SkPaint background); external void pop(); - external void addPlaceholder(SkPlaceholderStyleProperties placeholderStyle); + external void addPlaceholder( + double width, + double height, + SkPlaceholderAlignment alignment, + SkTextBaseline baseline, + double offset, + ); external SkParagraph build(); external void delete(); } @@ -1606,16 +1612,6 @@ class SkStrutStyleProperties { external set forceStrutHeight(bool? value); } -@JS() -@anonymous -class SkPlaceholderStyleProperties { - external set width(double? value); - external set height(double? value); - external set alignment(SkPlaceholderAlignment? value); - external set offset(double? value); - external set baseline(SkTextBaseline? value); -} - @JS() @anonymous class SkFontStyle { diff --git a/lib/web_ui/lib/src/engine/canvaskit/text.dart b/lib/web_ui/lib/src/engine/canvaskit/text.dart index cb4773abdaec2..41d1215a7f4e3 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/text.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/text.dart @@ -629,7 +629,7 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { _placeholderCount++; _placeholderScales.add(scale); - SkPlaceholderStyleProperties placeholderStyle = toSkPlaceholderStyle( + final _CkParagraphPlaceholder placeholderStyle = toSkPlaceholderStyle( width * scale, height * scale, alignment, @@ -639,24 +639,31 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { _addPlaceholder(placeholderStyle); } - void _addPlaceholder(SkPlaceholderStyleProperties placeholderStyle) { + void _addPlaceholder(_CkParagraphPlaceholder placeholderStyle) { _commands.add(_ParagraphCommand.addPlaceholder(placeholderStyle)); - _paragraphBuilder.addPlaceholder(placeholderStyle); + _paragraphBuilder.addPlaceholder( + placeholderStyle.width, + placeholderStyle.height, + placeholderStyle.alignment, + placeholderStyle.baseline, + placeholderStyle.offset, + ); } - static SkPlaceholderStyleProperties toSkPlaceholderStyle( + static _CkParagraphPlaceholder toSkPlaceholderStyle( double width, double height, ui.PlaceholderAlignment alignment, double baselineOffset, ui.TextBaseline baseline, ) { - final properties = SkPlaceholderStyleProperties(); - properties.width = width; - properties.height = height; - properties.alignment = toSkPlaceholderAlignment(alignment); - properties.offset = baselineOffset; - properties.baseline = toSkTextBaseline(baseline); + final properties = _CkParagraphPlaceholder( + width: width, + height: height, + alignment: toSkPlaceholderAlignment(alignment), + offset: baselineOffset, + baseline: toSkTextBaseline(baseline), + ); return properties; } @@ -722,11 +729,27 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { } } +class _CkParagraphPlaceholder { + _CkParagraphPlaceholder({ + required this.width, + required this.height, + required this.alignment, + required this.baseline, + required this.offset, + }); + + final double width; + final double height; + final SkPlaceholderAlignment alignment; + final SkTextBaseline baseline; + final double offset; +} + class _ParagraphCommand { final _ParagraphCommandType type; final String? text; final CkTextStyle? style; - final SkPlaceholderStyleProperties? placeholderStyle; + final _CkParagraphPlaceholder? placeholderStyle; const _ParagraphCommand._( this.type, @@ -745,7 +768,7 @@ class _ParagraphCommand { : this._(_ParagraphCommandType.pushStyle, null, style, null); const _ParagraphCommand.addPlaceholder( - SkPlaceholderStyleProperties placeholderStyle) + _CkParagraphPlaceholder placeholderStyle) : this._( _ParagraphCommandType.addPlaceholder, null, null, placeholderStyle); } diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index 03f401d597758..dea114a7b0282 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -11,6 +11,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import '../matchers.dart'; import 'common.dart'; import 'test_data.dart'; @@ -47,6 +48,7 @@ void testMain() { _toSkMatrixFromFloat32Tests(); _toSkRectTests(); _skVerticesTests(); + _paragraphTests(); group('SkPath', () { _pathTests(); }); @@ -1229,3 +1231,110 @@ void _textStyleTests() { } }); } + +void _paragraphTests() { + // This test is just a kitchen sink that blasts CanvasKit with all paragraph + // properties all at once, making sure CanvasKit doesn't choke on anything. + // In particular, this tests that our JS bindings are correct, such as that + // arguments are of acceptable types and passed in the correct order. + test('SkParagraph API kitchensink', () { + final SkParagraphStyleProperties props = SkParagraphStyleProperties(); + props.textAlign = canvasKit.TextAlign.Center; + props.textDirection = canvasKit.TextDirection.RTL; + props.heightMultiplier = 3; + props.textHeightBehavior = ui.TextHeightBehavior().encode(); + props.maxLines = 4; + props.ellipsis = '___'; + props.textStyle = SkTextStyleProperties() + ..backgroundColor = Float32List.fromList([1, 2, 3, 4]) + ..color = Float32List.fromList([5, 6, 7, 8]) + ..foregroundColor = Float32List.fromList([9, 10, 11, 12]) + ..decoration = 0x2 + ..decorationThickness = 2.0 + ..decorationColor = Float32List.fromList([13, 14, 15, 16]) + ..decorationStyle = canvasKit.DecorationStyle.Dotted + ..textBaseline = canvasKit.TextBaseline.Ideographic + ..fontSize = 24 + ..letterSpacing = 5 + ..wordSpacing = 10 + ..heightMultiplier = 2.5 + ..locale = 'en_CA' + ..fontFamilies = ['Roboto', 'serif'] + ..fontStyle = (SkFontStyle() + ..slant = canvasKit.FontSlant.Upright + ..weight = canvasKit.FontWeight.Normal) + ..shadows = [] + ..fontFeatures = [ + SkFontFeature() + ..name = 'pnum' + ..value = 1, + SkFontFeature() + ..name = 'tnum' + ..value = 1, + ]; + props.strutStyle = SkStrutStyleProperties() + ..fontFamilies = ['Roboto', 'Noto'] + ..fontStyle = (SkFontStyle() + ..slant = canvasKit.FontSlant.Italic + ..weight = canvasKit.FontWeight.Bold) + ..fontSize = 23 + ..heightMultiplier = 5 + ..leading = 6 + ..strutEnabled = true + ..forceStrutHeight = false; + + final SkParagraphStyle paragraphStyle = canvasKit.ParagraphStyle(props); + final SkParagraphBuilder builder = canvasKit.ParagraphBuilder.Make( + paragraphStyle, + skiaFontCollection.skFontMgr, + ); + + builder.addText('Hello'); + builder.addPlaceholder( + 50, + 25, + canvasKit.PlaceholderAlignment.Middle, + canvasKit.TextBaseline.Ideographic, + 4.0, + ); + builder.pushStyle(canvasKit.TextStyle(SkTextStyleProperties() + ..fontSize = 12)); + builder.addText('World'); + builder.pop(); + builder.pushPaintStyle(canvasKit.TextStyle(SkTextStyleProperties() + ..fontSize = 12), SkPaint(), SkPaint()); + builder.addText('!'); + builder.pop(); + final SkParagraph paragraph = builder.build(); + paragraph.layout(55); + expect(paragraph.getAlphabeticBaseline(), within(distance: 0.5, from: 22)); + expect(paragraph.didExceedMaxLines(), false); + expect(paragraph.getHeight(), 28); + expect(paragraph.getIdeographicBaseline(), within(distance: 0.5, from: 28)); + expect(paragraph.getLongestLine(), 50); + expect(paragraph.getMaxIntrinsicWidth(), 50); + expect(paragraph.getMinIntrinsicWidth(), 0); + expect(paragraph.getMaxWidth(), 55); + expect(paragraph.getRectsForRange(1, 3, canvasKit.RectHeightStyle.Tight, canvasKit.RectWidthStyle.Max), []); + expect(paragraph.getRectsForPlaceholders(), hasLength(1)); + expect(paragraph.getGlyphPositionAtCoordinate(5, 5).affinity, canvasKit.Affinity.Downstream); + + // "Hello" + for (int i = 0; i < 5; i++) { + expect(paragraph.getWordBoundary(i).start, 0); + expect(paragraph.getWordBoundary(i).end, 5); + } + // Placeholder + expect(paragraph.getWordBoundary(5).start, 5); + expect(paragraph.getWordBoundary(5).end, 6); + // "World" + for (int i = 6; i < 11; i++) { + expect(paragraph.getWordBoundary(i).start, 6); + expect(paragraph.getWordBoundary(i).end, 11); + } + // "!" + expect(paragraph.getWordBoundary(11).start, 11); + expect(paragraph.getWordBoundary(11).end, 12); + paragraph.delete(); + }); +}