Unverified Commit 8fd20b5d authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Text Painting Fuzzer (#12813)

Various improvements (in particular a new painting fuzzer) to the text manual test.

Some additional documentation.

A fix to Stack to remove an LTR bias: make unpositioned children apply "alignment".

Some more debugging information on RichText and Text.

A fix to the flutter tool to not crash when an RPC call throws an exception.
parent f4b0ccd9
...@@ -6,6 +6,7 @@ import 'dart:math' as math; ...@@ -6,6 +6,7 @@ import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
int seed = 0; int seed = 0;
...@@ -18,7 +19,8 @@ void main() { ...@@ -18,7 +19,8 @@ void main() {
'underlines': (BuildContext context) => const Underlines(), 'underlines': (BuildContext context) => const Underlines(),
'fallback': (BuildContext context) => const Fallback(), 'fallback': (BuildContext context) => const Fallback(),
'fuzzer': (BuildContext context) => new Fuzzer(seed: seed), 'fuzzer': (BuildContext context) => new Fuzzer(seed: seed),
'zalgo': (BuildContext context) => const Zalgo(), 'zalgo': (BuildContext context) => new Zalgo(seed: seed),
'painting': (BuildContext context) => new Painting(seed: seed),
}, },
)); ));
} }
...@@ -64,6 +66,12 @@ class _HomeState extends State<Home> { ...@@ -64,6 +66,12 @@ class _HomeState extends State<Home> {
textColor: Colors.white, textColor: Colors.white,
onPressed: () { Navigator.of(context).pushNamed('zalgo'); }, onPressed: () { Navigator.of(context).pushNamed('zalgo'); },
), ),
new FlatButton(
child: const Text('Painting Fuzzer'),
color: Colors.blueGrey.shade200,
textColor: Colors.black,
onPressed: () { Navigator.of(context).pushNamed('painting'); },
),
], ],
), ),
), ),
...@@ -329,7 +337,7 @@ class _FuzzerState extends State<Fuzzer> with SingleTickerProviderStateMixin { ...@@ -329,7 +337,7 @@ class _FuzzerState extends State<Fuzzer> with SingleTickerProviderStateMixin {
} }
String _createRandomText() { String _createRandomText() {
switch (_random.nextInt(80)) { switch (_random.nextInt(90)) {
case 0: case 0:
case 1: case 1:
case 2: case 2:
...@@ -448,6 +456,25 @@ class _FuzzerState extends State<Fuzzer> with SingleTickerProviderStateMixin { ...@@ -448,6 +456,25 @@ class _FuzzerState extends State<Fuzzer> with SingleTickerProviderStateMixin {
return '⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌'; return '⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌';
case 73: case 73:
return 'コンニチハ'; return 'コンニチハ';
case 74:
case 75:
case 76:
case 77:
case 78:
case 79:
case 80:
case 81:
case 82:
final StringBuffer buffer = new StringBuffer();
final int targetLength = _random.nextInt(8) + 1;
for (int index = 0; index < targetLength; index += 1) {
if (_random.nextInt(20) > 0) {
buffer.writeCharCode(randomCharacter(_random));
} else {
buffer.write(zalgo(_random, _random.nextInt(2) + 1, includeSpacingCombiningMarks: true));
}
}
return buffer.toString();
} }
return null; return null;
} }
...@@ -641,7 +668,7 @@ class _FallbackState extends State<Fallback> { ...@@ -641,7 +668,7 @@ class _FallbackState extends State<Fallback> {
), ),
new Material( new Material(
child: new Padding( child: new Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0), padding: const EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0),
child: new Row( child: new Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
...@@ -701,10 +728,16 @@ class _ZalgoState extends State<Zalgo> with SingleTickerProviderStateMixin { ...@@ -701,10 +728,16 @@ class _ZalgoState extends State<Zalgo> with SingleTickerProviderStateMixin {
} }
bool _allowSpacing = false; bool _allowSpacing = false;
bool _varyBase = false;
void _update(Duration duration) { void _update(Duration duration) {
setState(() { setState(() {
_text = zalgo(_random, 6 + _random.nextInt(10), includeSpacingCombiningMarks: _allowSpacing); _text = zalgo(
_random,
6 + _random.nextInt(10),
includeSpacingCombiningMarks: _allowSpacing,
base: _varyBase ? null : 'O',
);
}); });
} }
...@@ -750,6 +783,17 @@ class _ZalgoState extends State<Zalgo> with SingleTickerProviderStateMixin { ...@@ -750,6 +783,17 @@ class _ZalgoState extends State<Zalgo> with SingleTickerProviderStateMixin {
onChanged: (bool value) { onChanged: (bool value) {
setState(() { setState(() {
_allowSpacing = value; _allowSpacing = value;
_random = new math.Random(widget.seed); // reset for reproducability
});
},
),
new SwitchListTile(
title: const Text('Vary base character'),
value: _varyBase,
onChanged: (bool value) {
setState(() {
_varyBase = value;
_random = new math.Random(widget.seed); // reset for reproducability
}); });
}, },
), ),
...@@ -762,7 +806,179 @@ class _ZalgoState extends State<Zalgo> with SingleTickerProviderStateMixin { ...@@ -762,7 +806,179 @@ class _ZalgoState extends State<Zalgo> with SingleTickerProviderStateMixin {
} }
} }
String zalgo(math.Random random, int target, { bool includeSpacingCombiningMarks: false }) { class Painting extends StatefulWidget {
const Painting({ Key key, this.seed }) : super(key: key);
final int seed;
@override
_PaintingState createState() => new _PaintingState();
}
class _PaintingState extends State<Painting> with SingleTickerProviderStateMixin {
String _text;
Ticker _ticker;
math.Random _random;
@override
void initState() {
super.initState();
_random = new math.Random(widget.seed); // providing a seed is important for reproducability
_ticker = createTicker(_update)..start();
_update(null);
}
@override
void dispose() {
_ticker.dispose();
super.dispose();
}
final GlobalKey intrinsicKey = new GlobalKey();
final GlobalKey controlKey = new GlobalKey();
bool _ellipsize = false;
void _update(Duration duration) {
setState(() {
final StringBuffer buffer = new StringBuffer();
final int targetLength = _random.nextInt(20) + (_ellipsize ? MediaQuery.of(context).size.width.round() : 1);
for (int index = 0; index < targetLength; index += 1) {
if (_random.nextInt(5) > 0) {
buffer.writeCharCode(randomCharacter(_random));
} else {
buffer.write(zalgo(_random, _random.nextInt(2) + 1, includeSpacingCombiningMarks: true));
}
}
_text = buffer.toString();
});
SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
if (mounted && intrinsicKey.currentContext.size.height != controlKey.currentContext.size.height) {
debugPrint('Found some text that unexpectedly renders at different heights.');
debugPrint('Text: $_text');
debugPrint(_text.runes.map((int index) => 'U+' + index.toRadixString(16).padLeft(4, '0')).join(' '));
setState(() {
_ticker.stop();
});
}
});
}
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return new Container(
color: Colors.black,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Expanded(
child: new Padding(
padding: new EdgeInsets.only(top: size.height * 0.1),
child: new Stack(
alignment: Alignment.center,
children: <Widget>[
new Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: new Align(
alignment: Alignment.topCenter,
child: new IntrinsicWidth( // to test shrink-wrap vs rendering
child: new RichText(
key: intrinsicKey,
textAlign: TextAlign.center,
overflow: _ellipsize ? TextOverflow.ellipsis : TextOverflow.clip,
text: new TextSpan(
text: _text,
style: const TextStyle(
inherit: false,
fontSize: 28.0,
color: Colors.red,
),
),
),
),
),
),
new Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: new RichText(
key: controlKey,
textAlign: TextAlign.center,
overflow: _ellipsize ? TextOverflow.ellipsis : TextOverflow.clip,
text: new TextSpan(
text: _text,
style: const TextStyle(
inherit: false,
fontSize: 28.0,
color: Colors.white,
),
),
),
),
],
),
),
),
new Material(
child: new Column(
children: <Widget>[
new SwitchListTile(
title: const Text('Enable Fuzzer'),
value: _ticker.isActive,
onChanged: (bool value) {
setState(() {
if (value) {
_ticker.start();
} else {
_ticker.stop();
}
});
},
),
new SwitchListTile(
title: const Text('Enable Ellipses'),
value: _ellipsize,
onChanged: (bool value) {
setState(() {
_ellipsize = value;
_random = new math.Random(widget.seed); // reset for reproducability
if (!_ticker.isActive)
_update(null);
});
},
),
const ListTile(
title: const Text('There should be no red visible.'),
),
new ButtonBar(
children: <Widget>[
new FlatButton(
onPressed: _ticker.isActive ? null : () => _update(null),
child: const Text('ITERATE'),
),
new FlatButton(
onPressed: _ticker.isActive ? null : () {
print('The currently visible text is: $_text');
print(_text.runes.map((int value) => 'U+${value.toRadixString(16).padLeft(4, '0').toUpperCase()}').join(' '));
},
child: const Text('DUMP TEXT TO LOGS'),
),
],
),
],
),
),
],
),
);
}
}
String zalgo(math.Random random, int targetLength, { bool includeSpacingCombiningMarks: false, String base }) {
// The following three tables are derived from UnicodeData.txt: // The following three tables are derived from UnicodeData.txt:
// http://unicode.org/Public/UNIDATA/UnicodeData.txt // http://unicode.org/Public/UNIDATA/UnicodeData.txt
// There are three groups, character classes Mc, Me, and Mn. // There are three groups, character classes Mc, Me, and Mn.
...@@ -1088,7 +1304,7 @@ String zalgo(math.Random random, int target, { bool includeSpacingCombiningMarks ...@@ -1088,7 +1304,7 @@ String zalgo(math.Random random, int target, { bool includeSpacingCombiningMarks
int combiningCount = enclosingCombiningMarks.length + nonspacingCombiningMarks.length; int combiningCount = enclosingCombiningMarks.length + nonspacingCombiningMarks.length;
if (includeSpacingCombiningMarks) if (includeSpacingCombiningMarks)
combiningCount += spacingCombiningMarks.length; combiningCount += spacingCombiningMarks.length;
for (int count = 0; count < target; count += 1) { for (int count = 0; count < targetLength; count += 1) {
int characterCode = random.nextInt(combiningCount); int characterCode = random.nextInt(combiningCount);
if (characterCode < enclosingCombiningMarks.length) { if (characterCode < enclosingCombiningMarks.length) {
these.add(enclosingCombiningMarks[characterCode]); these.add(enclosingCombiningMarks[characterCode]);
...@@ -1102,11 +1318,717 @@ String zalgo(math.Random random, int target, { bool includeSpacingCombiningMarks ...@@ -1102,11 +1318,717 @@ String zalgo(math.Random random, int target, { bool includeSpacingCombiningMarks
} }
} }
} }
final List<int> characters = <int>[0x41 + random.nextInt(26)]; base ??= new String.fromCharCode(randomCharacter(random));
final List<int> characters = <int>[];
characters.addAll(these); characters.addAll(these);
return new String.fromCharCodes(characters); return base + new String.fromCharCodes(characters);
} }
T pickFromList<T>(math.Random random, List<T> list) { T pickFromList<T>(math.Random random, List<T> list) {
return list[random.nextInt(list.length)]; return list[random.nextInt(list.length)];
} }
class Range {
const Range(this.start, this.end);
final int start;
final int end;
}
int randomCharacter(math.Random random) {
// all ranges of non-control, non-combining characters
const List<Range> characterRanges = const <Range>[
const Range(0x00020, 0x0007e),
const Range(0x000a0, 0x000ac),
const Range(0x000ae, 0x002ff),
const Range(0x00370, 0x00377),
const Range(0x0037a, 0x0037f),
const Range(0x00384, 0x0038a),
const Range(0x0038c, 0x0038c),
const Range(0x0038e, 0x003a1),
const Range(0x003a3, 0x00482),
const Range(0x0048a, 0x0052f),
const Range(0x00531, 0x00556),
const Range(0x00559, 0x0055f),
const Range(0x00561, 0x00587),
const Range(0x00589, 0x0058a),
const Range(0x0058d, 0x0058f),
const Range(0x005be, 0x005be),
const Range(0x005c0, 0x005c0),
const Range(0x005c3, 0x005c3),
const Range(0x005c6, 0x005c6),
const Range(0x005d0, 0x005ea),
const Range(0x005f0, 0x005f4),
const Range(0x00606, 0x0060f),
const Range(0x0061b, 0x0061b),
const Range(0x0061e, 0x0064a),
const Range(0x00660, 0x0066f),
const Range(0x00671, 0x006d5),
const Range(0x006de, 0x006de),
const Range(0x006e5, 0x006e6),
const Range(0x006e9, 0x006e9),
const Range(0x006ee, 0x0070d),
const Range(0x00710, 0x00710),
const Range(0x00712, 0x0072f),
const Range(0x0074d, 0x007a5),
const Range(0x007b1, 0x007b1),
const Range(0x007c0, 0x007ea),
const Range(0x007f4, 0x007fa),
const Range(0x00800, 0x00815),
const Range(0x0081a, 0x0081a),
const Range(0x00824, 0x00824),
const Range(0x00828, 0x00828),
const Range(0x00830, 0x0083e),
const Range(0x00840, 0x00858),
const Range(0x0085e, 0x0085e),
const Range(0x00860, 0x0086a),
const Range(0x008a0, 0x008b4),
const Range(0x008b6, 0x008bd),
const Range(0x00904, 0x00939),
const Range(0x0093d, 0x0093d),
const Range(0x00950, 0x00950),
const Range(0x00958, 0x00961),
const Range(0x00964, 0x00980),
const Range(0x00985, 0x0098c),
const Range(0x0098f, 0x00990),
const Range(0x00993, 0x009a8),
const Range(0x009aa, 0x009b0),
const Range(0x009b2, 0x009b2),
const Range(0x009b6, 0x009b9),
const Range(0x009bd, 0x009bd),
const Range(0x009ce, 0x009ce),
const Range(0x009dc, 0x009dd),
const Range(0x009df, 0x009e1),
const Range(0x009e6, 0x009fd),
const Range(0x00a05, 0x00a0a),
const Range(0x00a0f, 0x00a10),
const Range(0x00a13, 0x00a28),
const Range(0x00a2a, 0x00a30),
const Range(0x00a32, 0x00a33),
const Range(0x00a35, 0x00a36),
const Range(0x00a38, 0x00a39),
const Range(0x00a59, 0x00a5c),
const Range(0x00a5e, 0x00a5e),
const Range(0x00a66, 0x00a6f),
const Range(0x00a72, 0x00a74),
const Range(0x00a85, 0x00a8d),
const Range(0x00a8f, 0x00a91),
const Range(0x00a93, 0x00aa8),
const Range(0x00aaa, 0x00ab0),
const Range(0x00ab2, 0x00ab3),
const Range(0x00ab5, 0x00ab9),
const Range(0x00abd, 0x00abd),
const Range(0x00ad0, 0x00ad0),
const Range(0x00ae0, 0x00ae1),
const Range(0x00ae6, 0x00af1),
const Range(0x00af9, 0x00af9),
const Range(0x00b05, 0x00b0c),
const Range(0x00b0f, 0x00b10),
const Range(0x00b13, 0x00b28),
const Range(0x00b2a, 0x00b30),
const Range(0x00b32, 0x00b33),
const Range(0x00b35, 0x00b39),
const Range(0x00b3d, 0x00b3d),
const Range(0x00b5c, 0x00b5d),
const Range(0x00b5f, 0x00b61),
const Range(0x00b66, 0x00b77),
const Range(0x00b83, 0x00b83),
const Range(0x00b85, 0x00b8a),
const Range(0x00b8e, 0x00b90),
const Range(0x00b92, 0x00b95),
const Range(0x00b99, 0x00b9a),
const Range(0x00b9c, 0x00b9c),
const Range(0x00b9e, 0x00b9f),
const Range(0x00ba3, 0x00ba4),
const Range(0x00ba8, 0x00baa),
const Range(0x00bae, 0x00bb9),
const Range(0x00bd0, 0x00bd0),
const Range(0x00be6, 0x00bfa),
const Range(0x00c05, 0x00c0c),
const Range(0x00c0e, 0x00c10),
const Range(0x00c12, 0x00c28),
const Range(0x00c2a, 0x00c39),
const Range(0x00c3d, 0x00c3d),
const Range(0x00c58, 0x00c5a),
const Range(0x00c60, 0x00c61),
const Range(0x00c66, 0x00c6f),
const Range(0x00c78, 0x00c80),
const Range(0x00c85, 0x00c8c),
const Range(0x00c8e, 0x00c90),
const Range(0x00c92, 0x00ca8),
const Range(0x00caa, 0x00cb3),
const Range(0x00cb5, 0x00cb9),
const Range(0x00cbd, 0x00cbd),
const Range(0x00cde, 0x00cde),
const Range(0x00ce0, 0x00ce1),
const Range(0x00ce6, 0x00cef),
const Range(0x00cf1, 0x00cf2),
const Range(0x00d05, 0x00d0c),
const Range(0x00d0e, 0x00d10),
const Range(0x00d12, 0x00d3a),
const Range(0x00d3d, 0x00d3d),
const Range(0x00d4e, 0x00d4f),
const Range(0x00d54, 0x00d56),
const Range(0x00d58, 0x00d61),
const Range(0x00d66, 0x00d7f),
const Range(0x00d85, 0x00d96),
const Range(0x00d9a, 0x00db1),
const Range(0x00db3, 0x00dbb),
const Range(0x00dbd, 0x00dbd),
const Range(0x00dc0, 0x00dc6),
const Range(0x00de6, 0x00def),
const Range(0x00df4, 0x00df4),
const Range(0x00e01, 0x00e30),
const Range(0x00e32, 0x00e33),
const Range(0x00e3f, 0x00e46),
const Range(0x00e4f, 0x00e5b),
const Range(0x00e81, 0x00e82),
const Range(0x00e84, 0x00e84),
const Range(0x00e87, 0x00e88),
const Range(0x00e8a, 0x00e8a),
const Range(0x00e8d, 0x00e8d),
const Range(0x00e94, 0x00e97),
const Range(0x00e99, 0x00e9f),
const Range(0x00ea1, 0x00ea3),
const Range(0x00ea5, 0x00ea5),
const Range(0x00ea7, 0x00ea7),
const Range(0x00eaa, 0x00eab),
const Range(0x00ead, 0x00eb0),
const Range(0x00eb2, 0x00eb3),
const Range(0x00ebd, 0x00ebd),
const Range(0x00ec0, 0x00ec4),
const Range(0x00ec6, 0x00ec6),
const Range(0x00ed0, 0x00ed9),
const Range(0x00edc, 0x00edf),
const Range(0x00f00, 0x00f17),
const Range(0x00f1a, 0x00f34),
const Range(0x00f36, 0x00f36),
const Range(0x00f38, 0x00f38),
const Range(0x00f3a, 0x00f3d),
const Range(0x00f40, 0x00f47),
const Range(0x00f49, 0x00f6c),
const Range(0x00f85, 0x00f85),
const Range(0x00f88, 0x00f8c),
const Range(0x00fbe, 0x00fc5),
const Range(0x00fc7, 0x00fcc),
const Range(0x00fce, 0x00fda),
const Range(0x01000, 0x0102a),
const Range(0x0103f, 0x01055),
const Range(0x0105a, 0x0105d),
const Range(0x01061, 0x01061),
const Range(0x01065, 0x01066),
const Range(0x0106e, 0x01070),
const Range(0x01075, 0x01081),
const Range(0x0108e, 0x0108e),
const Range(0x01090, 0x01099),
const Range(0x0109e, 0x010c5),
const Range(0x010c7, 0x010c7),
const Range(0x010cd, 0x010cd),
const Range(0x010d0, 0x01248),
const Range(0x0124a, 0x0124d),
const Range(0x01250, 0x01256),
const Range(0x01258, 0x01258),
const Range(0x0125a, 0x0125d),
const Range(0x01260, 0x01288),
const Range(0x0128a, 0x0128d),
const Range(0x01290, 0x012b0),
const Range(0x012b2, 0x012b5),
const Range(0x012b8, 0x012be),
const Range(0x012c0, 0x012c0),
const Range(0x012c2, 0x012c5),
const Range(0x012c8, 0x012d6),
const Range(0x012d8, 0x01310),
const Range(0x01312, 0x01315),
const Range(0x01318, 0x0135a),
const Range(0x01360, 0x0137c),
const Range(0x01380, 0x01399),
const Range(0x013a0, 0x013f5),
const Range(0x013f8, 0x013fd),
const Range(0x01400, 0x0169c),
const Range(0x016a0, 0x016f8),
const Range(0x01700, 0x0170c),
const Range(0x0170e, 0x01711),
const Range(0x01720, 0x01731),
const Range(0x01735, 0x01736),
const Range(0x01740, 0x01751),
const Range(0x01760, 0x0176c),
const Range(0x0176e, 0x01770),
const Range(0x01780, 0x017b3),
const Range(0x017d4, 0x017dc),
const Range(0x017e0, 0x017e9),
const Range(0x017f0, 0x017f9),
const Range(0x01800, 0x0180a),
const Range(0x01810, 0x01819),
const Range(0x01820, 0x01877),
const Range(0x01880, 0x01884),
const Range(0x01887, 0x018a8),
const Range(0x018aa, 0x018aa),
const Range(0x018b0, 0x018f5),
const Range(0x01900, 0x0191e),
const Range(0x01940, 0x01940),
const Range(0x01944, 0x0196d),
const Range(0x01970, 0x01974),
const Range(0x01980, 0x019ab),
const Range(0x019b0, 0x019c9),
const Range(0x019d0, 0x019da),
const Range(0x019de, 0x01a16),
const Range(0x01a1e, 0x01a54),
const Range(0x01a80, 0x01a89),
const Range(0x01a90, 0x01a99),
const Range(0x01aa0, 0x01aad),
const Range(0x01b05, 0x01b33),
const Range(0x01b45, 0x01b4b),
const Range(0x01b50, 0x01b6a),
const Range(0x01b74, 0x01b7c),
const Range(0x01b83, 0x01ba0),
const Range(0x01bae, 0x01be5),
const Range(0x01bfc, 0x01c23),
const Range(0x01c3b, 0x01c49),
const Range(0x01c4d, 0x01c88),
const Range(0x01cc0, 0x01cc7),
const Range(0x01cd3, 0x01cd3),
const Range(0x01ce9, 0x01cec),
const Range(0x01cee, 0x01cf1),
const Range(0x01cf5, 0x01cf6),
const Range(0x01d00, 0x01dbf),
const Range(0x01e00, 0x01f15),
const Range(0x01f18, 0x01f1d),
const Range(0x01f20, 0x01f45),
const Range(0x01f48, 0x01f4d),
const Range(0x01f50, 0x01f57),
const Range(0x01f59, 0x01f59),
const Range(0x01f5b, 0x01f5b),
const Range(0x01f5d, 0x01f5d),
const Range(0x01f5f, 0x01f7d),
const Range(0x01f80, 0x01fb4),
const Range(0x01fb6, 0x01fc4),
const Range(0x01fc6, 0x01fd3),
const Range(0x01fd6, 0x01fdb),
const Range(0x01fdd, 0x01fef),
const Range(0x01ff2, 0x01ff4),
const Range(0x01ff6, 0x01ffe),
const Range(0x02000, 0x0200a),
const Range(0x02010, 0x02029),
const Range(0x0202f, 0x0205f),
const Range(0x02070, 0x02071),
const Range(0x02074, 0x0208e),
const Range(0x02090, 0x0209c),
const Range(0x020a0, 0x020bf),
const Range(0x02100, 0x0218b),
const Range(0x02190, 0x02426),
const Range(0x02440, 0x0244a),
const Range(0x02460, 0x02b73),
const Range(0x02b76, 0x02b95),
const Range(0x02b98, 0x02bb9),
const Range(0x02bbd, 0x02bc8),
const Range(0x02bca, 0x02bd2),
const Range(0x02bec, 0x02bef),
const Range(0x02c00, 0x02c2e),
const Range(0x02c30, 0x02c5e),
const Range(0x02c60, 0x02cee),
const Range(0x02cf2, 0x02cf3),
const Range(0x02cf9, 0x02d25),
const Range(0x02d27, 0x02d27),
const Range(0x02d2d, 0x02d2d),
const Range(0x02d30, 0x02d67),
const Range(0x02d6f, 0x02d70),
const Range(0x02d80, 0x02d96),
const Range(0x02da0, 0x02da6),
const Range(0x02da8, 0x02dae),
const Range(0x02db0, 0x02db6),
const Range(0x02db8, 0x02dbe),
const Range(0x02dc0, 0x02dc6),
const Range(0x02dc8, 0x02dce),
const Range(0x02dd0, 0x02dd6),
const Range(0x02dd8, 0x02dde),
const Range(0x02e00, 0x02e49),
const Range(0x02e80, 0x02e99),
const Range(0x02e9b, 0x02ef3),
const Range(0x02f00, 0x02fd5),
const Range(0x02ff0, 0x02ffb),
const Range(0x03000, 0x03029),
const Range(0x03030, 0x0303f),
const Range(0x03041, 0x03096),
const Range(0x0309b, 0x030ff),
const Range(0x03105, 0x0312e),
const Range(0x03131, 0x0318e),
const Range(0x03190, 0x031ba),
const Range(0x031c0, 0x031e3),
const Range(0x031f0, 0x0321e),
const Range(0x03220, 0x032fe),
const Range(0x03300, 0x04db5),
const Range(0x04dc0, 0x09fea),
const Range(0x0a000, 0x0a48c),
const Range(0x0a490, 0x0a4c6),
const Range(0x0a4d0, 0x0a62b),
const Range(0x0a640, 0x0a66e),
const Range(0x0a673, 0x0a673),
const Range(0x0a67e, 0x0a69d),
const Range(0x0a6a0, 0x0a6ef),
const Range(0x0a6f2, 0x0a6f7),
const Range(0x0a700, 0x0a7ae),
const Range(0x0a7b0, 0x0a7b7),
const Range(0x0a7f7, 0x0a801),
const Range(0x0a803, 0x0a805),
const Range(0x0a807, 0x0a80a),
const Range(0x0a80c, 0x0a822),
const Range(0x0a828, 0x0a82b),
const Range(0x0a830, 0x0a839),
const Range(0x0a840, 0x0a877),
const Range(0x0a882, 0x0a8b3),
const Range(0x0a8ce, 0x0a8d9),
const Range(0x0a8f2, 0x0a8fd),
const Range(0x0a900, 0x0a925),
const Range(0x0a92e, 0x0a946),
const Range(0x0a95f, 0x0a97c),
const Range(0x0a984, 0x0a9b2),
const Range(0x0a9c1, 0x0a9cd),
const Range(0x0a9cf, 0x0a9d9),
const Range(0x0a9de, 0x0a9e4),
const Range(0x0a9e6, 0x0a9fe),
const Range(0x0aa00, 0x0aa28),
const Range(0x0aa40, 0x0aa42),
const Range(0x0aa44, 0x0aa4b),
const Range(0x0aa50, 0x0aa59),
const Range(0x0aa5c, 0x0aa7a),
const Range(0x0aa7e, 0x0aaaf),
const Range(0x0aab1, 0x0aab1),
const Range(0x0aab5, 0x0aab6),
const Range(0x0aab9, 0x0aabd),
const Range(0x0aac0, 0x0aac0),
const Range(0x0aac2, 0x0aac2),
const Range(0x0aadb, 0x0aaea),
const Range(0x0aaf0, 0x0aaf4),
const Range(0x0ab01, 0x0ab06),
const Range(0x0ab09, 0x0ab0e),
const Range(0x0ab11, 0x0ab16),
const Range(0x0ab20, 0x0ab26),
const Range(0x0ab28, 0x0ab2e),
const Range(0x0ab30, 0x0ab65),
const Range(0x0ab70, 0x0abe2),
const Range(0x0abeb, 0x0abeb),
const Range(0x0abf0, 0x0abf9),
const Range(0x0ac00, 0x0d7a3),
const Range(0x0d7b0, 0x0d7c6),
const Range(0x0d7cb, 0x0d7fb),
const Range(0x0f900, 0x0fa6d),
const Range(0x0fa70, 0x0fad9),
const Range(0x0fb00, 0x0fb06),
const Range(0x0fb13, 0x0fb17),
const Range(0x0fb1d, 0x0fb1d),
const Range(0x0fb1f, 0x0fb36),
const Range(0x0fb38, 0x0fb3c),
const Range(0x0fb3e, 0x0fb3e),
const Range(0x0fb40, 0x0fb41),
const Range(0x0fb43, 0x0fb44),
const Range(0x0fb46, 0x0fbc1),
const Range(0x0fbd3, 0x0fd3f),
const Range(0x0fd50, 0x0fd8f),
const Range(0x0fd92, 0x0fdc7),
const Range(0x0fdf0, 0x0fdfd),
const Range(0x0fe10, 0x0fe19),
const Range(0x0fe30, 0x0fe52),
const Range(0x0fe54, 0x0fe66),
const Range(0x0fe68, 0x0fe6b),
const Range(0x0fe70, 0x0fe74),
const Range(0x0fe76, 0x0fefc),
const Range(0x0ff01, 0x0ffbe),
const Range(0x0ffc2, 0x0ffc7),
const Range(0x0ffca, 0x0ffcf),
const Range(0x0ffd2, 0x0ffd7),
const Range(0x0ffda, 0x0ffdc),
const Range(0x0ffe0, 0x0ffe6),
const Range(0x0ffe8, 0x0ffee),
const Range(0x0fffc, 0x0fffd),
const Range(0x10000, 0x1000b),
const Range(0x1000d, 0x10026),
const Range(0x10028, 0x1003a),
const Range(0x1003c, 0x1003d),
const Range(0x1003f, 0x1004d),
const Range(0x10050, 0x1005d),
const Range(0x10080, 0x100fa),
const Range(0x10100, 0x10102),
const Range(0x10107, 0x10133),
const Range(0x10137, 0x1018e),
const Range(0x10190, 0x1019b),
const Range(0x101a0, 0x101a0),
const Range(0x101d0, 0x101fc),
const Range(0x10280, 0x1029c),
const Range(0x102a0, 0x102d0),
const Range(0x102e1, 0x102fb),
const Range(0x10300, 0x10323),
const Range(0x1032d, 0x1034a),
const Range(0x10350, 0x10375),
const Range(0x10380, 0x1039d),
const Range(0x1039f, 0x103c3),
const Range(0x103c8, 0x103d5),
const Range(0x10400, 0x1049d),
const Range(0x104a0, 0x104a9),
const Range(0x104b0, 0x104d3),
const Range(0x104d8, 0x104fb),
const Range(0x10500, 0x10527),
const Range(0x10530, 0x10563),
const Range(0x1056f, 0x1056f),
const Range(0x10600, 0x10736),
const Range(0x10740, 0x10755),
const Range(0x10760, 0x10767),
const Range(0x10800, 0x10805),
const Range(0x10808, 0x10808),
const Range(0x1080a, 0x10835),
const Range(0x10837, 0x10838),
const Range(0x1083c, 0x1083c),
const Range(0x1083f, 0x10855),
const Range(0x10857, 0x1089e),
const Range(0x108a7, 0x108af),
const Range(0x108e0, 0x108f2),
const Range(0x108f4, 0x108f5),
const Range(0x108fb, 0x1091b),
const Range(0x1091f, 0x10939),
const Range(0x1093f, 0x1093f),
const Range(0x10980, 0x109b7),
const Range(0x109bc, 0x109cf),
const Range(0x109d2, 0x10a00),
const Range(0x10a10, 0x10a13),
const Range(0x10a15, 0x10a17),
const Range(0x10a19, 0x10a33),
const Range(0x10a40, 0x10a47),
const Range(0x10a50, 0x10a58),
const Range(0x10a60, 0x10a9f),
const Range(0x10ac0, 0x10ae4),
const Range(0x10aeb, 0x10af6),
const Range(0x10b00, 0x10b35),
const Range(0x10b39, 0x10b55),
const Range(0x10b58, 0x10b72),
const Range(0x10b78, 0x10b91),
const Range(0x10b99, 0x10b9c),
const Range(0x10ba9, 0x10baf),
const Range(0x10c00, 0x10c48),
const Range(0x10c80, 0x10cb2),
const Range(0x10cc0, 0x10cf2),
const Range(0x10cfa, 0x10cff),
const Range(0x10e60, 0x10e7e),
const Range(0x11003, 0x11037),
const Range(0x11047, 0x1104d),
const Range(0x11052, 0x1106f),
const Range(0x11083, 0x110af),
const Range(0x110bb, 0x110bc),
const Range(0x110be, 0x110c1),
const Range(0x110d0, 0x110e8),
const Range(0x110f0, 0x110f9),
const Range(0x11103, 0x11126),
const Range(0x11136, 0x11143),
const Range(0x11150, 0x11172),
const Range(0x11174, 0x11176),
const Range(0x11183, 0x111b2),
const Range(0x111c1, 0x111c9),
const Range(0x111cd, 0x111cd),
const Range(0x111d0, 0x111df),
const Range(0x111e1, 0x111f4),
const Range(0x11200, 0x11211),
const Range(0x11213, 0x1122b),
const Range(0x11238, 0x1123d),
const Range(0x11280, 0x11286),
const Range(0x11288, 0x11288),
const Range(0x1128a, 0x1128d),
const Range(0x1128f, 0x1129d),
const Range(0x1129f, 0x112a9),
const Range(0x112b0, 0x112de),
const Range(0x112f0, 0x112f9),
const Range(0x11305, 0x1130c),
const Range(0x1130f, 0x11310),
const Range(0x11313, 0x11328),
const Range(0x1132a, 0x11330),
const Range(0x11332, 0x11333),
const Range(0x11335, 0x11339),
const Range(0x1133d, 0x1133d),
const Range(0x11350, 0x11350),
const Range(0x1135d, 0x11361),
const Range(0x11400, 0x11434),
const Range(0x11447, 0x11459),
const Range(0x1145b, 0x1145b),
const Range(0x1145d, 0x1145d),
const Range(0x11480, 0x114af),
const Range(0x114c4, 0x114c7),
const Range(0x114d0, 0x114d9),
const Range(0x11580, 0x115ae),
const Range(0x115c1, 0x115db),
const Range(0x11600, 0x1162f),
const Range(0x11641, 0x11644),
const Range(0x11650, 0x11659),
const Range(0x11660, 0x1166c),
const Range(0x11680, 0x116aa),
const Range(0x116c0, 0x116c9),
const Range(0x11700, 0x11719),
const Range(0x11730, 0x1173f),
const Range(0x118a0, 0x118f2),
const Range(0x118ff, 0x118ff),
const Range(0x11a00, 0x11a00),
const Range(0x11a0b, 0x11a32),
const Range(0x11a3a, 0x11a3a),
const Range(0x11a3f, 0x11a46),
const Range(0x11a50, 0x11a50),
const Range(0x11a5c, 0x11a83),
const Range(0x11a86, 0x11a89),
const Range(0x11a9a, 0x11a9c),
const Range(0x11a9e, 0x11aa2),
const Range(0x11ac0, 0x11af8),
const Range(0x11c00, 0x11c08),
const Range(0x11c0a, 0x11c2e),
const Range(0x11c40, 0x11c45),
const Range(0x11c50, 0x11c6c),
const Range(0x11c70, 0x11c8f),
const Range(0x11d00, 0x11d06),
const Range(0x11d08, 0x11d09),
const Range(0x11d0b, 0x11d30),
const Range(0x11d46, 0x11d46),
const Range(0x11d50, 0x11d59),
const Range(0x12000, 0x12399),
const Range(0x12400, 0x1246e),
const Range(0x12470, 0x12474),
const Range(0x12480, 0x12543),
const Range(0x13000, 0x1342e),
const Range(0x14400, 0x14646),
const Range(0x16800, 0x16a38),
const Range(0x16a40, 0x16a5e),
const Range(0x16a60, 0x16a69),
const Range(0x16a6e, 0x16a6f),
const Range(0x16ad0, 0x16aed),
const Range(0x16af5, 0x16af5),
const Range(0x16b00, 0x16b2f),
const Range(0x16b37, 0x16b45),
const Range(0x16b50, 0x16b59),
const Range(0x16b5b, 0x16b61),
const Range(0x16b63, 0x16b77),
const Range(0x16b7d, 0x16b8f),
const Range(0x16f00, 0x16f44),
const Range(0x16f50, 0x16f50),
const Range(0x16f93, 0x16f9f),
const Range(0x16fe0, 0x16fe1),
const Range(0x17000, 0x187ec),
const Range(0x18800, 0x18af2),
const Range(0x1b000, 0x1b11e),
const Range(0x1b170, 0x1b2fb),
const Range(0x1bc00, 0x1bc6a),
const Range(0x1bc70, 0x1bc7c),
const Range(0x1bc80, 0x1bc88),
const Range(0x1bc90, 0x1bc99),
const Range(0x1bc9c, 0x1bc9c),
const Range(0x1bc9f, 0x1bc9f),
const Range(0x1d000, 0x1d0f5),
const Range(0x1d100, 0x1d126),
const Range(0x1d129, 0x1d164),
const Range(0x1d16a, 0x1d16c),
const Range(0x1d183, 0x1d184),
const Range(0x1d18c, 0x1d1a9),
const Range(0x1d1ae, 0x1d1e8),
const Range(0x1d200, 0x1d241),
const Range(0x1d245, 0x1d245),
const Range(0x1d300, 0x1d356),
const Range(0x1d360, 0x1d371),
const Range(0x1d400, 0x1d454),
const Range(0x1d456, 0x1d49c),
const Range(0x1d49e, 0x1d49f),
const Range(0x1d4a2, 0x1d4a2),
const Range(0x1d4a5, 0x1d4a6),
const Range(0x1d4a9, 0x1d4ac),
const Range(0x1d4ae, 0x1d4b9),
const Range(0x1d4bb, 0x1d4bb),
const Range(0x1d4bd, 0x1d4c3),
const Range(0x1d4c5, 0x1d505),
const Range(0x1d507, 0x1d50a),
const Range(0x1d50d, 0x1d514),
const Range(0x1d516, 0x1d51c),
const Range(0x1d51e, 0x1d539),
const Range(0x1d53b, 0x1d53e),
const Range(0x1d540, 0x1d544),
const Range(0x1d546, 0x1d546),
const Range(0x1d54a, 0x1d550),
const Range(0x1d552, 0x1d6a5),
const Range(0x1d6a8, 0x1d7cb),
const Range(0x1d7ce, 0x1d9ff),
const Range(0x1da37, 0x1da3a),
const Range(0x1da6d, 0x1da74),
const Range(0x1da76, 0x1da83),
const Range(0x1da85, 0x1da8b),
const Range(0x1e800, 0x1e8c4),
const Range(0x1e8c7, 0x1e8cf),
const Range(0x1e900, 0x1e943),
const Range(0x1e950, 0x1e959),
const Range(0x1e95e, 0x1e95f),
const Range(0x1ee00, 0x1ee03),
const Range(0x1ee05, 0x1ee1f),
const Range(0x1ee21, 0x1ee22),
const Range(0x1ee24, 0x1ee24),
const Range(0x1ee27, 0x1ee27),
const Range(0x1ee29, 0x1ee32),
const Range(0x1ee34, 0x1ee37),
const Range(0x1ee39, 0x1ee39),
const Range(0x1ee3b, 0x1ee3b),
const Range(0x1ee42, 0x1ee42),
const Range(0x1ee47, 0x1ee47),
const Range(0x1ee49, 0x1ee49),
const Range(0x1ee4b, 0x1ee4b),
const Range(0x1ee4d, 0x1ee4f),
const Range(0x1ee51, 0x1ee52),
const Range(0x1ee54, 0x1ee54),
const Range(0x1ee57, 0x1ee57),
const Range(0x1ee59, 0x1ee59),
const Range(0x1ee5b, 0x1ee5b),
const Range(0x1ee5d, 0x1ee5d),
const Range(0x1ee5f, 0x1ee5f),
const Range(0x1ee61, 0x1ee62),
const Range(0x1ee64, 0x1ee64),
const Range(0x1ee67, 0x1ee6a),
const Range(0x1ee6c, 0x1ee72),
const Range(0x1ee74, 0x1ee77),
const Range(0x1ee79, 0x1ee7c),
const Range(0x1ee7e, 0x1ee7e),
const Range(0x1ee80, 0x1ee89),
const Range(0x1ee8b, 0x1ee9b),
const Range(0x1eea1, 0x1eea3),
const Range(0x1eea5, 0x1eea9),
const Range(0x1eeab, 0x1eebb),
const Range(0x1eef0, 0x1eef1),
const Range(0x1f000, 0x1f02b),
const Range(0x1f030, 0x1f093),
const Range(0x1f0a0, 0x1f0ae),
const Range(0x1f0b1, 0x1f0bf),
const Range(0x1f0c1, 0x1f0cf),
const Range(0x1f0d1, 0x1f0f5),
const Range(0x1f100, 0x1f10c),
const Range(0x1f110, 0x1f12e),
const Range(0x1f130, 0x1f16b),
const Range(0x1f170, 0x1f1ac),
const Range(0x1f1e6, 0x1f202),
const Range(0x1f210, 0x1f23b),
const Range(0x1f240, 0x1f248),
const Range(0x1f250, 0x1f251),
const Range(0x1f260, 0x1f265),
const Range(0x1f300, 0x1f6d4),
const Range(0x1f6e0, 0x1f6ec),
const Range(0x1f6f0, 0x1f6f8),
const Range(0x1f700, 0x1f773),
const Range(0x1f780, 0x1f7d4),
const Range(0x1f800, 0x1f80b),
const Range(0x1f810, 0x1f847),
const Range(0x1f850, 0x1f859),
const Range(0x1f860, 0x1f887),
const Range(0x1f890, 0x1f8ad),
const Range(0x1f900, 0x1f90b),
const Range(0x1f910, 0x1f93e),
const Range(0x1f940, 0x1f94c),
const Range(0x1f950, 0x1f96b),
const Range(0x1f980, 0x1f997),
const Range(0x1f9c0, 0x1f9c0),
const Range(0x1f9d0, 0x1f9e6),
const Range(0x20000, 0x2a6d6),
const Range(0x2a700, 0x2b734),
const Range(0x2b740, 0x2b81d),
const Range(0x2b820, 0x2cea1),
const Range(0x2ceb0, 0x2ebe0),
const Range(0x2f800, 0x2fa1d),
];
final Range range = pickFromList(random, characterRanges);
if (range.start == range.end)
return range.start;
return range.start + random.nextInt(range.end - range.start);
}
\ No newline at end of file
...@@ -187,6 +187,20 @@ class DecorationImagePainter { ...@@ -187,6 +187,20 @@ class DecorationImagePainter {
ImageStream _imageStream; ImageStream _imageStream;
ImageInfo _image; ImageInfo _image;
/// Draw the image onto the given canvas.
///
/// The image is drawn at the position and size given by the `rect` argument.
///
/// The image is clipped to the given `clipPath`, if any.
///
/// The `configuration` object is used to resolve the image (e.g. to pick
/// resolution-specific assets), and to implement the
/// [DecorationImage.matchTextDirection] feature.
///
/// If the image needs to be painted again, e.g. because it is animated or
/// because it had not yet been loaded the first time this method was called,
/// then the `onChanged` callback passed to [DecorationImage.createPainter]
/// will be called.
void paint(Canvas canvas, Rect rect, Path clipPath, ImageConfiguration configuration) { void paint(Canvas canvas, Rect rect, Path clipPath, ImageConfiguration configuration) {
if (_details == null) if (_details == null)
return; return;
......
...@@ -43,6 +43,10 @@ class RenderListBody extends RenderBox ...@@ -43,6 +43,10 @@ class RenderListBody extends RenderBox
child.parentData = new ListBodyParentData(); child.parentData = new ListBodyParentData();
} }
/// The direction in which the children are laid out.
///
/// For example, if the [axisDirection] is [AxisDirection.down], each child
/// will be laid out below the next, vertically.
AxisDirection get axisDirection => _axisDirection; AxisDirection get axisDirection => _axisDirection;
AxisDirection _axisDirection; AxisDirection _axisDirection;
set axisDirection(AxisDirection value) { set axisDirection(AxisDirection value) {
...@@ -53,6 +57,8 @@ class RenderListBody extends RenderBox ...@@ -53,6 +57,8 @@ class RenderListBody extends RenderBox
markNeedsLayout(); markNeedsLayout();
} }
/// The axis (horizontal or vertical) corresponding to the current
/// [axisDirection].
Axis get mainAxis => axisDirectionToAxis(axisDirection); Axis get mainAxis => axisDirectionToAxis(axisDirection);
@override @override
......
...@@ -2114,6 +2114,17 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2114,6 +2114,17 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
owner.requestVisualUpdate(); owner.requestVisualUpdate();
} }
/// Report the semantics of this node, for example for accessibility purposes.
///
/// This method should be overridden by subclasses that have interesting
/// semantic information.
///
/// The given [SemanticsConfiguration] object is mutable and should be
/// annotated in a manner that describes the current state. No reference
/// should be kept to that object; mutating it outside of the context of the
/// [describeSemanticsConfiguration] call (for example as a result of
/// asynchronous computation) will at best have no useful effect and at worse
/// will cause crashes as the data will be in an inconsistent state.
@protected @protected
void describeSemanticsConfiguration(SemanticsConfiguration config) { void describeSemanticsConfiguration(SemanticsConfiguration config) {
// Nothing to do by default. // Nothing to do by default.
......
...@@ -403,6 +403,20 @@ class RenderParagraph extends RenderBox { ...@@ -403,6 +403,20 @@ class RenderParagraph extends RenderBox {
return _textPainter.getWordBoundary(position); return _textPainter.getWordBoundary(position);
} }
/// Returns the size of the text as laid out.
///
/// This can differ from [size] if the text overflowed or if the [constraints]
/// provided by the parent [RenderObject] forced the layout to be bigger than
/// necessary for the given [text].
///
/// This returns the [TextPainter.size] of the underlying [TextPainter].
///
/// Valid only after [layout].
Size get textSize {
assert(!debugNeedsLayout);
return _textPainter.size;
}
@override @override
void describeSemanticsConfiguration(SemanticsConfiguration config) { void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config); super.describeSemanticsConfiguration(config);
......
...@@ -596,6 +596,15 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -596,6 +596,15 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
static final SemanticsConfiguration _kEmptyConfig = new SemanticsConfiguration(); static final SemanticsConfiguration _kEmptyConfig = new SemanticsConfiguration();
/// Reconfigures the properties of this object to describe the configuration
/// provided in the `config` argument and the children listen in the
/// `childrenInInversePaintOrder` argument.
///
/// The arguments may be null; this represents an empty configuration (all
/// values at their defaults, no children).
///
/// No reference is kept to the [SemanticsConfiguration] object, but the child
/// list is used as-is and should therefore not be changed after this call.
void updateWith({ void updateWith({
@required SemanticsConfiguration config, @required SemanticsConfiguration config,
@required List<SemanticsNode> childrenInInversePaintOrder, @required List<SemanticsNode> childrenInInversePaintOrder,
......
...@@ -332,14 +332,20 @@ class RenderStack extends RenderBox ...@@ -332,14 +332,20 @@ class RenderStack extends RenderBox
markNeedsLayout(); markNeedsLayout();
} }
/// How to align the non-positioned children in the stack. /// How to align the non-positioned or partially-positioned children in the
/// stack.
/// ///
/// The non-positioned children are placed relative to each other such that /// The non-positioned children are placed relative to each other such that
/// the points determined by [alignment] are co-located. For example, if the /// the points determined by [alignment] are co-located. For example, if the
/// [alignment] is [Alignment.topLeft], then the top left corner of /// [alignment] is [Alignment.topLeft], then the top left corner of
/// each non-positioned child will be located at the same global coordinate. /// each non-positioned child will be located at the same global coordinate.
/// ///
/// If this is set to a [AlignmentDirectional] object, then [textDirection] /// Partially-positioned children, those that do not specify an alignment in a
/// particular axis (e.g. that have neither `top` nor `bottom` set), use the
/// alignment to determine how they should be positioned in that
/// under-specified axis.
///
/// If this is set to an [AlignmentDirectional] object, then [textDirection]
/// must not be null. /// must not be null.
AlignmentGeometry get alignment => _alignment; AlignmentGeometry get alignment => _alignment;
AlignmentGeometry _alignment; AlignmentGeometry _alignment;
...@@ -504,20 +510,26 @@ class RenderStack extends RenderBox ...@@ -504,20 +510,26 @@ class RenderStack extends RenderBox
child.layout(childConstraints, parentUsesSize: true); child.layout(childConstraints, parentUsesSize: true);
double x = 0.0; double x;
if (childParentData.left != null) if (childParentData.left != null) {
x = childParentData.left; x = childParentData.left;
else if (childParentData.right != null) } else if (childParentData.right != null) {
x = size.width - childParentData.right - child.size.width; x = size.width - childParentData.right - child.size.width;
} else {
x = _resolvedAlignment.alongOffset(size - child.size).dx;
}
if (x < 0.0 || x + child.size.width > size.width) if (x < 0.0 || x + child.size.width > size.width)
_hasVisualOverflow = true; _hasVisualOverflow = true;
double y = 0.0; double y;
if (childParentData.top != null) if (childParentData.top != null) {
y = childParentData.top; y = childParentData.top;
else if (childParentData.bottom != null) } else if (childParentData.bottom != null) {
y = size.height - childParentData.bottom - child.size.height; y = size.height - childParentData.bottom - child.size.height;
} else {
y = _resolvedAlignment.alongOffset(size - child.size).dy;
}
if (y < 0.0 || y + child.size.height > size.height) if (y < 0.0 || y + child.size.height > size.height)
_hasVisualOverflow = true; _hasVisualOverflow = true;
......
...@@ -2239,9 +2239,10 @@ class ListBody extends MultiChildRenderObjectWidget { ...@@ -2239,9 +2239,10 @@ class ListBody extends MultiChildRenderObjectWidget {
/// Positioned children are those wrapped in a [Positioned] widget that has at /// Positioned children are those wrapped in a [Positioned] widget that has at
/// least one non-null property. The stack sizes itself to contain all the /// least one non-null property. The stack sizes itself to contain all the
/// non-positioned children, which are positioned according to [alignment] /// non-positioned children, which are positioned according to [alignment]
/// (which defaults to the top-left corner). The positioned children are then /// (which defaults to the top-left corner in left-to-right environments and the
/// placed relative to the stack according to their top, right, bottom, and left /// top-right corner in right-to-left environments). The positioned children are
/// properties. /// then placed relative to the stack according to their top, right, bottom, and
/// left properties.
/// ///
/// The stack paints its children in order with the first child being at the /// The stack paints its children in order with the first child being at the
/// bottom. If you want to change the order in which the children paint, you /// bottom. If you want to change the order in which the children paint, you
...@@ -2282,12 +2283,18 @@ class Stack extends MultiChildRenderObjectWidget { ...@@ -2282,12 +2283,18 @@ class Stack extends MultiChildRenderObjectWidget {
List<Widget> children: const <Widget>[], List<Widget> children: const <Widget>[],
}) : super(key: key, children: children); }) : super(key: key, children: children);
/// How to align the non-positioned children in the stack. /// How to align the non-positioned and partially-positioned children in the
/// stack.
/// ///
/// The non-positioned children are placed relative to each other such that /// The non-positioned children are placed relative to each other such that
/// the points determined by [alignment] are co-located. For example, if the /// the points determined by [alignment] are co-located. For example, if the
/// [alignment] is [Alignment.topLeft], then the top left corner of /// [alignment] is [Alignment.topLeft], then the top left corner of
/// each non-positioned child will be located at the same global coordinate. /// each non-positioned child will be located at the same global coordinate.
///
/// Partially-positioned children, those that do not specify an alignment in a
/// particular axis (e.g. that have neither `top` nor `bottom` set), use the
/// alignment to determine how they should be positioned in that
/// under-specified axis.
final AlignmentGeometry alignment; final AlignmentGeometry alignment;
/// The text direction with which to resolve [alignment]. /// The text direction with which to resolve [alignment].
...@@ -2398,6 +2405,12 @@ class IndexedStack extends Stack { ...@@ -2398,6 +2405,12 @@ class IndexedStack extends Stack {
/// [height] properties can be used to give the dimensions, with one /// [height] properties can be used to give the dimensions, with one
/// corresponding position property (e.g. [top] and [height]). /// corresponding position property (e.g. [top] and [height]).
/// ///
/// If all three values on a particular axis are null, then the
/// [Stack.alignment] property is used to position the child.
///
/// If all six values are null, the child is a non-positioned child. The [Stack]
/// uses only the non-positioned children to size itself.
///
/// See also: /// See also:
/// ///
/// * [PositionedDirectional], which adapts to the ambient [Directionality]. /// * [PositionedDirectional], which adapts to the ambient [Directionality].
...@@ -2413,6 +2426,8 @@ class Positioned extends ParentDataWidget<Stack> { ...@@ -2413,6 +2426,8 @@ class Positioned extends ParentDataWidget<Stack> {
/// ///
/// * [Positioned.directional], which specifies the widget's horizontal /// * [Positioned.directional], which specifies the widget's horizontal
/// position using `start` and `end` rather than `left` and `right`. /// position using `start` and `end` rather than `left` and `right`.
/// * [PositionedDirectional], which is similar to [Positioned.directional]
/// but adapts to the ambient [Directionality].
const Positioned({ const Positioned({
Key key, Key key,
this.left, this.left,
...@@ -2530,36 +2545,54 @@ class Positioned extends ParentDataWidget<Stack> { ...@@ -2530,36 +2545,54 @@ class Positioned extends ParentDataWidget<Stack> {
/// ///
/// Only two out of the three horizontal values ([left], [right], [width]) can be /// Only two out of the three horizontal values ([left], [right], [width]) can be
/// set. The third must be null. /// set. The third must be null.
///
/// If all three are null, the [Stack.alignment] is used to position the child
/// horizontally.
final double left; final double left;
/// The distance that the child's top edge is inset from the top of the stack. /// The distance that the child's top edge is inset from the top of the stack.
/// ///
/// Only two out of the three vertical values ([top], [bottom], [height]) can be /// Only two out of the three vertical values ([top], [bottom], [height]) can be
/// set. The third must be null. /// set. The third must be null.
///
/// If all three are null, the [Stack.alignment] is used to position the child
/// vertically.
final double top; final double top;
/// The distance that the child's right edge is inset from the right of the stack. /// The distance that the child's right edge is inset from the right of the stack.
/// ///
/// Only two out of the three horizontal values ([left], [right], [width]) can be /// Only two out of the three horizontal values ([left], [right], [width]) can be
/// set. The third must be null. /// set. The third must be null.
///
/// If all three are null, the [Stack.alignment] is used to position the child
/// horizontally.
final double right; final double right;
/// The distance that the child's bottom edge is inset from the bottom of the stack. /// The distance that the child's bottom edge is inset from the bottom of the stack.
/// ///
/// Only two out of the three vertical values ([top], [bottom], [height]) can be /// Only two out of the three vertical values ([top], [bottom], [height]) can be
/// set. The third must be null. /// set. The third must be null.
///
/// If all three are null, the [Stack.alignment] is used to position the child
/// vertically.
final double bottom; final double bottom;
/// The child's width. /// The child's width.
/// ///
/// Only two out of the three horizontal values ([left], [right], [width]) can be /// Only two out of the three horizontal values ([left], [right], [width]) can be
/// set. The third must be null. /// set. The third must be null.
///
/// If all three are null, the [Stack.alignment] is used to position the child
/// horizontally.
final double width; final double width;
/// The child's height. /// The child's height.
/// ///
/// Only two out of the three vertical values ([top], [bottom], [height]) can be /// Only two out of the three vertical values ([top], [bottom], [height]) can be
/// set. The third must be null. /// set. The third must be null.
///
/// If all three are null, the [Stack.alignment] is used to position the child
/// vertically.
final double height; final double height;
@override @override
...@@ -3897,6 +3930,18 @@ class RichText extends LeafRenderObjectWidget { ...@@ -3897,6 +3930,18 @@ class RichText extends LeafRenderObjectWidget {
..textScaleFactor = textScaleFactor ..textScaleFactor = textScaleFactor
..maxLines = maxLines; ..maxLines = maxLines;
} }
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: TextAlign.start));
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
description.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
description.add(new EnumProperty<TextOverflow>('overflow', overflow, defaultValue: TextOverflow.clip));
description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: 1.0));
description.add(new IntProperty('maxLines', maxLines, ifNull: 'unlimited'));
description.add(new StringProperty('text', text.toPlainText()));
}
} }
/// A widget that displays a [dart:ui.Image] directly. /// A widget that displays a [dart:ui.Image] directly.
......
...@@ -4519,6 +4519,11 @@ class LeafRenderObjectElement extends RenderObjectElement { ...@@ -4519,6 +4519,11 @@ class LeafRenderObjectElement extends RenderObjectElement {
void removeChildRenderObject(RenderObject child) { void removeChildRenderObject(RenderObject child) {
assert(false); assert(false);
} }
@override
List<DiagnosticsNode> debugDescribeChildren() {
return widget.debugDescribeChildren();
}
} }
/// An [Element] that uses a [SingleChildRenderObjectWidget] as its configuration. /// An [Element] that uses a [SingleChildRenderObjectWidget] as its configuration.
......
...@@ -29,7 +29,14 @@ class IconData { ...@@ -29,7 +29,14 @@ class IconData {
/// The font family from which the glyph for the [codePoint] will be selected. /// The font family from which the glyph for the [codePoint] will be selected.
final String fontFamily; final String fontFamily;
// The name of the package from which the font family is included. /// The name of the package from which the font family is included.
///
/// The name is used by the [Icon] widget when configuring the [TextStyle] so
/// that the given [fontFamily] is obtained from the appropriate asset.
///
/// See also:
///
/// * [TextStyle], which describes how to use fonts from other packages.
final String fontPackage; final String fontPackage;
@override @override
......
...@@ -292,5 +292,11 @@ class Text extends StatelessWidget { ...@@ -292,5 +292,11 @@ class Text extends StatelessWidget {
super.debugFillProperties(description); super.debugFillProperties(description);
description.add(new StringProperty('data', data, showName: false)); description.add(new StringProperty('data', data, showName: false));
style?.debugFillProperties(description); style?.debugFillProperties(description);
description.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
description.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
description.add(new EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
description.add(new IntProperty('maxLines', maxLines, defaultValue: null));
} }
} }
...@@ -46,6 +46,9 @@ void main() { ...@@ -46,6 +46,9 @@ void main() {
expect(painter.text.text.length, 28); expect(painter.text.text.length, 28);
painter.layout(); painter.layout();
// The skips here are because the old rendering code considers the bidi formatting characters
// to be part of the word sometimes and not others, which is fine, but we'd mildly prefer if
// we were consistently considering them part of words always.
final TextRange hebrew1 = painter.getWordBoundary(const TextPosition(offset: 4, affinity: TextAffinity.downstream)); final TextRange hebrew1 = painter.getWordBoundary(const TextPosition(offset: 4, affinity: TextAffinity.downstream));
expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs); expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs);
final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14, affinity: TextAffinity.downstream)); final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14, affinity: TextAffinity.downstream));
......
...@@ -629,4 +629,122 @@ void main() { ...@@ -629,4 +629,122 @@ void main() {
), ),
); );
}); });
testWidgets('Alignment with partially-positioned children', (WidgetTester tester) async {
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.rtl,
child: new Stack(
alignment: Alignment.center,
children: <Widget>[
const SizedBox(width: 100.0, height: 100.0),
const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
],
),
),
);
expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(350.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(350.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(0.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(350.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Stack(
alignment: Alignment.center,
children: <Widget>[
const SizedBox(width: 100.0, height: 100.0),
const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
],
),
),
);
expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(350.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(350.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(350.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
const SizedBox(width: 100.0, height: 100.0),
const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
],
),
),
);
expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(700.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(700.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Stack(
alignment: Alignment.topLeft,
children: <Widget>[
const SizedBox(width: 100.0, height: 100.0),
const Positioned(left: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(right: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const Positioned(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(start: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(end: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(top: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
const PositionedDirectional(bottom: 0.0, child: const SizedBox(width: 100.0, height: 100.0)),
],
),
),
);
expect(tester.getRect(find.byType(SizedBox).at(0)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(1)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(2)), new Rect.fromLTWH(700.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(3)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(4)), new Rect.fromLTWH(0.0, 500.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(5)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(6)), new Rect.fromLTWH(700.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(7)), new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0));
expect(tester.getRect(find.byType(SizedBox).at(8)), new Rect.fromLTWH(0.0, 500.0, 100.0, 100.0));
});
} }
...@@ -765,23 +765,23 @@ void main() { ...@@ -765,23 +765,23 @@ void main() {
equalsIgnoringHashCodes( equalsIgnoringHashCodes(
'Table-[GlobalKey#00000](renderObject: RenderTable#00000)\n' 'Table-[GlobalKey#00000](renderObject: RenderTable#00000)\n'
'├Text("A")\n' '├Text("A")\n'
'│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "A", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("B")\n' '├Text("B")\n'
'│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "B", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("C")\n' '├Text("C")\n'
'│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "C", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("D")\n' '├Text("D")\n'
'│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "D", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("EEE")\n' '├Text("EEE")\n'
'│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "EEE", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("F")\n' '├Text("F")\n'
'│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "F", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("G")\n' '├Text("G")\n'
'│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "G", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'├Text("H")\n' '├Text("H")\n'
'│└RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "H", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
'└Text("III")\n' '└Text("III")\n'
' └RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n', ' └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "III", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
), ),
); );
}); });
......
...@@ -52,7 +52,7 @@ void main() { ...@@ -52,7 +52,7 @@ void main() {
final String message = failure.message; final String message = failure.message;
expect(message, contains('Expected: no matching nodes in the widget tree\n')); expect(message, contains('Expected: no matching nodes in the widget tree\n'));
expect(message, contains('Actual: ?:<exactly one widget with text "foo": Text("foo")>\n')); expect(message, contains('Actual: ?:<exactly one widget with text "foo": Text("foo", textDirection: ltr)>\n'));
expect(message, contains('Which: means one was found but none were expected\n')); expect(message, contains('Which: means one was found but none were expected\n'));
}); });
...@@ -70,7 +70,7 @@ void main() { ...@@ -70,7 +70,7 @@ void main() {
final String message = failure.message; final String message = failure.message;
expect(message, contains('Expected: no matching nodes in the widget tree\n')); expect(message, contains('Expected: no matching nodes in the widget tree\n'));
expect(message, contains('Actual: ?:<exactly one widget with text "foo" (ignoring offstage widgets): Text("foo")>\n')); expect(message, contains('Actual: ?:<exactly one widget with text "foo" (ignoring offstage widgets): Text("foo", textDirection: ltr)>\n'));
expect(message, contains('Which: means one was found but none were expected\n')); expect(message, contains('Which: means one was found but none were expected\n'));
}); });
......
...@@ -738,6 +738,10 @@ class VM extends ServiceObjectOwner { ...@@ -738,6 +738,10 @@ class VM extends ServiceObjectOwner {
} on WebSocketChannelException catch (error) { } on WebSocketChannelException catch (error) {
throwToolExit('Error connecting to observatory: $error'); throwToolExit('Error connecting to observatory: $error');
return null; return null;
} on rpc.RpcException catch (error) {
printError('Error ${error.code} received from application: ${error.message}');
printTrace('${error.data}');
return null;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment