您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
873 行
19 KiB
873 行
19 KiB
/*using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using uiwidgets;
|
|
using Unity.UIWidgets.async2;
|
|
using Unity.UIWidgets.DevTools.inspector;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.ui;
|
|
using Unity.UIWidgets.widgets;
|
|
using UnityEngine;
|
|
using Color = Unity.UIWidgets.ui.Color;
|
|
using Rect = Unity.UIWidgets.ui.Rect;
|
|
|
|
namespace Unity.UIWidgets.DevTools.inspector
|
|
{
|
|
public class InspectorTreeUtils
|
|
{
|
|
public static readonly Regex treeNodePrimaryDescriptionPattern = new Regex(@"^([\w ]+)(.*)$");
|
|
// TODO(jacobr): temporary workaround for missing structure from assertion thrown building
|
|
// widget errors.
|
|
public static readonly Regex assertionThrownBuildingError = new Regex(
|
|
@"^(The following assertion was thrown building [a-zA-Z]+)(\(.*\))(:)$");
|
|
|
|
public delegate void TreeEventCallback(InspectorTreeNode node);
|
|
|
|
|
|
public static readonly float iconPadding = 5.0f;
|
|
public static readonly float chartLineStrokeWidth = 1.0f;
|
|
public static readonly float columnWidth = 16.0f;
|
|
public static readonly float verticalPadding = 10.0f;
|
|
public static readonly float rowHeight = 24.0f;
|
|
|
|
|
|
// TODO(jacobr): merge this scheme with other color schemes in DevTools.
|
|
public static Color selectedRowBackgroundColor
|
|
{
|
|
get
|
|
{
|
|
|
|
return CommonThemeUtils.isLight
|
|
? Color.fromARGB(255, 202, 191, 69)
|
|
: Color.fromARGB(255, 99, 101, 103);
|
|
}
|
|
}
|
|
|
|
public static Color hoverColor
|
|
{
|
|
get
|
|
{
|
|
return CommonThemeUtils.isLight ? Colors.yellowAccent : Color.fromARGB(255, 70, 73, 76);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// TODO(kenz): extend TreeNode class to share tree logic.
|
|
public class InspectorTreeNode {
|
|
public InspectorTreeNode(
|
|
InspectorTreeNode parent = null,
|
|
bool expandChildren = true
|
|
)
|
|
{
|
|
_children = new List<InspectorTreeNode>();
|
|
_parent = parent;
|
|
_isExpanded = expandChildren;
|
|
}
|
|
|
|
bool showLinesToChildren {
|
|
get
|
|
{
|
|
return _children.Count > 1 && !_children.Last().isProperty;
|
|
}
|
|
|
|
}
|
|
|
|
public bool isDirty
|
|
{
|
|
get
|
|
{
|
|
return _isDirty;
|
|
}
|
|
set
|
|
{
|
|
if (value) {
|
|
_isDirty = true;
|
|
_shouldShow = null;
|
|
if (_childrenCount == null) {
|
|
// Already dirty.
|
|
return;
|
|
}
|
|
_childrenCount = null;
|
|
if (parent != null) {
|
|
parent.isDirty = true;
|
|
}
|
|
} else {
|
|
_isDirty = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bool _isDirty = true;
|
|
|
|
|
|
|
|
/// Returns whether the node is currently visible in the tree.
|
|
void updateShouldShow(bool value) {
|
|
if (value != _shouldShow) {
|
|
_shouldShow = value;
|
|
foreach (var child in children) {
|
|
child.updateShouldShow(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool? shouldShow {
|
|
get
|
|
{
|
|
_shouldShow = _shouldShow ?? parent == null || parent.isExpanded && parent.shouldShow.Value;
|
|
return _shouldShow;
|
|
}
|
|
|
|
}
|
|
|
|
bool? _shouldShow;
|
|
|
|
public bool selected = false;
|
|
|
|
RemoteDiagnosticsNode _diagnostic;
|
|
public readonly List<InspectorTreeNode> _children;
|
|
|
|
public IEnumerable<InspectorTreeNode> children
|
|
{
|
|
get
|
|
{
|
|
return _children;
|
|
}
|
|
}
|
|
|
|
bool isCreatedByLocalProject
|
|
{
|
|
get
|
|
{
|
|
return _diagnostic.isCreatedByLocalProject;
|
|
}
|
|
}
|
|
|
|
bool isProperty
|
|
{
|
|
get
|
|
{
|
|
return diagnostic == null || diagnostic.isProperty;
|
|
}
|
|
}
|
|
|
|
public bool isExpanded
|
|
{
|
|
get
|
|
{
|
|
return _isExpanded;
|
|
}
|
|
set
|
|
{
|
|
if (value != _isExpanded) {
|
|
_isExpanded = value;
|
|
isDirty = true;
|
|
if (_shouldShow ?? false) {
|
|
foreach (var child in children) {
|
|
child.updateShouldShow(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool _isExpanded;
|
|
|
|
bool allowExpandCollapse = true;
|
|
|
|
bool showExpandCollapse {
|
|
get
|
|
{
|
|
return (diagnostic?.hasChildren == true || children.Any()) &&
|
|
allowExpandCollapse;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public InspectorTreeNode parent
|
|
{
|
|
get
|
|
{
|
|
return _parent;
|
|
}
|
|
set
|
|
{
|
|
_parent = value;
|
|
_parent.isDirty = true;
|
|
}
|
|
|
|
}
|
|
|
|
InspectorTreeNode _parent;
|
|
|
|
|
|
|
|
public RemoteDiagnosticsNode diagnostic
|
|
{
|
|
get
|
|
{
|
|
return _diagnostic;
|
|
}
|
|
|
|
set
|
|
{
|
|
_diagnostic = value;
|
|
_isExpanded = value.childrenReady;
|
|
//isDirty = true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public int? childrenCount {
|
|
get
|
|
{
|
|
if (!isExpanded) {
|
|
_childrenCount = 0;
|
|
}
|
|
if (_childrenCount != null) {
|
|
return _childrenCount;
|
|
}
|
|
int count = 0;
|
|
foreach (InspectorTreeNode child in _children) {
|
|
count += (child.subtreeSize?? 0);
|
|
}
|
|
_childrenCount = count;
|
|
return _childrenCount;
|
|
}
|
|
|
|
}
|
|
|
|
public bool hasPlaceholderChildren {
|
|
get
|
|
{
|
|
return children.Count() == 1 && children.First().diagnostic == null;
|
|
}
|
|
|
|
}
|
|
|
|
int? _childrenCount;
|
|
|
|
public int? subtreeSize
|
|
{
|
|
get
|
|
{
|
|
return childrenCount + 1;
|
|
}
|
|
}
|
|
|
|
bool isLeaf
|
|
{
|
|
get
|
|
{
|
|
return _children.isEmpty();
|
|
}
|
|
}
|
|
|
|
// TODO(jacobr): move getRowIndex to the InspectorTree class.
|
|
public int getRowIndex(InspectorTreeNode node) {
|
|
int index = 0;
|
|
while (true) {
|
|
InspectorTreeNode parent = node.parent;
|
|
if (parent == null) {
|
|
break;
|
|
}
|
|
foreach (InspectorTreeNode sibling in parent._children) {
|
|
if (sibling == node) {
|
|
break;
|
|
}
|
|
index += (sibling.subtreeSize?? 0);
|
|
}
|
|
index += 1;
|
|
node = parent;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// TODO(jacobr): move this method to the InspectorTree class.
|
|
// TODO: optimize this method.
|
|
/// Use [getCachedRow] wherever possible, as [getRow] is slow and can cause
|
|
/// performance problems.
|
|
public InspectorTreeRow getRow(int index) {
|
|
List<int> ticks = new List<int>();
|
|
InspectorTreeNode node = this;
|
|
if (subtreeSize <= index) {
|
|
return null;
|
|
}
|
|
int current = 0;
|
|
int depth = 0;
|
|
while (node != null) {
|
|
var style = node.diagnostic?.style;
|
|
bool indented = style != DiagnosticsTreeStyle.flat &&
|
|
style != DiagnosticsTreeStyle.error;
|
|
if (current == index) {
|
|
return new InspectorTreeRow(
|
|
node: node,
|
|
index: index,
|
|
ticks: ticks,
|
|
depth: depth,
|
|
lineToParent:
|
|
!node.isProperty && index != 0 && node.parent.showLinesToChildren
|
|
);
|
|
}
|
|
D.assert(index > current);
|
|
current++;
|
|
List<InspectorTreeNode> children = node._children;
|
|
int i;
|
|
for (i = 0; i < children.Count; ++i) {
|
|
var child = children[i];
|
|
var subtreeSize = child.subtreeSize;
|
|
if (current + subtreeSize > index) {
|
|
node = child;
|
|
if (children.Count > 1 &&
|
|
i + 1 != children.Count &&
|
|
!children.Last().isProperty) {
|
|
if (indented) {
|
|
ticks.Add(depth);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
current += (subtreeSize?? 0);
|
|
}
|
|
D.assert(i < children.Count);
|
|
if (indented) {
|
|
depth++;
|
|
}
|
|
}
|
|
D.assert(false); // internal error.
|
|
return null;
|
|
}
|
|
|
|
public void removeChild(InspectorTreeNode child) {
|
|
child.parent = null;
|
|
var removed = _children.Remove(child);
|
|
D.assert(removed != null);
|
|
isDirty = true;
|
|
}
|
|
|
|
public void appendChild(InspectorTreeNode child) {
|
|
_children.Add(child);
|
|
child.parent = this;
|
|
isDirty = true;
|
|
}
|
|
|
|
public void clearChildren() {
|
|
_children.Clear();
|
|
isDirty = true;
|
|
}
|
|
}
|
|
|
|
/// A row in the tree with all information required to render it.
|
|
public class InspectorTreeRow {
|
|
public InspectorTreeRow(
|
|
InspectorTreeNode node,
|
|
int? index = null,
|
|
List<int> ticks = null,
|
|
int? depth = null,
|
|
bool? lineToParent = null
|
|
)
|
|
{
|
|
this.node = node;
|
|
this.index = index;
|
|
this.ticks = ticks;
|
|
this.depth = depth;
|
|
this.lineToParent = lineToParent;
|
|
}
|
|
|
|
public readonly InspectorTreeNode node;
|
|
|
|
/// Column indexes of ticks to draw lines from parents to children.
|
|
public readonly List<int> ticks;
|
|
public readonly int? depth;
|
|
public readonly int? index;
|
|
public readonly bool? lineToParent;
|
|
|
|
bool isSelected
|
|
{
|
|
get
|
|
{
|
|
return node.selected;
|
|
}
|
|
}
|
|
}
|
|
|
|
public delegate void NodeAddedCallback(InspectorTreeNode node, RemoteDiagnosticsNode diagnosticsNode);
|
|
|
|
public class InspectorTreeConfig {
|
|
public delegate void OnClientActiveChange(bool added);
|
|
public InspectorTreeConfig(
|
|
bool? summaryTree = null,
|
|
FlutterTreeType? treeType = null,
|
|
NodeAddedCallback onNodeAdded = null,
|
|
OnClientActiveChange onClientActiveChange = null,
|
|
VoidCallback onSelectionChange = null,
|
|
InspectorTreeUtils.TreeEventCallback onExpand = null,
|
|
InspectorTreeUtils.TreeEventCallback onHover = null
|
|
)
|
|
{
|
|
this.summaryTree = summaryTree;
|
|
this.treeType = treeType;
|
|
this.onNodeAdded = onNodeAdded;
|
|
this.onSelectionChange = onSelectionChange;
|
|
this.onExpand = onExpand;
|
|
this.onHover = onHover;
|
|
|
|
}
|
|
|
|
public readonly bool? summaryTree;
|
|
public readonly FlutterTreeType? treeType;
|
|
public readonly NodeAddedCallback onNodeAdded;
|
|
public readonly VoidCallback onSelectionChange;
|
|
public readonly OnClientActiveChange ONClientActiveChange;
|
|
public readonly InspectorTreeUtils.TreeEventCallback onExpand;
|
|
public readonly InspectorTreeUtils.TreeEventCallback onHover;
|
|
}
|
|
|
|
public abstract class InspectorTreeController
|
|
{
|
|
|
|
protected abstract void setState(VoidCallback fn);
|
|
|
|
public InspectorTreeNode root
|
|
{
|
|
get
|
|
{
|
|
return _root;
|
|
}
|
|
set
|
|
{
|
|
setState(() => {
|
|
_root = value;
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
InspectorTreeNode _root;
|
|
|
|
|
|
RemoteDiagnosticsNode subtreeRoot; // Optional.
|
|
|
|
public InspectorTreeNode selection
|
|
{
|
|
get
|
|
{
|
|
return _selection;
|
|
}
|
|
set
|
|
{
|
|
if (value == _selection) return;
|
|
|
|
setState(() => {
|
|
_selection.selected = false;
|
|
_selection = value;
|
|
_selection.selected = true;
|
|
if (config.onSelectionChange != null) {
|
|
config.onSelectionChange();
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
InspectorTreeNode _selection;
|
|
|
|
InspectorTreeConfig config
|
|
{
|
|
get
|
|
{
|
|
return _config;
|
|
}
|
|
set
|
|
{
|
|
// Only allow setting config once.
|
|
D.assert(_config == null);
|
|
_config = value;
|
|
}
|
|
|
|
}
|
|
|
|
InspectorTreeConfig _config;
|
|
|
|
InspectorTreeNode hover
|
|
{
|
|
get
|
|
{
|
|
return _hover;
|
|
}
|
|
set
|
|
{
|
|
if (value == _hover) {
|
|
return;
|
|
}
|
|
setState(() => {
|
|
_hover = value;
|
|
// TODO(jacobr): we could choose to repaint only a portion of the UI
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
InspectorTreeNode _hover;
|
|
|
|
float? lastContentWidth;
|
|
|
|
public abstract InspectorTreeNode createNode();
|
|
|
|
public readonly List<InspectorTreeRow> cachedRows = new List<InspectorTreeRow>();
|
|
|
|
// TODO: we should add a listener instead that clears the cache when the
|
|
// root is marked as dirty.
|
|
void _maybeClearCache() {
|
|
if (root.isDirty) {
|
|
cachedRows.Clear();
|
|
root.isDirty = false;
|
|
lastContentWidth = null;
|
|
}
|
|
}
|
|
|
|
public InspectorTreeRow getCachedRow(int index) {
|
|
_maybeClearCache();
|
|
while (cachedRows.Count <= index) {
|
|
cachedRows.Add(null);
|
|
}
|
|
cachedRows[index] = cachedRows[index] ??root.getRow(index);
|
|
return cachedRows[index];
|
|
}
|
|
|
|
double getRowOffset(int index) {
|
|
return (getCachedRow(index)?.depth ?? 0) * InspectorTreeUtils.columnWidth;
|
|
}
|
|
|
|
|
|
|
|
RemoteDiagnosticsNode currentHoverDiagnostic;
|
|
|
|
void navigateUp() {
|
|
_navigateHelper(-1);
|
|
}
|
|
|
|
void navigateDown() {
|
|
_navigateHelper(1);
|
|
}
|
|
|
|
void navigateLeft() {
|
|
// This logic is consistent with how IntelliJ handles tree navigation on
|
|
// on left arrow key press.
|
|
if (selection == null) {
|
|
_navigateHelper(-1);
|
|
return;
|
|
}
|
|
|
|
if (selection.isExpanded) {
|
|
setState(() => {
|
|
selection.isExpanded = false;
|
|
});
|
|
return;
|
|
}
|
|
if (selection.parent != null) {
|
|
selection = selection.parent;
|
|
}
|
|
}
|
|
|
|
void navigateRight() {
|
|
// This logic is consistent with how IntelliJ handles tree navigation on
|
|
// on right arrow key press.
|
|
|
|
if (selection == null || selection.isExpanded) {
|
|
_navigateHelper(1);
|
|
return;
|
|
}
|
|
|
|
setState(() => {
|
|
selection.isExpanded = true;
|
|
});
|
|
}
|
|
|
|
void _navigateHelper(int indexOffset) {
|
|
if (numRows == 0) return;
|
|
|
|
if (selection == null) {
|
|
selection = root;
|
|
return;
|
|
}
|
|
|
|
selection = root
|
|
.getRow(
|
|
(root.getRowIndex(selection) + indexOffset).clamp(0, numRows.Value - 1))
|
|
?.node;
|
|
}
|
|
|
|
float horizontalPadding
|
|
{
|
|
get
|
|
{
|
|
return 10.0f;
|
|
}
|
|
}
|
|
|
|
double getDepthIndent(int depth) {
|
|
return (depth + 1) * InspectorTreeUtils.columnWidth + horizontalPadding;
|
|
}
|
|
|
|
double getRowY(int index) {
|
|
return InspectorTreeUtils.rowHeight * index + InspectorTreeUtils.verticalPadding;
|
|
}
|
|
|
|
void nodeChanged(InspectorTreeNode node) {
|
|
if (node == null) return;
|
|
setState(() => {
|
|
node.isDirty = true;
|
|
});
|
|
}
|
|
|
|
void removeNodeFromParent(InspectorTreeNode node) {
|
|
setState(() => {
|
|
node.parent?.removeChild(node);
|
|
});
|
|
}
|
|
|
|
void appendChild(InspectorTreeNode node, InspectorTreeNode child) {
|
|
setState(() => {
|
|
node.appendChild(child);
|
|
});
|
|
}
|
|
|
|
void expandPath(InspectorTreeNode node) {
|
|
setState(() => {
|
|
_expandPath(node);
|
|
});
|
|
}
|
|
|
|
void _expandPath(InspectorTreeNode node) {
|
|
while (node != null) {
|
|
if (!node.isExpanded) {
|
|
node.isExpanded = true;
|
|
}
|
|
node = node.parent;
|
|
}
|
|
}
|
|
|
|
public void collapseToSelected() {
|
|
setState(() => {
|
|
_collapseAllNodes(root);
|
|
if (selection == null) return;
|
|
_expandPath(selection);
|
|
});
|
|
}
|
|
|
|
void _collapseAllNodes(InspectorTreeNode root) {
|
|
root.isExpanded = false;
|
|
foreach (var child in root.children)
|
|
{
|
|
_collapseAllNodes(child);
|
|
}
|
|
}
|
|
|
|
int? numRows
|
|
{
|
|
get
|
|
{
|
|
return root != null ? root.subtreeSize : 0;
|
|
}
|
|
}
|
|
|
|
int getRowIndex(float y) => (int)((y - InspectorTreeUtils.verticalPadding) / InspectorTreeUtils.rowHeight);
|
|
|
|
public InspectorTreeRow getRowForNode(InspectorTreeNode node) {
|
|
return getCachedRow(root.getRowIndex(node));
|
|
}
|
|
|
|
InspectorTreeRow getRow(Offset offset) {
|
|
if (root == null) return null;
|
|
int row = getRowIndex(offset.dy);
|
|
return row < root.subtreeSize ? getCachedRow(row) : null;
|
|
}
|
|
|
|
public abstract void animateToTargets(List<InspectorTreeNode> targets);
|
|
|
|
void onExpandRow(InspectorTreeRow row) {
|
|
setState(() => {
|
|
row.node.isExpanded = true;
|
|
if (config.onExpand != null) {
|
|
config.onExpand(row.node);
|
|
}
|
|
});
|
|
}
|
|
|
|
void onCollapseRow(InspectorTreeRow row) {
|
|
setState(() => {
|
|
row.node.isExpanded = false;
|
|
});
|
|
}
|
|
|
|
void onSelectRow(InspectorTreeRow row) {
|
|
selection = row.node;
|
|
expandPath(row.node);
|
|
}
|
|
|
|
bool expandPropertiesByDefault(DiagnosticsTreeStyle style) {
|
|
// This code matches the text style defaults for which styles are
|
|
// by default and which aren't.
|
|
switch (style) {
|
|
case DiagnosticsTreeStyle.none:
|
|
case DiagnosticsTreeStyle.singleLine:
|
|
case DiagnosticsTreeStyle.errorProperty:
|
|
return false;
|
|
|
|
case DiagnosticsTreeStyle.sparse:
|
|
case DiagnosticsTreeStyle.offstage:
|
|
case DiagnosticsTreeStyle.dense:
|
|
case DiagnosticsTreeStyle.transition:
|
|
case DiagnosticsTreeStyle.error:
|
|
case DiagnosticsTreeStyle.whitespace:
|
|
case DiagnosticsTreeStyle.flat:
|
|
case DiagnosticsTreeStyle.shallow:
|
|
case DiagnosticsTreeStyle.truncateChildren:
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
InspectorTreeNode setupInspectorTreeNode(
|
|
InspectorTreeNode node,
|
|
RemoteDiagnosticsNode diagnosticsNode,
|
|
bool expandChildren = true,
|
|
bool expandProperties = true
|
|
) {
|
|
D.assert(expandChildren != null);
|
|
D.assert(expandProperties != null);
|
|
node.diagnostic = diagnosticsNode;
|
|
if (config.onNodeAdded != null) {
|
|
config.onNodeAdded(node, diagnosticsNode);
|
|
}
|
|
|
|
if (diagnosticsNode.hasChildren ||
|
|
diagnosticsNode.inlineProperties.isNotEmpty()) {
|
|
if (diagnosticsNode.childrenReady || !diagnosticsNode.hasChildren) {
|
|
bool styleIsMultiline =
|
|
expandPropertiesByDefault(diagnosticsNode.style.Value);
|
|
setupChildren(
|
|
diagnosticsNode,
|
|
node,
|
|
node.diagnostic.childrenNow,
|
|
expandChildren: expandChildren && styleIsMultiline,
|
|
expandProperties: expandProperties && styleIsMultiline
|
|
);
|
|
} else {
|
|
node.clearChildren();
|
|
node.appendChild(createNode());
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
void setupChildren(
|
|
RemoteDiagnosticsNode parent,
|
|
InspectorTreeNode treeNode,
|
|
List<RemoteDiagnosticsNode> children,
|
|
bool expandChildren = true,
|
|
bool expandProperties = true
|
|
) {
|
|
D.assert(expandChildren != null);
|
|
D.assert(expandProperties != null);
|
|
treeNode.isExpanded = expandChildren;
|
|
if (treeNode.children.Any()) {
|
|
// Only case supported is this is the loading node.
|
|
D.assert(treeNode.children.Count() == 1);
|
|
removeNodeFromParent(treeNode.children.First());
|
|
}
|
|
var inlineProperties = parent.inlineProperties;
|
|
|
|
if (inlineProperties != null) {
|
|
foreach (RemoteDiagnosticsNode property in inlineProperties) {
|
|
appendChild(
|
|
treeNode,
|
|
setupInspectorTreeNode(
|
|
createNode(),
|
|
property,
|
|
expandChildren: expandProperties,
|
|
expandProperties: expandProperties
|
|
)
|
|
);
|
|
}
|
|
}
|
|
if (children != null) {
|
|
foreach (RemoteDiagnosticsNode child in children) {
|
|
appendChild(
|
|
treeNode,
|
|
setupInspectorTreeNode(
|
|
createNode(),
|
|
child,
|
|
expandChildren: expandChildren,
|
|
expandProperties: expandProperties
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Future maybePopulateChildren(InspectorTreeNode treeNode) {
|
|
RemoteDiagnosticsNode diagnostic = treeNode.diagnostic;
|
|
if (diagnostic != null &&
|
|
diagnostic.hasChildren &&
|
|
(treeNode.hasPlaceholderChildren || !treeNode.children.Any())) {
|
|
try
|
|
{
|
|
diagnostic.children.then((children) =>
|
|
{
|
|
if (treeNode.hasPlaceholderChildren || !treeNode.children.Any()) {
|
|
setupChildren(
|
|
diagnostic,
|
|
treeNode,
|
|
children as List<RemoteDiagnosticsNode>,
|
|
expandChildren: true,
|
|
expandProperties: false
|
|
);
|
|
nodeChanged(treeNode);
|
|
if (treeNode == selection) {
|
|
expandPath(treeNode);
|
|
}
|
|
}
|
|
});
|
|
|
|
} catch (Exception e) {
|
|
Debug.Log(e.ToString());
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
mixin InspectorTreeFixedRowHeightController on InspectorTreeController {
|
|
Rect getBoundingBox(InspectorTreeRow row);
|
|
|
|
void scrollToRect(Rect targetRect);
|
|
|
|
public override void animateToTargets(List<InspectorTreeNode> targets) {
|
|
Rect targetRect;
|
|
|
|
foreach (InspectorTreeNode target in targets) {
|
|
var row = InspectorTreeController.getRowForNode(target);
|
|
if (row != null) {
|
|
var rowRect = getBoundingBox(row);
|
|
targetRect =
|
|
targetRect == null ? rowRect : targetRect.expandToInclude(rowRect);
|
|
}
|
|
}
|
|
|
|
if (targetRect == null || targetRect.isEmpty) return;
|
|
|
|
targetRect = targetRect.inflate(20.0f);
|
|
scrollToRect(targetRect);
|
|
}
|
|
}
|
|
|
|
}*/
|