From 1f40d96fbfaea4ca90af3cd7f170293fbca1011c Mon Sep 17 00:00:00 2001 From: Hixie Date: Thu, 15 Oct 2015 11:07:46 -0700 Subject: [PATCH] Improve debugging output Teach dumpRenderTree() to draw actual trees. Make the TextStyle output terser so it doesn't overflow the output. Make debugDumpApp() say what mode we're in (checked vs release). Hide lifecycle state from release mode dumps (since it's checked-only state). --- examples/stocks/lib/main.dart | 1 + examples/stocks/lib/stock_home.dart | 2 +- .../flutter/lib/src/painting/text_style.dart | 140 +++++++++++++++--- .../flutter/lib/src/rendering/object.dart | 35 +++-- .../flutter/lib/src/rendering/proxy_box.dart | 3 +- packages/flutter/lib/src/widgets/binding.dart | 3 + .../flutter/lib/src/widgets/framework.dart | 11 +- 7 files changed, 160 insertions(+), 35 deletions(-) diff --git a/examples/stocks/lib/main.dart b/examples/stocks/lib/main.dart index 314a3b57a5..61e7d5d4d9 100644 --- a/examples/stocks/lib/main.dart +++ b/examples/stocks/lib/main.dart @@ -11,6 +11,7 @@ import 'dart:ui' as ui; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; +import 'package:flutter/rendering.dart'; import 'stock_data.dart'; diff --git a/examples/stocks/lib/stock_home.dart b/examples/stocks/lib/stock_home.dart index 0d18aec008..33d9cdd5de 100644 --- a/examples/stocks/lib/stock_home.dart +++ b/examples/stocks/lib/stock_home.dart @@ -112,7 +112,7 @@ class StockHomeState extends State { ), new DrawerItem( icon: 'device/dvr', - onPressed: () { debugDumpApp(); }, + onPressed: () { debugDumpApp(); debugDumpRenderTree(); }, child: new Text('Dump App to Console') ), new DrawerDivider(), diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index 9ca3de29a9..cf241df700 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -252,25 +252,131 @@ class TextStyle { List result = []; if (color != null) result.add('${prefix}color: $color'); - // TODO(hansmuller): escape the fontFamily string. if (fontFamily != null) - result.add('${prefix}fontFamily: "$fontFamily"'); + result.add('${prefix}family: "$fontFamily"'); if (fontSize != null) - result.add('${prefix}fontSize: $fontSize'); - if (fontWeight != null) - result.add('${prefix}fontWeight: $fontWeight'); - if (fontStyle != null) - result.add('${prefix}fontStyle: $fontStyle'); - if (textAlign != null) - result.add('${prefix}textAlign: $textAlign'); - if (textBaseline != null) - result.add('${prefix}textBaseline: $textBaseline'); - if (decoration != null) - result.add('${prefix}decoration: $decoration'); - if (decorationColor != null) - result.add('${prefix}decorationColor: $decorationColor'); - if (decorationStyle != null) - result.add('${prefix}decorationStyle: $decorationStyle'); + result.add('${prefix}size: $fontSize'); + if (fontWeight != null) { + switch (fontWeight) { + case FontWeight.w100: + result.add('${prefix}weight: 100'); + break; + case FontWeight.w200: + result.add('${prefix}weight: 200'); + break; + case FontWeight.w300: + result.add('${prefix}weight: 300'); + break; + case FontWeight.w400: + result.add('${prefix}weight: 400'); + break; + case FontWeight.w500: + result.add('${prefix}weight: 500'); + break; + case FontWeight.w600: + result.add('${prefix}weight: 600'); + break; + case FontWeight.w700: + result.add('${prefix}weight: 700'); + break; + case FontWeight.w800: + result.add('${prefix}weight: 800'); + break; + case FontWeight.w900: + result.add('${prefix}weight: 900'); + break; + } + } + if (fontStyle != null) { + switch (fontStyle) { + case FontStyle.normal: + result.add('${prefix}style: normal'); + break; + case FontStyle.italic: + result.add('${prefix}style: italic'); + break; + } + } + if (textAlign != null) { + switch (textAlign) { + case TextAlign.left: + result.add('${prefix}align: left'); + break; + case TextAlign.right: + result.add('${prefix}align: right'); + break; + case TextAlign.center: + result.add('${prefix}align: center'); + break; + } + } + if (textBaseline != null) { + switch (textBaseline) { + case TextBaseline.alphabetic: + result.add('${prefix}baseline: alphabetic'); + break; + case TextBaseline.ideographic: + result.add('${prefix}baseline: ideographic'); + break; + } + } + if (decoration != null || decorationColor != null || decorationStyle != null) { + String decorationDescription = '${prefix}decoration: '; + bool haveDecorationDescription = false; + if (decorationStyle != null) { + switch (decorationStyle) { + case TextDecorationStyle.solid: + decorationDescription += 'solid'; + break; + case TextDecorationStyle.double: + decorationDescription += 'double'; + break; + case TextDecorationStyle.dotted: + decorationDescription += 'dotted'; + break; + case TextDecorationStyle.dashed: + decorationDescription += 'dashed'; + break; + case TextDecorationStyle.wavy: + decorationDescription += 'wavy'; + break; + } + haveDecorationDescription = true; + } + if (decorationColor != null) { + if (haveDecorationDescription) + decorationDescription += ' '; + decorationDescription += '$decorationColor'; + haveDecorationDescription = true; + } + if (decoration != null) { + if (haveDecorationDescription) + decorationDescription += ' '; + bool multipleDecorations = false; + for (TextDecoration value in decoration) { + if (multipleDecorations) + decorationDescription += '+'; + switch (value) { + case TextDecoration.none: + decorationDescription += 'none'; + break; + case TextDecoration.underline: + decorationDescription += 'underline'; + break; + case TextDecoration.overline: + decorationDescription += 'overline'; + break; + case TextDecoration.lineThrough: + decorationDescription += 'line-through'; + break; + } + multipleDecorations = true; + } + haveDecorationDescription = true; + } + assert(haveDecorationDescription); + result.add(decorationDescription); + } if (result.isEmpty) return '$prefix'; return result.join('\n'); diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index 606d4fb988..be74fec813 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -1139,11 +1139,18 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { /// Returns a description of the tree rooted at this node. /// If the prefix argument is provided, then every line in the output /// will be prefixed by that string. - String toStringDeep([String prefix = '']) { + String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) { RenderObject debugPreviousActiveLayout = _debugActiveLayout; _debugActiveLayout = null; - prefix += ' '; - String result = '$this\n${debugDescribeSettings(prefix)}${debugDescribeChildren(prefix)}'; + String result = '$prefixLineOne$this\n'; + final String childrenDescription = debugDescribeChildren(prefixOtherLines); + final String settingsPrefix = childrenDescription != '' ? '$prefixOtherLines \u2502 ' : '$prefixOtherLines '; + result += debugDescribeSettings(settingsPrefix); + if (childrenDescription != '') + result += '$prefixOtherLines \u2502\n'; + else + result += '$prefixOtherLines\n'; + result += childrenDescription; _debugActiveLayout = debugPreviousActiveLayout; return result; } @@ -1198,7 +1205,7 @@ abstract class RenderObjectWithChildMixin implem } String debugDescribeChildren(String prefix) { if (child != null) - return '${prefix}child: ${child.toStringDeep(prefix)}'; + return '${child.toStringDeep("$prefix \u2514\u2500child: ", "$prefix ")}'; return ''; } } @@ -1442,13 +1449,19 @@ abstract class ContainerRenderObjectMixin '$runtimeType($x, $y)'; } /// Applies a transformation before painting its child @@ -1016,7 +1017,7 @@ class RenderTransform extends RenderProxyBox { String debugDescribeSettings(String prefix) { List result = _transform.toString().split('\n').map((String s) => '$prefix $s\n').toList(); result.removeLast(); - return '${super.debugDescribeSettings(prefix)}${prefix}transform matrix:\n${result.join()}\n${prefix}origin: $origin\n'; + return '${super.debugDescribeSettings(prefix)}${prefix}transform matrix:\n${result.join()}\n${prefix}origin: $origin\nalignment: $alignment\n'; } } diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart index c73678c52e..8ceeb73244 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -92,6 +92,9 @@ void runApp(Widget app) { void debugDumpApp() { assert(WidgetFlutterBinding.instance != null); assert(WidgetFlutterBinding.instance.renderViewElement != null); + String mode = 'RELEASE MODE'; + assert(() { mode = 'CHECKED MODE'; return true; }); + print('${WidgetFlutterBinding.instance.runtimeType} - $mode'); WidgetFlutterBinding.instance.renderViewElement.toStringDeep().split('\n').forEach(print); } diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index 5ff36a3ab8..1cfd022b49 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -394,8 +394,11 @@ abstract class State { void debugFillDescription(List description) { description.add('$hashCode'); - if (_debugLifecycleState != _StateLifecycle.ready) - description.add('$_debugLifecycleState'); + assert(() { + if (_debugLifecycleState != _StateLifecycle.ready) + description.add('$_debugLifecycleState'); + return true; + }); if (_config == null) description.add('no config'); if (_element == null) @@ -829,9 +832,7 @@ abstract class Element implements BuildContext { String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) { String result = '$prefixLineOne$this\n'; List children = []; - visitChildren((Element child) { - children.add(child); - }); + visitChildren(children.add); if (children.length > 0) { Element last = children.removeLast(); for (Element child in children)