Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a5f7819
Don't scroll proportionally, scroll accurately
CoderDake Jul 15, 2022
19c25bb
Revert "Don't scroll proportionally, scroll accurately"
CoderDake Jul 15, 2022
757f30c
make text access much cleaner
CoderDake Jul 18, 2022
bf0aac4
Merge remote-tracking branch 'upstream/master' into inspector-scroll
CoderDake Jul 22, 2022
d48dedd
Absolutely Crushed It!!!!!
CoderDake Jul 22, 2022
ff17103
keep it simple, keep it accurate
CoderDake Jul 22, 2022
1da6cb0
better comments and names
CoderDake Jul 22, 2022
426dfa2
address small comments first
CoderDake Aug 4, 2022
ce7f6e6
best attempt so far
CoderDake Aug 11, 2022
8fed737
Merge remote-tracking branch 'upstream/master' into inspector-scroll
CoderDake Sep 6, 2022
9f75df0
Clean up and create tests for both cases
CoderDake Sep 8, 2022
2c9d305
scrollToRect clean up
CoderDake Jul 22, 2022
1c25830
saving changes i almost lost
CoderDake Sep 8, 2022
9d6882f
name improve
CoderDake Sep 8, 2022
e695203
imports
CoderDake Sep 8, 2022
4100faa
named params
CoderDake Sep 13, 2022
b235b87
after talks with jacob, changed scroll to snap to the centre of the s…
CoderDake Sep 13, 2022
3fe63cd
attempt at accounting for fontSize
CoderDake Sep 13, 2022
d31a088
Handle text styles better and add a test for different font Size
CoderDake Sep 14, 2022
c4d9831
epsilon
CoderDake Sep 14, 2022
be5847c
test cleanup
CoderDake Sep 14, 2022
58744db
named params
CoderDake Sep 14, 2022
8ca00a0
if statement optimization, and better DartDoc
CoderDake Sep 14, 2022
dde8264
oops 'murica
CoderDake Sep 14, 2022
db4c6f4
Cleaner Icon width approx
CoderDake Sep 14, 2022
147d2d1
lint fluff
CoderDake Sep 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 89 additions & 25 deletions packages/devtools_app/lib/src/screens/inspector/diagnostics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import '../../shared/object_tree.dart';
import '../../shared/theme.dart';
import '../../ui/hover.dart';
import '../../ui/icons.dart';
import '../../ui/utils.dart';
import '../debugger/debugger_controller.dart';
import '../debugger/variables.dart';
import 'diagnostics_node.dart';
Expand Down Expand Up @@ -51,18 +52,65 @@ class DiagnosticsNodeDescription extends StatelessWidget {
final DebuggerController debuggerController;
final TextStyle? nodeDescriptionHighlightStyle;

Widget _paddedIcon(Widget icon) {
static Widget _paddedIcon(Widget icon) {
return Padding(
padding: const EdgeInsets.only(right: iconPadding),
child: icon,
);
}

Iterable<TextSpan> _buildDescriptionTextSpans(
String description,
TextStyle textStyle,
ColorScheme colorScheme,
) sync* {
/// Approximates the width of the elements inside a [RemoteDiagnosticsNode]
/// widget.
static double approximateNodeWidth(
RemoteDiagnosticsNode? diagnostic,
) {
// If we have rendered this node, then we know it's text style,
// otherwise assume defaultFontSize for the TextStyle.
final textStyle = diagnostic?.descriptionTextStyleFromBuild ??
TextStyle(fontSize: defaultFontSize);

final spans = DiagnosticsNodeDescription.buildDescriptionTextSpans(
description: diagnostic?.description ?? '',
textStyle: textStyle,
colorScheme: const ColorScheme.dark(),
diagnostic: diagnostic,
);

var spanWidth = spans.fold<double>(
0,
(sum, span) => sum + calculateTextSpanWidth(span),
);
String? name = diagnostic?.name;

// An Icon is approximately the width of 1 character

if (diagnostic?.showName == true && name != null) {
// The diagnostic will show it's name instead of an icon so add an
// approximate name width.

if (diagnostic?.description != null) {
// If there is a description then a separator will show with the name.
name += ': ';
}
spanWidth +=
calculateTextSpanWidth(TextSpan(text: name, style: textStyle));
} else {
final approximateIconWidth = IconKind.info.icon.width + iconPadding;

// When there is no name, an icon will be shown with the text spans.
spanWidth += approximateIconWidth;
Comment thread
CoderDake marked this conversation as resolved.
Resolved
}
return spanWidth;
}

static Iterable<TextSpan> buildDescriptionTextSpans({
required String description,
required TextStyle textStyle,
required ColorScheme colorScheme,
RemoteDiagnosticsNode? diagnostic,
String? searchValue,
TextStyle? nodeDescriptionHighlightStyle,
}) sync* {
final diagnosticLocal = diagnostic!;
if (diagnosticLocal.isDiagnosticableValue) {
final match = treeNodePrimaryDescriptionPattern.firstMatch(description);
Expand Down Expand Up @@ -110,17 +158,27 @@ class DiagnosticsNodeDescription extends StatelessWidget {
}
}

Widget buildDescription(
String description,
TextStyle textStyle,
BuildContext context,
ColorScheme colorScheme,
) {
Widget buildDescription({
required String description,
required TextStyle textStyle,
required BuildContext context,
required ColorScheme colorScheme,
RemoteDiagnosticsNode? diagnostic,
Comment thread
CoderDake marked this conversation as resolved.
Resolved
String? searchValue,
TextStyle? nodeDescriptionHighlightStyle,
}) {
// Store the textStyle of the built widget so that it can be used in
// [approximateNodeWidth] later.
diagnostic?.descriptionTextStyleFromBuild = textStyle;

final textSpan = TextSpan(
children: _buildDescriptionTextSpans(
description,
textStyle,
colorScheme,
children: buildDescriptionTextSpans(
description: description,
textStyle: textStyle,
colorScheme: colorScheme,
diagnostic: diagnostic,
searchValue: searchValue,
nodeDescriptionHighlightStyle: nodeDescriptionHighlightStyle,
).toList(),
);

Expand Down Expand Up @@ -263,10 +321,13 @@ class DiagnosticsNodeDescription extends StatelessWidget {
children.add(
Flexible(
child: buildDescription(
description,
descriptionTextStyle,
context,
colorScheme,
description: description,
textStyle: descriptionTextStyle,
context: context,
colorScheme: colorScheme,
diagnostic: diagnostic,
searchValue: searchValue,
nodeDescriptionHighlightStyle: nodeDescriptionHighlightStyle,
),
),
);
Expand Down Expand Up @@ -318,10 +379,13 @@ class DiagnosticsNodeDescription extends StatelessWidget {
}

var diagnosticDescription = buildDescription(
diagnosticLocal.description ?? '',
descriptionTextStyle,
context,
colorScheme,
description: diagnosticLocal.description ?? '',
textStyle: descriptionTextStyle,
context: context,
colorScheme: colorScheme,
diagnostic: diagnostic,
searchValue: searchValue,
nodeDescriptionHighlightStyle: nodeDescriptionHighlightStyle,
);

if (errorText != null) {
Expand Down Expand Up @@ -386,7 +450,7 @@ class DiagnosticsNodeDescription extends StatelessWidget {
);
}

TextSpan _buildHighlightedSearchPreview(
static TextSpan _buildHighlightedSearchPreview(
String textPreview,
String? searchValue,
TextStyle textStyle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ class RemoteDiagnosticsNode extends DiagnosticableTree {
this.parent,
);

/// Stores the [TextStyle] that was used when building the description.
///
/// When not set, then the description has not been built yet.
/// This style is used when approximating the length of the
/// [DiagnosticsNodeDescription], to ensure we are approximating the content
/// area using the right style.
TextStyle? descriptionTextStyleFromBuild;

static final CustomIconMaker iconMaker = CustomIconMaker();

static BoxConstraints deserializeConstraints(Map<String, Object?> json) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -704,33 +704,8 @@ class InspectorController extends DisposableController
if (node == null) {
return;
}
final List<InspectorTreeNode> targets = [node];

// Backtrack to the the first non-property parent so that all properties
// for the node are visible if one property is animated to. This is helpful
// as typically users want to view the properties of a node as a chunk.
while (node!.parent != null && node.diagnostic?.isProperty == true) {
node = node.parent;
}
// Make sure we scroll so that immediate un-expanded children
// are also in view. There is no risk in including these children as
// the amount of space they take up is bounded. This also ensures that if
// a node is selected, its properties will also be selected as by
// convention properties are the first children of a node and properties
// typically do not have children and are never expanded by default.
for (InspectorTreeNode child in node.children) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this logic removed? If the problem is it does bad things with too many properties, we can tweak the logic to only require some small number of children.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While updating the scrolling code, I ended up changing the behaviour so that scrolling always snaps the focused item to the top left of the viewport. So adding any children to the bounding box doesn't change the result of the scrolling.

I am also changing the behaviour, so that we don't distinguish between properties and their parents. The reason for this is that sometimes I would be scrolling the properties with my arrow keys and the focus would go off the screen but the scroll would not update since the parent would be on the screen. I figured that simplifying the whole process to "Just make sure whatever I'm focused on is visible" would help make the experience more consistent.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scrolling to the top left corner of the screen will be jarring for cases like going through a list of search results.
For example image stepping through search results and you get one that is slightly off the screen but now you scroll ALL the way up till it is at the top of the screen.
I would try to maintain the previous functionality which is pretty much industry standard for how you should scroll to bring some new content to be visible on the screen. Instead I would special case so we abandon making the parent visible when it is not possible as the list of properties is larger than the height of the screen.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We chatted about this during a 1:1 and decided together that scrolling to the center of the screen is a happy medium we can agree on.

final RemoteDiagnosticsNode? diagnosticsNode = child.diagnostic;
targets.add(child);
if (!child.isLeaf && child.isExpanded) {
// Stop if we get to expanded children as they might be too large
// to try to scroll into view.
break;
}
if (diagnosticsNode != null && !diagnosticsNode.isProperty) {
break;
}
}
inspectorTree.animateToTargets(targets);
inspectorTree.animateToTargets([node]);
}

void setSelectedNode(InspectorTreeNode? newSelection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ extension InspectorColorScheme on ColorScheme {
const double iconPadding = 4.0;
const double chartLineStrokeWidth = 1.0;
double get columnWidth => scaleByFontFactor(isDense() ? 12.0 : 16.0);
double get verticalPadding => scaleByFontFactor(10.0);
double get rowHeight => scaleByFontFactor(isDense() ? 20.0 : 24.0);

/// This class could be refactored out to be a reasonable generic collapsible
Expand Down
Loading