diff --git a/dev/manual_tests/lib/text.dart b/dev/manual_tests/lib/text.dart index 2b90e2f743..11b3a634e5 100644 --- a/dev/manual_tests/lib/text.dart +++ b/dev/manual_tests/lib/text.dart @@ -6,6 +6,7 @@ import 'dart:math' as math; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; int seed = 0; @@ -18,7 +19,8 @@ void main() { 'underlines': (BuildContext context) => const Underlines(), 'fallback': (BuildContext context) => const Fallback(), '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 { textColor: Colors.white, 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 with SingleTickerProviderStateMixin { } String _createRandomText() { - switch (_random.nextInt(80)) { + switch (_random.nextInt(90)) { case 0: case 1: case 2: @@ -448,6 +456,25 @@ class _FuzzerState extends State with SingleTickerProviderStateMixin { return '⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌'; case 73: 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; } @@ -641,7 +668,7 @@ class _FallbackState extends State { ), new Material( 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( crossAxisAlignment: CrossAxisAlignment.end, children: [ @@ -701,10 +728,16 @@ class _ZalgoState extends State with SingleTickerProviderStateMixin { } bool _allowSpacing = false; + bool _varyBase = false; void _update(Duration duration) { 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 with SingleTickerProviderStateMixin { onChanged: (bool value) { setState(() { _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 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 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: [ + new Expanded( + child: new Padding( + padding: new EdgeInsets.only(top: size.height * 0.1), + child: new Stack( + alignment: Alignment.center, + children: [ + 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: [ + 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: [ + 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: // http://unicode.org/Public/UNIDATA/UnicodeData.txt // There are three groups, character classes Mc, Me, and Mn. @@ -1088,7 +1304,7 @@ String zalgo(math.Random random, int target, { bool includeSpacingCombiningMarks int combiningCount = enclosingCombiningMarks.length + nonspacingCombiningMarks.length; if (includeSpacingCombiningMarks) combiningCount += spacingCombiningMarks.length; - for (int count = 0; count < target; count += 1) { + for (int count = 0; count < targetLength; count += 1) { int characterCode = random.nextInt(combiningCount); if (characterCode < enclosingCombiningMarks.length) { these.add(enclosingCombiningMarks[characterCode]); @@ -1102,11 +1318,717 @@ String zalgo(math.Random random, int target, { bool includeSpacingCombiningMarks } } } - final List characters = [0x41 + random.nextInt(26)]; + base ??= new String.fromCharCode(randomCharacter(random)); + final List characters = []; characters.addAll(these); - return new String.fromCharCodes(characters); + return base + new String.fromCharCodes(characters); } T pickFromList(math.Random random, List list) { 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 characterRanges = const [ + 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 diff --git a/packages/flutter/lib/src/painting/images.dart b/packages/flutter/lib/src/painting/images.dart index b63c1e9903..e02b5a3813 100644 --- a/packages/flutter/lib/src/painting/images.dart +++ b/packages/flutter/lib/src/painting/images.dart @@ -187,6 +187,20 @@ class DecorationImagePainter { ImageStream _imageStream; 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) { if (_details == null) return; diff --git a/packages/flutter/lib/src/rendering/list_body.dart b/packages/flutter/lib/src/rendering/list_body.dart index cfe3cb2fbe..76fc6dc566 100644 --- a/packages/flutter/lib/src/rendering/list_body.dart +++ b/packages/flutter/lib/src/rendering/list_body.dart @@ -43,6 +43,10 @@ class RenderListBody extends RenderBox 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 _axisDirection; set axisDirection(AxisDirection value) { @@ -53,6 +57,8 @@ class RenderListBody extends RenderBox markNeedsLayout(); } + /// The axis (horizontal or vertical) corresponding to the current + /// [axisDirection]. Axis get mainAxis => axisDirectionToAxis(axisDirection); @override diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index b0cb1a645e..3bf20e55fb 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -2114,6 +2114,17 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im 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 void describeSemanticsConfiguration(SemanticsConfiguration config) { // Nothing to do by default. diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart index a0d70977a2..0fd367587c 100644 --- a/packages/flutter/lib/src/rendering/paragraph.dart +++ b/packages/flutter/lib/src/rendering/paragraph.dart @@ -403,6 +403,20 @@ class RenderParagraph extends RenderBox { 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 void describeSemanticsConfiguration(SemanticsConfiguration config) { super.describeSemanticsConfiguration(config); diff --git a/packages/flutter/lib/src/rendering/semantics.dart b/packages/flutter/lib/src/rendering/semantics.dart index 2ede294e11..e8e2bc0dfc 100644 --- a/packages/flutter/lib/src/rendering/semantics.dart +++ b/packages/flutter/lib/src/rendering/semantics.dart @@ -596,9 +596,18 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { 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({ @required SemanticsConfiguration config, - @required List childrenInInversePaintOrder, + @required List childrenInInversePaintOrder, }) { config ??= _kEmptyConfig; if (_isDifferentFromCurrentSemanticAnnotation(config)) diff --git a/packages/flutter/lib/src/rendering/stack.dart b/packages/flutter/lib/src/rendering/stack.dart index 36b7029800..0d72ef0379 100644 --- a/packages/flutter/lib/src/rendering/stack.dart +++ b/packages/flutter/lib/src/rendering/stack.dart @@ -332,14 +332,20 @@ class RenderStack extends RenderBox 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 points determined by [alignment] are co-located. For example, if the /// [alignment] is [Alignment.topLeft], then the top left corner of /// 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. AlignmentGeometry get alignment => _alignment; AlignmentGeometry _alignment; @@ -504,20 +510,26 @@ class RenderStack extends RenderBox child.layout(childConstraints, parentUsesSize: true); - double x = 0.0; - if (childParentData.left != null) + double x; + if (childParentData.left != null) { x = childParentData.left; - else if (childParentData.right != null) + } else if (childParentData.right != null) { 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) _hasVisualOverflow = true; - double y = 0.0; - if (childParentData.top != null) + double y; + if (childParentData.top != null) { y = childParentData.top; - else if (childParentData.bottom != null) + } else if (childParentData.bottom != null) { 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) _hasVisualOverflow = true; diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index a000c6a39b..90114c877d 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -2239,9 +2239,10 @@ class ListBody extends MultiChildRenderObjectWidget { /// 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 /// non-positioned children, which are positioned according to [alignment] -/// (which defaults to the top-left corner). The positioned children are then -/// placed relative to the stack according to their top, right, bottom, and left -/// properties. +/// (which defaults to the top-left corner in left-to-right environments and the +/// top-right corner in right-to-left environments). The positioned children are +/// 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 /// bottom. If you want to change the order in which the children paint, you @@ -2282,12 +2283,18 @@ class Stack extends MultiChildRenderObjectWidget { List children: const [], }) : 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 points determined by [alignment] are co-located. For example, if the /// [alignment] is [Alignment.topLeft], then the top left corner of /// 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; /// The text direction with which to resolve [alignment]. @@ -2398,6 +2405,12 @@ class IndexedStack extends Stack { /// [height] properties can be used to give the dimensions, with one /// 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: /// /// * [PositionedDirectional], which adapts to the ambient [Directionality]. @@ -2413,6 +2426,8 @@ class Positioned extends ParentDataWidget { /// /// * [Positioned.directional], which specifies the widget's horizontal /// 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({ Key key, this.left, @@ -2530,36 +2545,54 @@ class Positioned extends ParentDataWidget { /// /// Only two out of the three horizontal values ([left], [right], [width]) can be /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// horizontally. final double left; /// 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 /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// vertically. final double top; /// 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 /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// horizontally. final double right; /// 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 /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// vertically. final double bottom; /// The child's width. /// /// Only two out of the three horizontal values ([left], [right], [width]) can be /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// horizontally. final double width; /// The child's height. /// /// Only two out of the three vertical values ([top], [bottom], [height]) can be /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// vertically. final double height; @override @@ -3897,6 +3930,18 @@ class RichText extends LeafRenderObjectWidget { ..textScaleFactor = textScaleFactor ..maxLines = maxLines; } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder description) { + super.debugFillProperties(description); + description.add(new EnumProperty('textAlign', textAlign, defaultValue: TextAlign.start)); + description.add(new EnumProperty('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('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. diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index c047394835..04be6d5c0a 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -4519,6 +4519,11 @@ class LeafRenderObjectElement extends RenderObjectElement { void removeChildRenderObject(RenderObject child) { assert(false); } + + @override + List debugDescribeChildren() { + return widget.debugDescribeChildren(); + } } /// An [Element] that uses a [SingleChildRenderObjectWidget] as its configuration. diff --git a/packages/flutter/lib/src/widgets/icon_data.dart b/packages/flutter/lib/src/widgets/icon_data.dart index 98c4a4d881..c34a754110 100644 --- a/packages/flutter/lib/src/widgets/icon_data.dart +++ b/packages/flutter/lib/src/widgets/icon_data.dart @@ -29,7 +29,14 @@ class IconData { /// The font family from which the glyph for the [codePoint] will be selected. 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; @override diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart index b8165f7671..a2ff2df8b0 100644 --- a/packages/flutter/lib/src/widgets/text.dart +++ b/packages/flutter/lib/src/widgets/text.dart @@ -292,5 +292,11 @@ class Text extends StatelessWidget { super.debugFillProperties(description); description.add(new StringProperty('data', data, showName: false)); style?.debugFillProperties(description); + description.add(new EnumProperty('textAlign', textAlign, defaultValue: null)); + description.add(new EnumProperty('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('overflow', overflow, defaultValue: null)); + description.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null)); + description.add(new IntProperty('maxLines', maxLines, defaultValue: null)); } } diff --git a/packages/flutter/test/painting/text_painter_rtl_test.dart b/packages/flutter/test/painting/text_painter_rtl_test.dart index e1edb61efb..065d7acb81 100644 --- a/packages/flutter/test/painting/text_painter_rtl_test.dart +++ b/packages/flutter/test/painting/text_painter_rtl_test.dart @@ -46,6 +46,9 @@ void main() { expect(painter.text.text.length, 28); 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)); expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs); final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14, affinity: TextAffinity.downstream)); diff --git a/packages/flutter/test/widgets/stack_test.dart b/packages/flutter/test/widgets/stack_test.dart index 62652e45b6..a3e4cdf81f 100644 --- a/packages/flutter/test/widgets/stack_test.dart +++ b/packages/flutter/test/widgets/stack_test.dart @@ -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: [ + 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: [ + 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: [ + 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: [ + 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)); + }); } diff --git a/packages/flutter/test/widgets/table_test.dart b/packages/flutter/test/widgets/table_test.dart index 4d8c8a9ed7..5ce9a7fa11 100644 --- a/packages/flutter/test/widgets/table_test.dart +++ b/packages/flutter/test/widgets/table_test.dart @@ -765,23 +765,23 @@ void main() { equalsIgnoringHashCodes( 'Table-[GlobalKey#00000](renderObject: RenderTable#00000)\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' - '│└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' - '│└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' - '│└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' - '│└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' - '│└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' - '│└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' - '│└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' - ' └RichText(renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n', + ' └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "III", renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n' ), ); }); diff --git a/packages/flutter_test/test/widget_tester_test.dart b/packages/flutter_test/test/widget_tester_test.dart index c9970d3a4f..af4903b6b0 100644 --- a/packages/flutter_test/test/widget_tester_test.dart +++ b/packages/flutter_test/test/widget_tester_test.dart @@ -52,7 +52,7 @@ void main() { final String message = failure.message; expect(message, contains('Expected: no matching nodes in the widget tree\n')); - expect(message, contains('Actual: ?:\n')); + expect(message, contains('Actual: ?:\n')); expect(message, contains('Which: means one was found but none were expected\n')); }); @@ -70,7 +70,7 @@ void main() { final String message = failure.message; expect(message, contains('Expected: no matching nodes in the widget tree\n')); - expect(message, contains('Actual: ?:\n')); + expect(message, contains('Actual: ?:\n')); expect(message, contains('Which: means one was found but none were expected\n')); }); diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart index 651c65a12e..7cfab0131c 100644 --- a/packages/flutter_tools/lib/src/vmservice.dart +++ b/packages/flutter_tools/lib/src/vmservice.dart @@ -738,6 +738,10 @@ class VM extends ServiceObjectOwner { } on WebSocketChannelException catch (error) { throwToolExit('Error connecting to observatory: $error'); return null; + } on rpc.RpcException catch (error) { + printError('Error ${error.code} received from application: ${error.message}'); + printTrace('${error.data}'); + return null; } }