|
|
|
|
|
|
using System; |
|
|
|
using System.Collections; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
using Unity.UIWidgets.async2; |
|
|
|
using Unity.UIWidgets.foundation; |
|
|
|
using Unity.UIWidgets.gestures; |
|
|
|
|
|
|
using Unity.UIWidgets.ui; |
|
|
|
using UnityEngine; |
|
|
|
using Canvas = Unity.UIWidgets.ui.Canvas; |
|
|
|
using Color = Unity.UIWidgets.ui.Color; |
|
|
|
using Rect = Unity.UIWidgets.ui.Rect; |
|
|
|
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|
|
|
public delegate Dictionary<string, object> AddAdditionalPropertiesCallback(DiagnosticsNode diagnosticsNode, InspectorSerializationDelegate inspectorSerializationDelegate); |
|
|
|
|
|
|
|
public class WidgetInspectorUtils{ |
|
|
|
public static float _kScreenEdgeMargin = 10.0f; |
|
|
|
public static float _kTooltipPadding = 5.0f; |
|
|
|
public static float _kInspectButtonMargin = 10.0f; |
|
|
|
public static float _kOffScreenMargin = 1.0f; |
|
|
|
class _SerializeConfig { |
|
|
|
public static TextStyle _messageStyle = new TextStyle( |
|
|
|
color: new Color(0xFFFFFFFF), |
|
|
|
fontSize: 10.0f, |
|
|
|
height: 1.2f |
|
|
|
); |
|
|
|
public static int _kMaxTooltipLines = 5; |
|
|
|
public static Color _kTooltipBackgroundColor = Color.fromARGB(230, 60, 60, 60); |
|
|
|
public static Color _kHighlightedRenderObjectFillColor = Color.fromARGB(128, 128, 128, 255); |
|
|
|
public static Color _kHighlightedRenderObjectBorderColor = Color.fromARGB(128, 64, 64, 128); |
|
|
|
|
|
|
|
/*bool _isDebugCreator(DiagnosticsNode node) => node is DiagnosticsDebugCreator; |
|
|
|
IEnumerable<DiagnosticsNode> transformDebugCreator(IEnumerable<DiagnosticsNode> properties) sync* { |
|
|
|
List<DiagnosticsNode> pending = new List<DiagnosticsNode>(); |
|
|
|
bool foundStackTrace = false; |
|
|
|
foreach ( DiagnosticsNode node in properties) { |
|
|
|
if (!foundStackTrace && node is DiagnosticsStackTrace) |
|
|
|
foundStackTrace = true; |
|
|
|
if (_isDebugCreator(node)) { |
|
|
|
yield* _parseDiagnosticsNode(node); |
|
|
|
} else { |
|
|
|
if (foundStackTrace) { |
|
|
|
pending.Add(node); |
|
|
|
} else { |
|
|
|
yield node; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
yield* pending; |
|
|
|
}*/ |
|
|
|
public static readonly Dictionary<_Location, int> _locationToId = new Dictionary<_Location, int>(); |
|
|
|
public static readonly List<_Location> _locations = new List<_Location>(); |
|
|
|
|
|
|
|
public static int _toLocationId(_Location location) { |
|
|
|
int id = _locationToId[location]; |
|
|
|
if (id != null) { |
|
|
|
return id; |
|
|
|
} |
|
|
|
id = _locations.Count; |
|
|
|
_locations.Add(location); |
|
|
|
_locationToId[location] = id; |
|
|
|
return id; |
|
|
|
} |
|
|
|
/*public IEnumerable<DiagnosticsNode> _parseDiagnosticsNode(DiagnosticsNode node) { |
|
|
|
if (!_isDebugCreator(node)) |
|
|
|
return null; |
|
|
|
DebugCreator debugCreator = node.value as DebugCreator; |
|
|
|
Element element = debugCreator.element; |
|
|
|
return _describeRelevantUserCode(element); |
|
|
|
}*/ |
|
|
|
public IEnumerable<DiagnosticsNode> _describeRelevantUserCode(Element element) { |
|
|
|
if (!WidgetInspectorService.instance.isWidgetCreationTracked()) { |
|
|
|
return new List<DiagnosticsNode>() { |
|
|
|
new ErrorDescription( |
|
|
|
"Widget creation tracking is currently disabled. Enabling " + |
|
|
|
"it enables improved error messages. It can be enabled by passing " + |
|
|
|
"`--track-widget-creation` to `flutter run` or `flutter test`." |
|
|
|
), |
|
|
|
new ErrorSpacer() |
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
List<DiagnosticsNode> nodes = new List<DiagnosticsNode>(); |
|
|
|
|
|
|
|
bool processElement(Element target) { |
|
|
|
if (_isLocalCreationLocation(target)) { |
|
|
|
nodes.Add( |
|
|
|
new DiagnosticsBlock( |
|
|
|
name: "The relevant error-causing widget was", |
|
|
|
children: new List<DiagnosticsNode>() { |
|
|
|
new ErrorDescription( |
|
|
|
"${target.widget.toStringShort()} ${_describeCreationLocation(target)}"), |
|
|
|
} |
|
|
|
)); |
|
|
|
nodes.Add(new ErrorSpacer()); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
if (processElement(element)) |
|
|
|
element.visitAncestorElements(processElement); |
|
|
|
return nodes; |
|
|
|
} |
|
|
|
|
|
|
|
public bool _isLocalCreationLocation(object _object){ |
|
|
|
_Location location = _getCreationLocation(_object); |
|
|
|
if (location == null) |
|
|
|
return false; |
|
|
|
return WidgetInspectorService.instance._isLocalCreationLocation(location); |
|
|
|
} |
|
|
|
public string _describeCreationLocation(object _object) { |
|
|
|
_Location location = _getCreationLocation(_object); |
|
|
|
return location?.ToString(); |
|
|
|
} |
|
|
|
public static _Location _getCreationLocation(object _object) { |
|
|
|
object candidate = _object is Element ? ((Element)_object).widget : _object; |
|
|
|
return candidate is _HasCreationLocation ? ((_HasCreationLocation)candidate)._location : null; |
|
|
|
} |
|
|
|
Rect _calculateSubtreeBoundsHelper(RenderObject _object, Matrix4 transform) { |
|
|
|
Rect bounds = MatrixUtils.transformRect(transform, _object.semanticBounds); |
|
|
|
|
|
|
|
_object.visitChildren((RenderObject child) =>{ |
|
|
|
Matrix4 childTransform = transform.clone(); |
|
|
|
_object.applyPaintTransform(child, childTransform); |
|
|
|
Rect childBounds = _calculateSubtreeBoundsHelper(child, childTransform); |
|
|
|
Rect paintClip = _object.describeApproximatePaintClip(child); |
|
|
|
if (paintClip != null) { |
|
|
|
Rect transformedPaintClip = MatrixUtils.transformRect( |
|
|
|
transform, |
|
|
|
paintClip |
|
|
|
); |
|
|
|
childBounds = childBounds.intersect(transformedPaintClip); |
|
|
|
} |
|
|
|
|
|
|
|
if (childBounds.isFinite && !childBounds.isEmpty) { |
|
|
|
bounds = bounds.isEmpty ? childBounds : bounds.expandToInclude(childBounds); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
return bounds; |
|
|
|
} |
|
|
|
|
|
|
|
/// Calculate bounds for a render object and all of its descendants.
|
|
|
|
Rect _calculateSubtreeBounds(RenderObject _object){ |
|
|
|
return _calculateSubtreeBoundsHelper(_object, Matrix4.identity()); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
public abstract class _HasCreationLocation { |
|
|
|
public _Location _location { get; } |
|
|
|
} |
|
|
|
|
|
|
|
public class _ProxyLayer : Layer { |
|
|
|
public _ProxyLayer(Layer _layer) { |
|
|
|
this._layer = _layer; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly Layer _layer; |
|
|
|
|
|
|
|
public override void addToScene(ui.SceneBuilder builder, Offset layerOffset = null ) { |
|
|
|
layerOffset = layerOffset ?? Offset.zero; |
|
|
|
_layer.addToScene(builder, layerOffset); |
|
|
|
} |
|
|
|
|
|
|
|
public override bool findAnnotations<S>( |
|
|
|
AnnotationResult<S> result, |
|
|
|
Offset localPosition, |
|
|
|
bool onlyFirst) { |
|
|
|
return _layer.findAnnotations(result, localPosition, onlyFirst: onlyFirst); |
|
|
|
} |
|
|
|
} |
|
|
|
public class _MulticastCanvas : Canvas { |
|
|
|
public _MulticastCanvas( |
|
|
|
Canvas main, |
|
|
|
Canvas screenshot |
|
|
|
) : base( new PictureRecorder()) { |
|
|
|
D.assert(main != null); |
|
|
|
D.assert(screenshot != null); |
|
|
|
_main = main; |
|
|
|
_screenshot = screenshot; |
|
|
|
} |
|
|
|
public readonly Canvas _main; |
|
|
|
public readonly Canvas _screenshot; |
|
|
|
public override void clipPath(Path path, bool doAntiAlias = true ) { |
|
|
|
_main.clipPath(path, doAntiAlias: doAntiAlias); |
|
|
|
_screenshot.clipPath(path, doAntiAlias: doAntiAlias); |
|
|
|
} |
|
|
|
public override void clipRRect(RRect rrect, bool doAntiAlias = true ) { |
|
|
|
_main.clipRRect(rrect, doAntiAlias: doAntiAlias); |
|
|
|
_screenshot.clipRRect(rrect, doAntiAlias: doAntiAlias); |
|
|
|
} |
|
|
|
|
|
|
|
public override void clipRect(Rect rect, ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true ) { |
|
|
|
_main.clipRect(rect, clipOp: clipOp, doAntiAlias: doAntiAlias); |
|
|
|
_screenshot.clipRect(rect, clipOp: clipOp, doAntiAlias: doAntiAlias); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawArc(Rect rect, float startAngle, float sweepAngle, bool useCenter, Paint paint) { |
|
|
|
_main.drawArc(rect, startAngle, sweepAngle, useCenter, paint); |
|
|
|
_screenshot.drawArc(rect, startAngle, sweepAngle, useCenter, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawAtlas(ui.Image atlas, List<RSTransform> transforms, List<Rect> rects, List<Color> colors, BlendMode blendMode, Rect cullRect, Paint paint) { |
|
|
|
_main.drawAtlas(atlas, transforms, rects, colors, blendMode, cullRect, paint); |
|
|
|
_screenshot.drawAtlas(atlas, transforms, rects, colors, blendMode, cullRect, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawCircle(Offset c, float radius, Paint paint) { |
|
|
|
_main.drawCircle(c, radius, paint); |
|
|
|
_screenshot.drawCircle(c, radius, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawColor(Color color, BlendMode blendMode) { |
|
|
|
_main.drawColor(color, blendMode); |
|
|
|
_screenshot.drawColor(color, blendMode); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawDRRect(RRect outer, RRect inner, Paint paint) { |
|
|
|
_main.drawDRRect(outer, inner, paint); |
|
|
|
_screenshot.drawDRRect(outer, inner, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawImage(ui.Image image, Offset p, Paint paint) { |
|
|
|
_main.drawImage(image, p, paint); |
|
|
|
_screenshot.drawImage(image, p, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawImageNine(ui.Image image, Rect center, Rect dst, Paint paint) { |
|
|
|
_main.drawImageNine(image, center, dst, paint); |
|
|
|
_screenshot.drawImageNine(image, center, dst, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawImageRect(ui.Image image, Rect src, Rect dst, Paint paint) { |
|
|
|
_main.drawImageRect(image, src, dst, paint); |
|
|
|
_screenshot.drawImageRect(image, src, dst, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawLine(Offset p1, Offset p2, Paint paint) { |
|
|
|
_main.drawLine(p1, p2, paint); |
|
|
|
_screenshot.drawLine(p1, p2, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawOval(Rect rect, Paint paint) { |
|
|
|
_main.drawOval(rect, paint); |
|
|
|
_screenshot.drawOval(rect, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawPaint(Paint paint) { |
|
|
|
_main.drawPaint(paint); |
|
|
|
_screenshot.drawPaint(paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawParagraph(ui.Paragraph paragraph, Offset offset) { |
|
|
|
_main.drawParagraph(paragraph, offset); |
|
|
|
_screenshot.drawParagraph(paragraph, offset); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawPath(Path path, Paint paint) { |
|
|
|
_main.drawPath(path, paint); |
|
|
|
_screenshot.drawPath(path, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawPicture(ui.Picture picture) { |
|
|
|
_main.drawPicture(picture); |
|
|
|
_screenshot.drawPicture(picture); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawPoints(ui.PointMode pointMode, List<Offset> points, Paint paint) { |
|
|
|
_main.drawPoints(pointMode, points, paint); |
|
|
|
_screenshot.drawPoints(pointMode, points, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawRRect(RRect rrect, Paint paint) { |
|
|
|
_main.drawRRect(rrect, paint); |
|
|
|
_screenshot.drawRRect(rrect, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawRawAtlas(ui.Image atlas, |
|
|
|
float[] rstTransforms, |
|
|
|
float[] rects, |
|
|
|
uint[] colors, |
|
|
|
BlendMode blendMode, |
|
|
|
Rect cullRect, |
|
|
|
Paint paint) { |
|
|
|
_main.drawRawAtlas(atlas, rstTransforms, rects, colors, blendMode, cullRect, paint); |
|
|
|
_screenshot.drawRawAtlas(atlas, rstTransforms, rects, colors, blendMode, cullRect, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawRawPoints(PointMode pointMode, float[] points, Paint paint) { |
|
|
|
_main.drawRawPoints(pointMode, points, paint); |
|
|
|
_screenshot.drawRawPoints(pointMode, points, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawRect(Rect rect, Paint paint) { |
|
|
|
_main.drawRect(rect, paint); |
|
|
|
_screenshot.drawRect(rect, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawShadow(Path path, Color color, float elevation, bool transparentOccluder) { |
|
|
|
_main.drawShadow(path, color, elevation, transparentOccluder); |
|
|
|
_screenshot.drawShadow(path, color, elevation, transparentOccluder); |
|
|
|
} |
|
|
|
|
|
|
|
public override void drawVertices(ui.Vertices vertices, BlendMode blendMode, Paint paint) { |
|
|
|
_main.drawVertices(vertices, blendMode, paint); |
|
|
|
_screenshot.drawVertices(vertices, blendMode, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override int getSaveCount() { |
|
|
|
return _main.getSaveCount(); |
|
|
|
} |
|
|
|
|
|
|
|
public override void restore() { |
|
|
|
_main.restore(); |
|
|
|
_screenshot.restore(); |
|
|
|
} |
|
|
|
|
|
|
|
public override void rotate(float radians) { |
|
|
|
_main.rotate(radians); |
|
|
|
_screenshot.rotate(radians); |
|
|
|
} |
|
|
|
|
|
|
|
public override void save() { |
|
|
|
_main.save(); |
|
|
|
_screenshot.save(); |
|
|
|
} |
|
|
|
|
|
|
|
public override void saveLayer(Rect bounds, Paint paint) { |
|
|
|
_main.saveLayer(bounds, paint); |
|
|
|
_screenshot.saveLayer(bounds, paint); |
|
|
|
} |
|
|
|
|
|
|
|
public override void scale(float sx, float? sy = null ) { |
|
|
|
_main.scale(sx, sy); |
|
|
|
_screenshot.scale(sx, sy); |
|
|
|
} |
|
|
|
|
|
|
|
public override void skew(float sx, float sy) { |
|
|
|
_main.skew(sx, sy); |
|
|
|
_screenshot.skew(sx, sy); |
|
|
|
} |
|
|
|
|
|
|
|
public override void transform(float[] matrix4) { |
|
|
|
_main.transform(matrix4); |
|
|
|
_screenshot.transform(matrix4); |
|
|
|
} |
|
|
|
|
|
|
|
public override void translate(float dx, float dy) { |
|
|
|
_main.translate(dx, dy); |
|
|
|
_screenshot.translate(dx, dy); |
|
|
|
} |
|
|
|
} |
|
|
|
public class _ScreenshotContainerLayer : OffsetLayer { |
|
|
|
public override void addToScene(ui.SceneBuilder builder ,Offset layerOffset = null) { |
|
|
|
layerOffset = layerOffset ?? Offset.zero; |
|
|
|
addChildrenToScene(builder, layerOffset); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class _SerializeConfig { |
|
|
|
public _SerializeConfig(string groupName, bool summaryTree = false, |
|
|
|
int subtreeDepth = 1, List<Diagnosticable> pathToInclude = null, |
|
|
|
bool includeProperties = false, bool expandPropertyValues = true) { |
|
|
|
|
|
|
public InspectorSelectionChangedCallback selectionChangedCallback; |
|
|
|
public DeveloperInspect developerInspect; |
|
|
|
public InspectorShowCallback inspectorShowCallback; |
|
|
|
|
|
|
|
List<String> _pubRootDirectories; |
|
|
|
|
|
|
|
public static WidgetInspectorService instance { |
|
|
|
get { return WidgetsBinding.instance.widgetInspectorService; } |
|
|
|
|
|
|
// get { return _instance; }
|
|
|
|
// }
|
|
|
|
|
|
|
|
public bool _isLocalCreationLocation(_Location location) { |
|
|
|
if (location == null || location.file == null) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
string file = new Uri(location.file).LocalPath; //Uri.parse(location.file).path;
|
|
|
|
|
|
|
|
// By default check whether the creation location was within package:flutter.
|
|
|
|
if (_pubRootDirectories == null) { |
|
|
|
// TODO(chunhtai): Make it more robust once
|
|
|
|
// https://github.com/flutter/flutter/issues/32660 is fixed.
|
|
|
|
return !file.Contains("packages/flutter/"); |
|
|
|
} |
|
|
|
|
|
|
|
foreach (string directory in _pubRootDirectories) { |
|
|
|
if(file.StartsWith(directory)) |
|
|
|
{ |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
public List<DiagnosticsNode> _filterChildren( |
|
|
|
List<DiagnosticsNode> nodes, |
|
|
|
InspectorSerializationDelegate _delegate |
|
|
|
) { |
|
|
|
|
|
|
|
List<DiagnosticsNode> result = new List<DiagnosticsNode>(); |
|
|
|
foreach ( DiagnosticsNode child in nodes) { |
|
|
|
if (!_delegate.summaryTree || _shouldShowInSummaryTree(child)) |
|
|
|
result.Add(child); |
|
|
|
else |
|
|
|
result = _getChildrenFiltered(child, _delegate); |
|
|
|
} |
|
|
|
List<DiagnosticsNode> children = result; |
|
|
|
return children; |
|
|
|
} |
|
|
|
|
|
|
|
public List<DiagnosticsNode> _truncateNodes(IEnumerable<DiagnosticsNode>nodes, int maxDescendentsTruncatableNode) { |
|
|
|
int count = 0; |
|
|
|
foreach (var node in nodes) { |
|
|
|
if (node is DiagnosticsNode) { |
|
|
|
if (((DiagnosticsNode) node).value is Element) { |
|
|
|
count++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (count == nodes.Count() && isWidgetCreationTracked()) { |
|
|
|
List<DiagnosticsNode> localNodes = nodes.Where((DiagnosticsNode node) => |
|
|
|
_isValueCreatedByLocalProject(node.value)).ToList(); |
|
|
|
if (localNodes.isNotEmpty()) { |
|
|
|
return localNodes; |
|
|
|
} |
|
|
|
} |
|
|
|
//return nodes.take(maxDescendentsTruncatableNode).toList();
|
|
|
|
List<DiagnosticsNode> results = new List<DiagnosticsNode>(); |
|
|
|
for (int i = 0; i < maxDescendentsTruncatableNode; i++) { |
|
|
|
results.Add(nodes.ToList()[i]); |
|
|
|
} |
|
|
|
return results; |
|
|
|
} |
|
|
|
|
|
|
|
return _nodeToJson(widgetsBinding.renderViewElement.toDiagnosticsNode(), |
|
|
|
new _SerializeConfig(groupName)); |
|
|
|
return _nodeToJson(WidgetsBinding.instance?.renderViewElement?.toDiagnosticsNode(), new InspectorSerializationDelegate(groupName: groupName, service: this)); |
|
|
|
return _nodeToJson(widgetsBinding.renderViewElement.toDiagnosticsNode(), |
|
|
|
new _SerializeConfig(groupName, subtreeDepth: 1000000, summaryTree: true)); |
|
|
|
return _nodeToJson( |
|
|
|
WidgetsBinding.instance?.renderViewElement?.toDiagnosticsNode(), |
|
|
|
new InspectorSerializationDelegate(groupName: groupName, subtreeDepth: 1000000, summaryTree: true, service: this)); |
|
|
|
return _nodeToJson(widgetsBinding.renderView.toDiagnosticsNode(), |
|
|
|
new _SerializeConfig(groupName: groupName, subtreeDepth: 1000000)); |
|
|
|
return _nodeToJson(RendererBinding.instance?.renderView?.toDiagnosticsNode(), |
|
|
|
new InspectorSerializationDelegate(groupName: groupName, service: this)); |
|
|
|
|
|
|
|
public Dictionary<string, object> getDetailsSubtree(string id, string groupName) { |
|
|
|
public Dictionary<string, object> getDetailsSubtree(string id, string groupName,int subtreeDepth) { |
|
|
|
return _nodeToJson(root, |
|
|
|
new _SerializeConfig(groupName, summaryTree: false, subtreeDepth: 2, includeProperties: true)); |
|
|
|
return _nodeToJson( |
|
|
|
root, |
|
|
|
new InspectorSerializationDelegate( |
|
|
|
groupName: groupName, |
|
|
|
summaryTree: false, |
|
|
|
subtreeDepth: subtreeDepth, |
|
|
|
includeProperties: true, |
|
|
|
service: this |
|
|
|
) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
public bool setSelectionById(string id, string groupName = "") { |
|
|
|
|
|
|
|
|
|
|
public Dictionary<string, object> getSelectedRenderObject(string previousSelectionId, string groupName) { |
|
|
|
DiagnosticsNode previousSelection = toObject(previousSelectionId) as DiagnosticsNode; |
|
|
|
RenderObject current = selection == null ? null : selection.current; |
|
|
|
return _nodeToJson( |
|
|
|
current == (previousSelection == null ? null : previousSelection.valueObject) |
|
|
|
? previousSelection |
|
|
|
: (current == null ? null : current.toDiagnosticsNode()), |
|
|
|
new _SerializeConfig(groupName: groupName)); |
|
|
|
RenderObject current = selection?.current; |
|
|
|
return _nodeToJson(current == previousSelection?.value ? previousSelection : current?.toDiagnosticsNode(), new InspectorSerializationDelegate(groupName: groupName, service: this)); |
|
|
|
DiagnosticsNode previousSelection = (DiagnosticsNode) toObject(previousSelectionId); |
|
|
|
Element current = null; |
|
|
|
if (selection != null) { |
|
|
|
current = selection.currentElement; |
|
|
|
} |
|
|
|
|
|
|
|
return _nodeToJson( |
|
|
|
current == (previousSelection == null ? null : previousSelection.valueObject) |
|
|
|
? previousSelection |
|
|
|
: (current == null ? null : current.toDiagnosticsNode()), |
|
|
|
new _SerializeConfig(groupName: groupName)); |
|
|
|
DiagnosticsNode previousSelection = toObject(previousSelectionId) as DiagnosticsNode; |
|
|
|
Element current = selection?.currentElement; |
|
|
|
return _nodeToJson(current == previousSelection?.value ? previousSelection : current?.toDiagnosticsNode(), new InspectorSerializationDelegate(groupName: groupName, service: this)); |
|
|
|
} |
|
|
|
|
|
|
|
public Dictionary<string, object> getSelectedSummaryWidget(string previousSelectionId, string groupName) { |
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
Dictionary<string, object> _nodeToJson( |
|
|
|
public Dictionary<string, object> _nodeToJson( |
|
|
|
DiagnosticsNode node, |
|
|
|
InspectorSerializationDelegate _delegate |
|
|
|
) { |
|
|
|
|
|
|
return ret; |
|
|
|
}*/ |
|
|
|
|
|
|
|
List<Dictionary<string, object>> _nodesToJson(List<DiagnosticsNode> nodes, _SerializeConfig config) { |
|
|
|
if (nodes == null) { |
|
|
|
return new List<Dictionary<string, object>>(); |
|
|
|
} |
|
|
|
|
|
|
|
return nodes.Select(node => { |
|
|
|
if (config.pathToInclude != null && config.pathToInclude.Count > 0) { |
|
|
|
if (config.pathToInclude[0] == node.valueObject) { |
|
|
|
return _nodeToJson(node, _SerializeConfig.merge(config, |
|
|
|
pathToInclude: config.pathToInclude.GetRange(1, config.pathToInclude.Count - 1))); |
|
|
|
} |
|
|
|
else { |
|
|
|
return _nodeToJson(node, _SerializeConfig.merge(config)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return _nodeToJson(node, |
|
|
|
config.summaryTree || config.subtreeDepth > 1 || _shouldShowInSummaryTree(node) |
|
|
|
? _SerializeConfig.merge(config, subtreeDepth: config.subtreeDepth - 1) |
|
|
|
: config |
|
|
|
); |
|
|
|
}).ToList(); |
|
|
|
List<Dictionary<string, object>> _nodesToJson( |
|
|
|
List<DiagnosticsNode> nodes, |
|
|
|
InspectorSerializationDelegate _delegate, |
|
|
|
DiagnosticsNode parent |
|
|
|
) { |
|
|
|
return DiagnosticsNode.toJsonList(nodes, parent, _delegate); |
|
|
|
public List<Dictionary<string, object>> getProperties(string diagnosticsNodeId, string groupName) { |
|
|
|
var node = toObject(diagnosticsNodeId) as DiagnosticsNode; |
|
|
|
return _nodesToJson(node == null ? new List<DiagnosticsNode>() : node.getProperties(), |
|
|
|
new _SerializeConfig(groupName: groupName)); |
|
|
|
List<Dictionary<string, object>> _getProperties(string diagnosticsNodeId, string groupName) { |
|
|
|
DiagnosticsNode node = toObject(diagnosticsNodeId) as DiagnosticsNode; |
|
|
|
return _nodesToJson(node == null ? new List<DiagnosticsNode>() : node.getProperties(), new InspectorSerializationDelegate(groupName: groupName, service: this), parent: node); |
|
|
|
List<DiagnosticsNode> _getChildrenFiltered(DiagnosticsNode node, |
|
|
|
_SerializeConfig config) { |
|
|
|
var children = new List<DiagnosticsNode>(); |
|
|
|
foreach (var child in node.getChildren()) { |
|
|
|
if (!config.summaryTree || _shouldShowInSummaryTree(child)) { |
|
|
|
children.Add(child); |
|
|
|
} |
|
|
|
else { |
|
|
|
children.AddRange(_getChildrenFiltered(child, config)); |
|
|
|
} |
|
|
|
} |
|
|
|
return children; |
|
|
|
List<DiagnosticsNode> _getChildrenFiltered( |
|
|
|
DiagnosticsNode node, |
|
|
|
InspectorSerializationDelegate _delegate |
|
|
|
) { |
|
|
|
return _filterChildren(node.getChildren(), _delegate); |
|
|
|
bool _shouldShowInSummaryTree(DiagnosticsNode node) { |
|
|
|
public bool _shouldShowInSummaryTree(DiagnosticsNode node) { |
|
|
|
var value = node.valueObject; |
|
|
|
if (!(value is Diagnosticable)) { |
|
|
|
return true; |
|
|
|
|
|
|
return _isValueCreatedByLocalProject(value); |
|
|
|
} |
|
|
|
|
|
|
|
string toId(object obj, string groupName) { |
|
|
|
public string toId(object obj, string groupName) { |
|
|
|
if (obj == null) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
return data.obj; |
|
|
|
} |
|
|
|
|
|
|
|
bool isWidgetCreationTracked() { |
|
|
|
public bool isWidgetCreationTracked() { |
|
|
|
return false; //todo
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
public readonly InspectorSelectButtonBuilder selectButtonBuilder; |
|
|
|
|
|
|
|
public override State createState() { |
|
|
|
return new _WidgetInspectorState(WidgetsBinding.instance.widgetInspectorService.selection); |
|
|
|
return new _WidgetInspectorState(); |
|
|
|
} |
|
|
|
} |
|
|
|
public class InspectorSerializationDelegate : DiagnosticsSerializationDelegate { |
|
|
|
public InspectorSerializationDelegate( |
|
|
|
string groupName = null, |
|
|
|
bool? summaryTree = null, |
|
|
|
int? maxDescendentsTruncatableNode = null, |
|
|
|
bool? expandPropertyValues = null, |
|
|
|
int? subtreeDepth = null, |
|
|
|
bool? includeProperties = null, |
|
|
|
WidgetInspectorService service = null, |
|
|
|
AddAdditionalPropertiesCallback addAdditionalPropertiesCallback = null |
|
|
|
) { |
|
|
|
this.groupName = groupName; |
|
|
|
this.summaryTree = summaryTree ?? false; |
|
|
|
this.maxDescendentsTruncatableNode = maxDescendentsTruncatableNode ?? -1; |
|
|
|
this.expandPropertyValues = expandPropertyValues ?? true; |
|
|
|
this.subtreeDepth = subtreeDepth ?? 1; |
|
|
|
this.includeProperties = includeProperties ?? false; |
|
|
|
this.service = service; |
|
|
|
this.addAdditionalPropertiesCallback = addAdditionalPropertiesCallback; |
|
|
|
} |
|
|
|
public readonly string groupName; |
|
|
|
public readonly bool summaryTree; |
|
|
|
public readonly int maxDescendentsTruncatableNode; |
|
|
|
public readonly bool expandPropertyValues; |
|
|
|
public readonly int subtreeDepth; |
|
|
|
public readonly bool includeProperties; |
|
|
|
public readonly WidgetInspectorService service; |
|
|
|
public readonly AddAdditionalPropertiesCallback addAdditionalPropertiesCallback ; |
|
|
|
public readonly List<DiagnosticsNode> _nodesCreatedByLocalProject = new List<DiagnosticsNode>(); |
|
|
|
public bool _interactive { |
|
|
|
get { return groupName != null;} |
|
|
|
} |
|
|
|
|
|
|
|
public override Dictionary<string, object> additionalNodeProperties(DiagnosticsNode node) { |
|
|
|
Dictionary<string, object> result = new Dictionary<string, object>(); |
|
|
|
object value = node.value; |
|
|
|
if (_interactive) { |
|
|
|
result["objectId"] = service.toId(node, groupName); |
|
|
|
result["valueId"] = service.toId(value, groupName); |
|
|
|
} |
|
|
|
if (summaryTree) { |
|
|
|
result["summaryTree"] = true; |
|
|
|
} |
|
|
|
_Location creationLocation = WidgetInspectorUtils._getCreationLocation(value); |
|
|
|
if (creationLocation != null) { |
|
|
|
result["locationId"] = WidgetInspectorUtils._toLocationId(creationLocation); |
|
|
|
result["creationLocation"] = creationLocation.toJsonMap(); |
|
|
|
if (service._isLocalCreationLocation(creationLocation)) { |
|
|
|
_nodesCreatedByLocalProject.Add(node); |
|
|
|
result["createdByLocalProject"] = true; |
|
|
|
} |
|
|
|
} |
|
|
|
if (addAdditionalPropertiesCallback != null) { |
|
|
|
if (addAdditionalPropertiesCallback(node, this) != null) { |
|
|
|
foreach (var callback in addAdditionalPropertiesCallback(node,this)) { |
|
|
|
result.Add(callback.Key,callback.Value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//result.addAll(addAdditionalPropertiesCallback(node, this) ?? <string, object>{});
|
|
|
|
} |
|
|
|
return result; |
|
|
|
} |
|
|
|
public override DiagnosticsSerializationDelegate delegateForNode(DiagnosticsNode node) { |
|
|
|
return summaryTree || subtreeDepth > 1 || service._shouldShowInSummaryTree(node) |
|
|
|
? copyWith(subtreeDepth: subtreeDepth - 1) |
|
|
|
: this; |
|
|
|
} |
|
|
|
public override List<DiagnosticsNode> filterChildren(List<DiagnosticsNode> children, DiagnosticsNode owner) { |
|
|
|
return service._filterChildren(children, this); |
|
|
|
} |
|
|
|
public override List<DiagnosticsNode> filterProperties(List<DiagnosticsNode> properties, DiagnosticsNode owner) { |
|
|
|
bool createdByLocalProject = _nodesCreatedByLocalProject.Contains(owner); |
|
|
|
return properties.Where((DiagnosticsNode node)=>{ |
|
|
|
return !node.isFiltered(createdByLocalProject ? DiagnosticLevel.fine : DiagnosticLevel.info); |
|
|
|
}).ToList(); |
|
|
|
} |
|
|
|
public override List<DiagnosticsNode> truncateNodesList(List<DiagnosticsNode> nodes, DiagnosticsNode owner) { |
|
|
|
if (maxDescendentsTruncatableNode >= 0 && |
|
|
|
owner?.allowTruncate == true && |
|
|
|
nodes.Count > maxDescendentsTruncatableNode) { |
|
|
|
nodes = service._truncateNodes(nodes, maxDescendentsTruncatableNode); |
|
|
|
} |
|
|
|
return nodes; |
|
|
|
} |
|
|
|
public override DiagnosticsSerializationDelegate copyWith(int? subtreeDepth = null, bool? includeProperties = null) { |
|
|
|
return new InspectorSerializationDelegate( |
|
|
|
groupName: groupName, |
|
|
|
summaryTree: summaryTree, |
|
|
|
maxDescendentsTruncatableNode: maxDescendentsTruncatableNode, |
|
|
|
expandPropertyValues: expandPropertyValues, |
|
|
|
subtreeDepth: subtreeDepth ?? this.subtreeDepth, |
|
|
|
includeProperties: includeProperties ?? this.includeProperties, |
|
|
|
service: service, |
|
|
|
addAdditionalPropertiesCallback: addAdditionalPropertiesCallback |
|
|
|
); |
|
|
|
public class _Location { |
|
|
|
public _Location( |
|
|
|
int line, |
|
|
|
int column, |
|
|
|
string file = null, |
|
|
|
string name = null, |
|
|
|
List<_Location> parameterLocations = null |
|
|
|
) { |
|
|
|
this.file = file; |
|
|
|
this.line = line; |
|
|
|
this.column = column; |
|
|
|
this.name = name; |
|
|
|
this.parameterLocations = parameterLocations; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly string file; |
|
|
|
public readonly int line; |
|
|
|
public readonly int column; |
|
|
|
public readonly string name; |
|
|
|
public readonly List<_Location> parameterLocations; |
|
|
|
|
|
|
|
public Dictionary<string, object> toJsonMap() { |
|
|
|
Dictionary<string, object> json = new Dictionary<string, object>(); |
|
|
|
json["file"] = file; |
|
|
|
json["line"] = line; |
|
|
|
json["column"] = column; |
|
|
|
if (name != null) { |
|
|
|
json["name"] = name; |
|
|
|
} |
|
|
|
|
|
|
|
if (parameterLocations != null) { |
|
|
|
json["parameterLocations"] = parameterLocations.Select( |
|
|
|
(_Location location) => location.toJsonMap()).ToList(); |
|
|
|
} |
|
|
|
|
|
|
|
return json; |
|
|
|
} |
|
|
|
|
|
|
|
public override string ToString() { |
|
|
|
List<string> parts = new List<string>(); |
|
|
|
if (name != null) { |
|
|
|
parts.Add(name); |
|
|
|
} |
|
|
|
|
|
|
|
if (file != null) { |
|
|
|
parts.Add(file); |
|
|
|
} |
|
|
|
parts.Add("$line"); |
|
|
|
parts.Add("$column"); |
|
|
|
string result = ""; |
|
|
|
foreach (var part in parts) { |
|
|
|
result += part; |
|
|
|
if(part!= parts.last()) |
|
|
|
result += ":"; |
|
|
|
} |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public _WidgetInspectorState(InspectorSelection selection) { |
|
|
|
this.selection = selection; |
|
|
|
public _WidgetInspectorState() { |
|
|
|
selection = WidgetInspectorService.instance.selection; |
|
|
|
} |
|
|
|
|
|
|
|
Offset _lastPointerLocation; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _InspectorOverlayLayer : Layer { |
|
|
|
// const int _kMaxTooltipLines = 5;
|
|
|
|
// private static Color _kTooltipBackgroundColor = Color.fromARGB(230, 60, 60, 60);
|
|
|
|
static Color _kHighlightedRenderObjectFillColor = Color.fromARGB(128, 128, 128, 255); |
|
|
|
static Color _kHighlightedRenderObjectBorderColor = Color.fromARGB(128, 64, 64, 128); |
|
|
|
D.assert(overlayRect != null); |
|
|
|
D.assert(selection != null); |
|
|
|
bool inDebugMode = false; |
|
|
|
D.assert(()=> { |
|
|
|
inDebugMode = true; |
|
|
|
return true; |
|
|
|
}); |
|
|
|
if (inDebugMode == false) { |
|
|
|
throw new UIWidgetsError( |
|
|
|
"The inspector should never be used in production mode due to the " + |
|
|
|
"negative performance impact." |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public InspectorSelection selection; |
|
|
|
|
|
|
public Picture _picture; |
|
|
|
// public TextPainter _textPainter;
|
|
|
|
// public float _textPainterMaxWidth;
|
|
|
|
public TextPainter _textPainter; |
|
|
|
public float _textPainterMaxWidth; |
|
|
|
internal override S find<S>(Offset regionOffset) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
internal override void addToScene(SceneBuilder builder, Offset layerOffset = null) { |
|
|
|
public override void addToScene(ui.SceneBuilder builder, Offset layerOffset = null) { |
|
|
|
|
|
|
|
if (!selection.active) { |
|
|
|
if (!selection.active) |
|
|
|
} |
|
|
|
foreach (RenderObject candidate in selection.candidates) { |
|
|
|
if (candidate == selected || !candidate.attached) { |
|
|
|
foreach ( RenderObject candidate in selection.candidates) { |
|
|
|
if (candidate == selected || !candidate.attached) |
|
|
|
} |
|
|
|
|
|
|
|
_InspectorOverlayRenderState state = new _InspectorOverlayRenderState(overlayRect, |
|
|
|
new _TransformedRect(selected), |
|
|
|
candidates, selection.currentElement.toStringShort(), |
|
|
|
TextDirection.ltr |
|
|
|
_InspectorOverlayRenderState state = new _InspectorOverlayRenderState( |
|
|
|
overlayRect: overlayRect, |
|
|
|
selected: new _TransformedRect(selected), |
|
|
|
tooltip: selection.currentElement.toStringShort(), |
|
|
|
textDirection: TextDirection.ltr, |
|
|
|
candidates: candidates |
|
|
|
); |
|
|
|
|
|
|
|
if (state != _lastState) { |
|
|
|
|
|
|
|
|
|
|
builder.addPicture(layerOffset, _picture); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
Canvas canvas = new Canvas(recorder, state.overlayRect); |
|
|
|
Size size = state.overlayRect.size; |
|
|
|
|
|
|
|
var fillPaint = new Paint() {color = _kHighlightedRenderObjectFillColor}; |
|
|
|
var fillPaint = new Paint() {style = PaintingStyle.fill,color = WidgetInspectorUtils._kHighlightedRenderObjectFillColor}; |
|
|
|
color = _kHighlightedRenderObjectBorderColor, style = PaintingStyle.stroke, |
|
|
|
color = WidgetInspectorUtils._kHighlightedRenderObjectBorderColor, |
|
|
|
style = PaintingStyle.stroke, |
|
|
|
strokeWidth = 1 |
|
|
|
}; |
|
|
|
Rect selectedPaintRect = state.selected.rect.deflate(0.5f); |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// todo paint descipion
|
|
|
|
Rect targetRect = MatrixUtils.transformRect( |
|
|
|
state.selected.transform, state.selected.rect); |
|
|
|
Offset target = new Offset(targetRect.left, targetRect.center.dy); |
|
|
|
float offsetFromWidget = 9.0f; |
|
|
|
float verticalOffset = (targetRect.height) / 2 + offsetFromWidget; |
|
|
|
|
|
|
|
_paintDescription(canvas, state.tooltip, state.textDirection, target, verticalOffset, size, targetRect); |
|
|
|
|
|
|
|
// TODO(jacobr): provide an option to perform a debug paint of just the
|
|
|
|
// selected widget.
|
|
|
|
} |
|
|
|
public void _paintDescription( |
|
|
|
Canvas canvas, |
|
|
|
String message, |
|
|
|
TextDirection textDirection, |
|
|
|
Offset target, |
|
|
|
float verticalOffset, |
|
|
|
Size size, |
|
|
|
Rect targetRect |
|
|
|
) { |
|
|
|
canvas.save(); |
|
|
|
float maxWidth = size.width - 2 * (WidgetInspectorUtils._kScreenEdgeMargin + WidgetInspectorUtils._kTooltipPadding); |
|
|
|
TextSpan textSpan = _textPainter?.text as TextSpan; |
|
|
|
if (_textPainter == null || textSpan.text != message || _textPainterMaxWidth != maxWidth) { |
|
|
|
_textPainterMaxWidth = maxWidth; |
|
|
|
_textPainter = new TextPainter(); |
|
|
|
_textPainter.maxLines = WidgetInspectorUtils._kMaxTooltipLines; |
|
|
|
_textPainter.ellipsis = "..."; |
|
|
|
_textPainter.text = new TextSpan(style: WidgetInspectorUtils._messageStyle, text: message); |
|
|
|
_textPainter.textDirection = textDirection; |
|
|
|
_textPainter.layout(maxWidth: maxWidth); |
|
|
|
} |
|
|
|
|
|
|
|
Size tooltipSize = _textPainter.size + new Offset(WidgetInspectorUtils._kTooltipPadding * 2, WidgetInspectorUtils._kTooltipPadding * 2); |
|
|
|
Offset tipOffset = Geometry.positionDependentBox( |
|
|
|
size: size, |
|
|
|
childSize: tooltipSize, |
|
|
|
target: target, |
|
|
|
verticalOffset: verticalOffset, |
|
|
|
preferBelow: false |
|
|
|
); |
|
|
|
|
|
|
|
Paint tooltipBackground = new Paint(); |
|
|
|
tooltipBackground.style = PaintingStyle.fill; |
|
|
|
tooltipBackground.color = WidgetInspectorUtils._kTooltipBackgroundColor; |
|
|
|
canvas.drawRect( |
|
|
|
Rect.fromPoints( |
|
|
|
tipOffset, |
|
|
|
tipOffset.translate(tooltipSize.width, tooltipSize.height) |
|
|
|
), |
|
|
|
tooltipBackground |
|
|
|
); |
|
|
|
|
|
|
|
float wedgeY = tipOffset.dy; |
|
|
|
bool tooltipBelow = tipOffset.dy > target.dy; |
|
|
|
if (!tooltipBelow) |
|
|
|
wedgeY += tooltipSize.height; |
|
|
|
float wedgeSize = WidgetInspectorUtils._kTooltipPadding * 2; |
|
|
|
float wedgeX = Mathf.Max(tipOffset.dx, target.dx) + wedgeSize * 2; |
|
|
|
wedgeX = Mathf.Min(wedgeX, tipOffset.dx + tooltipSize.width - wedgeSize * 2); |
|
|
|
List<Offset> wedge = new List<Offset>(){ |
|
|
|
new Offset(wedgeX - wedgeSize, wedgeY), |
|
|
|
new Offset(wedgeX + wedgeSize, wedgeY), |
|
|
|
new Offset(wedgeX, wedgeY + (tooltipBelow ? -wedgeSize : wedgeSize)), |
|
|
|
}; |
|
|
|
Path path = new Path(); |
|
|
|
path.addPolygon(wedge,true); |
|
|
|
canvas.drawPath(path, tooltipBackground); |
|
|
|
_textPainter.paint(canvas, tipOffset + new Offset(WidgetInspectorUtils._kTooltipPadding, WidgetInspectorUtils._kTooltipPadding)); |
|
|
|
canvas.restore(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override bool findAnnotations<S>( |
|
|
|
AnnotationResult<S> result, |
|
|
|
Offset localPosition, |
|
|
|
bool onlyFirst) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |