Skip to content

Commit 8563215

Browse files
authored
Reland "Set stable color for semantics debugger (#157884)" (#159355)
This reverts commit 07690a6. previous pr was merged when tree is close by mistake, and was revert out of precaution. There is no change in this reland. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent 7be361c commit 8563215

File tree

3 files changed

+43
-6
lines changed

3 files changed

+43
-6
lines changed

packages/flutter/lib/src/semantics/semantics.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,8 +2092,7 @@ class SemanticsNode with DiagnosticableTreeMixin {
20922092
/// Visits the immediate children of this node.
20932093
///
20942094
/// This function calls visitor for each immediate child until visitor returns
2095-
/// false. Returns true if all the visitor calls returned true, otherwise
2096-
/// returns false.
2095+
/// false.
20972096
void visitChildren(SemanticsNodeVisitor visitor) {
20982097
if (_children != null) {
20992098
for (final SemanticsNode child in _children!) {

packages/flutter/lib/src/widgets/semantics_debugger.dart

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ class _SemanticsDebuggerPainter extends CustomPainter {
202202
canvas.save();
203203
canvas.scale(1.0 / devicePixelRatio, 1.0 / devicePixelRatio);
204204
if (rootNode != null) {
205-
_paint(canvas, rootNode, _findDepth(rootNode));
205+
_paint(canvas, rootNode, _findDepth(rootNode), 0, 0);
206206
}
207207
if (pointerPosition != null) {
208208
final Paint paint = Paint();
@@ -332,14 +332,14 @@ class _SemanticsDebuggerPainter extends CustomPainter {
332332
return childrenDepth + 1;
333333
}
334334

335-
void _paint(Canvas canvas, SemanticsNode node, int rank) {
335+
void _paint(Canvas canvas, SemanticsNode node, int rank, int indexInParent, int level) {
336336
canvas.save();
337337
if (node.transform != null) {
338338
canvas.transform(node.transform!.storage);
339339
}
340340
final Rect rect = node.rect;
341341
if (!rect.isEmpty) {
342-
final Color lineColor = Color(0xFF000000 + math.Random(node.id).nextInt(0xFFFFFF));
342+
final Color lineColor = _colorForNode(indexInParent, level);
343343
final Rect innerRect = rect.deflate(rank * 1.0);
344344
if (innerRect.isEmpty) {
345345
final Paint fill = Paint()
@@ -361,13 +361,31 @@ class _SemanticsDebuggerPainter extends CustomPainter {
361361
}
362362
if (!node.mergeAllDescendantsIntoThisNode) {
363363
final int childRank = rank - 1;
364+
final int childLevel = level + 1;
365+
int childIndex = 0;
364366
node.visitChildren((SemanticsNode child) {
365-
_paint(canvas, child, childRank);
367+
_paint(canvas, child, childRank, childIndex, childLevel);
368+
childIndex += 1;
366369
return true;
367370
});
368371
}
369372
canvas.restore();
370373
}
374+
375+
static Color _colorForNode(int index, int level) {
376+
return HSLColor.fromAHSL(
377+
1.0,
378+
// Use custom hash to ensure stable value regardless of Dart changes
379+
360.0 * math.Random(_getColorSeed(index, level)).nextDouble(),
380+
1.0,
381+
0.7,
382+
).toColor();
383+
}
384+
385+
static int _getColorSeed(int level, int index) {
386+
// Should be no collision as long as children number < 10000.
387+
return level * 10000 + index;
388+
}
371389
}
372390

373391
/// A widget ignores pointer event but still keeps semantics actions.

packages/flutter/test/widgets/semantics_debugger_test.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,26 @@ void main() {
6161
expect(true, isTrue); // expect that we reach here without crashing
6262
});
6363

64+
testWidgets('SemanticsDebugger draw persistent color based on structure', (WidgetTester tester) async {
65+
await tester.pumpWidget(
66+
Directionality(
67+
textDirection: TextDirection.ltr,
68+
child: SemanticsDebugger(
69+
child: Stack(
70+
children: <Widget>[
71+
Semantics(
72+
container: true,
73+
child: Semantics(container: true),
74+
),
75+
],
76+
),
77+
),
78+
),
79+
);
80+
81+
expect(find.byType(SemanticsDebugger), paints..rect()..rect(color: const Color(0xFFF866FF)));
82+
});
83+
6484
testWidgets('SemanticsDebugger reparents subtree', (WidgetTester tester) async {
6585
final GlobalKey key = GlobalKey();
6686

0 commit comments

Comments
 (0)