Set stable color for semantics debugger (#157884)

fixes https://github.com/flutter/flutter/issues/156242



## 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
This commit is contained in:
chunhtai 2024-11-22 14:47:59 -08:00 committed by GitHub
parent 9cffcefa15
commit fe087fff95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 43 additions and 6 deletions

View File

@ -2092,8 +2092,7 @@ class SemanticsNode with DiagnosticableTreeMixin {
/// Visits the immediate children of this node.
///
/// This function calls visitor for each immediate child until visitor returns
/// false. Returns true if all the visitor calls returned true, otherwise
/// returns false.
/// false.
void visitChildren(SemanticsNodeVisitor visitor) {
if (_children != null) {
for (final SemanticsNode child in _children!) {

View File

@ -202,7 +202,7 @@ class _SemanticsDebuggerPainter extends CustomPainter {
canvas.save();
canvas.scale(1.0 / devicePixelRatio, 1.0 / devicePixelRatio);
if (rootNode != null) {
_paint(canvas, rootNode, _findDepth(rootNode));
_paint(canvas, rootNode, _findDepth(rootNode), 0, 0);
}
if (pointerPosition != null) {
final Paint paint = Paint();
@ -332,14 +332,14 @@ class _SemanticsDebuggerPainter extends CustomPainter {
return childrenDepth + 1;
}
void _paint(Canvas canvas, SemanticsNode node, int rank) {
void _paint(Canvas canvas, SemanticsNode node, int rank, int indexInParent, int level) {
canvas.save();
if (node.transform != null) {
canvas.transform(node.transform!.storage);
}
final Rect rect = node.rect;
if (!rect.isEmpty) {
final Color lineColor = Color(0xFF000000 + math.Random(node.id).nextInt(0xFFFFFF));
final Color lineColor = _colorForNode(indexInParent, level);
final Rect innerRect = rect.deflate(rank * 1.0);
if (innerRect.isEmpty) {
final Paint fill = Paint()
@ -361,13 +361,31 @@ class _SemanticsDebuggerPainter extends CustomPainter {
}
if (!node.mergeAllDescendantsIntoThisNode) {
final int childRank = rank - 1;
final int childLevel = level + 1;
int childIndex = 0;
node.visitChildren((SemanticsNode child) {
_paint(canvas, child, childRank);
_paint(canvas, child, childRank, childIndex, childLevel);
childIndex += 1;
return true;
});
}
canvas.restore();
}
static Color _colorForNode(int index, int level) {
return HSLColor.fromAHSL(
1.0,
// Use custom hash to ensure stable value regardless of Dart changes
360.0 * math.Random(_getColorSeed(index, level)).nextDouble(),
1.0,
0.7,
).toColor();
}
static int _getColorSeed(int level, int index) {
// Should be no collision as long as children number < 10000.
return level * 10000 + index;
}
}
/// A widget ignores pointer event but still keeps semantics actions.

View File

@ -61,6 +61,26 @@ void main() {
expect(true, isTrue); // expect that we reach here without crashing
});
testWidgets('SemanticsDebugger draw persistent color based on structure', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: SemanticsDebugger(
child: Stack(
children: <Widget>[
Semantics(
container: true,
child: Semantics(container: true),
),
],
),
),
),
);
expect(find.byType(SemanticsDebugger), paints..rect()..rect(color: const Color(0xFFF866FF)));
});
testWidgets('SemanticsDebugger reparents subtree', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();