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 314a3b57a56..61e7d5d4d91 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 0d18aec008f..33d9cdd5dee 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 9ca3de29a93..cf241df7006 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 606d4fb988a..be74fec813f 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 c73678c52e9..8ceeb73244f 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 5ff36a3ab8b..1cfd022b497 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)