Skip to content

Commit 73a94d6

Browse files
committed
fix(replay): avoid unnecessary semantic tree walks
1 parent a4ecee4 commit 73a94d6

2 files changed

Lines changed: 26 additions & 22 deletions

File tree

packages/tugboat/lib/src/anchor_resolver.dart

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -257,30 +257,32 @@ class AnchorResolver {
257257
if (controlValue != null && semanticAnnotation != null) break;
258258
}
259259

260-
// An overlay can sit outside this capture boundary while the global
261-
// semantics tree still contains the actual control at the tap point. Use
262-
// a complete semantic parameter pair from that tree in preference to
263-
// metadata from an obscured control underneath the overlay.
264-
final hits = _semanticsNodesAt(
265-
globalPosition: globalPosition,
266-
rootContext: rootContext,
267-
rootRender: rootRender,
268-
);
269-
final semanticFromHits = _semanticAnnotationFromHits(hits);
270-
final semanticPair =
271-
semanticFromHits?.label != null && semanticFromHits?.value != null;
272260
final localSemanticPair =
273261
semanticAnnotation?.label != null && semanticAnnotation?.value != null;
274-
if (semanticAnnotation == null || (!localSemanticPair && semanticPair)) {
275-
semanticAnnotation = semanticFromHits ?? semanticAnnotation;
276-
}
262+
if (controlValue == null || !localSemanticPair) {
263+
// An overlay can sit outside this capture boundary while the global
264+
// semantics tree still contains the actual control at the tap point.
265+
// Only inspect that tree when local hit-test metadata is incomplete:
266+
// flushing and walking it is comparatively expensive for every tap.
267+
final hits = _semanticsNodesAt(
268+
globalPosition: globalPosition,
269+
rootContext: rootContext,
270+
rootRender: rootRender,
271+
);
272+
final semanticFromHits = _semanticAnnotationFromHits(hits);
273+
final semanticPair =
274+
semanticFromHits?.label != null && semanticFromHits?.value != null;
275+
if (semanticAnnotation == null || (!localSemanticPair && semanticPair)) {
276+
semanticAnnotation = semanticFromHits ?? semanticAnnotation;
277+
}
277278

278-
final controlFromHits = _controlValueFromSemanticsHits(hits);
279-
if (controlValue == null ||
280-
(!localSemanticPair &&
281-
semanticPair &&
282-
controlValue.sources.contains('semantics'))) {
283-
controlValue = controlFromHits ?? controlValue;
279+
final controlFromHits = _controlValueFromSemanticsHits(hits);
280+
if (controlValue == null ||
281+
(!localSemanticPair &&
282+
semanticPair &&
283+
controlValue.sources.contains('semantics'))) {
284+
controlValue = controlFromHits ?? controlValue;
285+
}
284286
}
285287

286288
return TugboatInteractionMetadata._(

packages/tugboat/test/tugboat_replay_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ void main() {
9191
final tap = TugboatReplay.controller!.session!.events
9292
.where((event) => event.type == 'tap')
9393
.last;
94-
final semantic = tap.data['semanticAnnotation'] as Map<String, Object?>;
94+
final semantic = Map<String, Object?>.from(
95+
tap.data['semanticAnnotation'] as Map,
96+
);
9597
expect(semantic['label'], {'kind': 'string', 'value': 'Image quality'});
9698
expect(semantic['value'], {'kind': 'string', 'value': '2K'});
9799
});

0 commit comments

Comments
 (0)