Unverified Commit af129b61 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

`OverlayPortal.overlayChild` contributes semantics to `OverlayPortal` instead...

`OverlayPortal.overlayChild` contributes semantics to `OverlayPortal` instead of `Overlay` (#134921)

Fixes https://github.com/flutter/flutter/issues/134456
parent 6dc5d2fd
...@@ -901,6 +901,8 @@ class _TooltipOverlay extends StatelessWidget { ...@@ -901,6 +901,8 @@ class _TooltipOverlay extends StatelessWidget {
constraints: BoxConstraints(minHeight: height), constraints: BoxConstraints(minHeight: height),
child: DefaultTextStyle( child: DefaultTextStyle(
style: Theme.of(context).textTheme.bodyMedium!, style: Theme.of(context).textTheme.bodyMedium!,
child: Semantics(
container: true,
child: Container( child: Container(
decoration: decoration, decoration: decoration,
padding: padding, padding: padding,
...@@ -917,6 +919,7 @@ class _TooltipOverlay extends StatelessWidget { ...@@ -917,6 +919,7 @@ class _TooltipOverlay extends StatelessWidget {
), ),
), ),
), ),
),
); );
if (onEnter != null || onExit != null) { if (onEnter != null || onExit != null) {
result = _ExclusiveMouseRegion( result = _ExclusiveMouseRegion(
......
...@@ -923,12 +923,26 @@ class _TheaterParentData extends StackParentData { ...@@ -923,12 +923,26 @@ class _TheaterParentData extends StackParentData {
// children that are created by an OverlayPortal. // children that are created by an OverlayPortal.
OverlayEntry? overlayEntry; OverlayEntry? overlayEntry;
/// A [OverlayPortal] makes its overlay child a render child of an ancestor
/// [Overlay]. Currently, to make sure the overlay child is painted after its
/// [OverlayPortal], and before the next [OverlayEntry] (which could be
/// something that should obstruct the overlay child, such as a [ModalRoute])
/// in the host [Overlay], the paint order of each overlay child is managed by
/// the [OverlayEntry] that hosts its [OverlayPortal].
///
/// The following methods are exposed to allow easy access to the overlay
/// children's render objects whose order is managed by [overlayEntry], in the
/// right order.
// _overlayStateMounted is set to null in _OverlayEntryWidgetState's dispose // _overlayStateMounted is set to null in _OverlayEntryWidgetState's dispose
// method. This property is only accessed during layout, paint and hit-test so // method. This property is only accessed during layout, paint and hit-test so
// the `value!` should be safe. // the `value!` should be safe.
Iterator<RenderBox>? get paintOrderIterator => overlayEntry?._overlayEntryStateNotifier?.value!._paintOrderIterable.iterator; Iterator<RenderBox>? get paintOrderIterator => overlayEntry?._overlayEntryStateNotifier?.value!._paintOrderIterable.iterator;
Iterator<RenderBox>? get hitTestOrderIterator => overlayEntry?._overlayEntryStateNotifier?.value!._hitTestOrderIterable.iterator; Iterator<RenderBox>? get hitTestOrderIterator => overlayEntry?._overlayEntryStateNotifier?.value!._hitTestOrderIterable.iterator;
void visitChildrenOfOverlayEntry(RenderObjectVisitor visitor) => overlayEntry?._overlayEntryStateNotifier?.value!._paintOrderIterable.forEach(visitor);
// A convenience method for traversing `paintOrderIterator` with a
// [RenderObjectVisitor].
void visitOverlayPortalChildrenOnOverlayEntry(RenderObjectVisitor visitor) => overlayEntry?._overlayEntryStateNotifier?.value!._paintOrderIterable.forEach(visitor);
} }
class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox, StackParentData>, _RenderTheaterMixin { class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox, StackParentData>, _RenderTheaterMixin {
...@@ -978,7 +992,7 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox ...@@ -978,7 +992,7 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox
RenderBox? child = firstChild; RenderBox? child = firstChild;
while (child != null) { while (child != null) {
final _TheaterParentData childParentData = child.parentData! as _TheaterParentData; final _TheaterParentData childParentData = child.parentData! as _TheaterParentData;
childParentData.visitChildrenOfOverlayEntry(_detachChild); childParentData.visitOverlayPortalChildrenOnOverlayEntry(_detachChild);
child = childParentData.nextSibling; child = childParentData.nextSibling;
} }
} }
...@@ -1197,7 +1211,7 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox ...@@ -1197,7 +1211,7 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox
while (child != null) { while (child != null) {
visitor(child); visitor(child);
final _TheaterParentData childParentData = child.parentData! as _TheaterParentData; final _TheaterParentData childParentData = child.parentData! as _TheaterParentData;
childParentData.visitChildrenOfOverlayEntry(visitor); childParentData.visitOverlayPortalChildrenOnOverlayEntry(visitor);
child = childParentData.nextSibling; child = childParentData.nextSibling;
} }
} }
...@@ -1208,7 +1222,6 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox ...@@ -1208,7 +1222,6 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox
while (child != null) { while (child != null) {
visitor(child); visitor(child);
final _TheaterParentData childParentData = child.parentData! as _TheaterParentData; final _TheaterParentData childParentData = child.parentData! as _TheaterParentData;
childParentData.visitChildrenOfOverlayEntry(visitor);
child = childParentData.nextSibling; child = childParentData.nextSibling;
} }
} }
...@@ -1264,7 +1277,7 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox ...@@ -1264,7 +1277,7 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox
} }
int subcount = 1; int subcount = 1;
childParentData.visitChildrenOfOverlayEntry((RenderObject renderObject) { childParentData.visitOverlayPortalChildrenOnOverlayEntry((RenderObject renderObject) {
final RenderBox child = renderObject as RenderBox; final RenderBox child = renderObject as RenderBox;
if (onstage) { if (onstage) {
onstageChildren.add( onstageChildren.add(
...@@ -1468,6 +1481,17 @@ class OverlayPortalController { ...@@ -1468,6 +1481,17 @@ class OverlayPortalController {
/// [OverlayPortalController.show] was called. The last [OverlayPortal] to have /// [OverlayPortalController.show] was called. The last [OverlayPortal] to have
/// called `show` gets to paint its overlay child in the foreground. /// called `show` gets to paint its overlay child in the foreground.
/// ///
/// ### Semantics
///
/// The semantics subtree generated by the overlay child is considered attached
/// to [OverlayPortal] instead of the target [Overlay]. An [OverlayPortal]'s
/// semantics subtree can be dropped from the semantics tree due to invisibility
/// while the overlay child is still visible (for example, when the
/// [OverlayPortal] is completely invisible in a [ListView] but kept alive by
/// a [KeepAlive] widget). When this happens the semantics subtree generated by
/// the overlay child is also dropped, even if the overlay child is still visible
/// on screen.
///
/// {@template flutter.widgets.overlayPortalVsOverlayEntry} /// {@template flutter.widgets.overlayPortalVsOverlayEntry}
/// ### Differences between [OverlayPortal] and [OverlayEntry] /// ### Differences between [OverlayPortal] and [OverlayEntry]
/// ///
...@@ -2028,8 +2052,9 @@ class _DeferredLayout extends SingleChildRenderObjectWidget { ...@@ -2028,8 +2052,9 @@ class _DeferredLayout extends SingleChildRenderObjectWidget {
} }
} }
// A `RenderProxyBox` that defers its layout until its `_layoutSurrogate` is // A `RenderProxyBox` that defers its layout until its `_layoutSurrogate` (which
// laid out. // is not necessarily an ancestor of this RenderBox, but shares at least one
// `_RenderTheater` ancestor with this RenderBox) is laid out.
// //
// This `RenderObject` must be a child of a `_RenderTheater`. It guarantees that: // This `RenderObject` must be a child of a `_RenderTheater`. It guarantees that:
// //
...@@ -2200,4 +2225,13 @@ class _RenderLayoutSurrogateProxyBox extends RenderProxyBox { ...@@ -2200,4 +2225,13 @@ class _RenderLayoutSurrogateProxyBox extends RenderProxyBox {
// walk. // walk.
_deferredLayoutChild?.layoutByLayoutSurrogate(); _deferredLayoutChild?.layoutByLayoutSurrogate();
} }
@override
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
super.visitChildrenForSemantics(visitor);
final _RenderDeferredLayoutBox? deferredChild = _deferredLayoutChild;
if (deferredChild != null) {
visitor(deferredChild);
}
}
} }
...@@ -3374,10 +3374,6 @@ void main() { ...@@ -3374,10 +3374,6 @@ void main() {
label: 'ABC', label: 'ABC',
rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0), rect: const Rect.fromLTRB(0.0, 0.0, 88.0, 48.0),
), ),
],
),
],
),
TestSemantics( TestSemantics(
id: 6, id: 6,
rect: const Rect.fromLTRB(0.0, 0.0, 120.0, 64.0), rect: const Rect.fromLTRB(0.0, 0.0, 120.0, 64.0),
...@@ -3406,6 +3402,10 @@ void main() { ...@@ -3406,6 +3402,10 @@ void main() {
), ),
], ],
), ),
],
),
],
),
ignoreTransform: true, ignoreTransform: true,
), ),
); );
......
...@@ -1480,6 +1480,68 @@ void main() { ...@@ -1480,6 +1480,68 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Tooltip semantics does not merge into child', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
builder: (BuildContext context) {
return ListView(
children: <Widget>[
const Text('before'),
Tooltip(
key: tooltipKey,
showDuration: const Duration(seconds: 50),
message: 'B',
child: const Text('child'),
),
const Text('after'),
],
);
},
),
],
),
),
);
tooltipKey.currentState?.ensureTooltipVisible();
// Starts the animation.
await tester.pump();
// Make sure the fade in animation has started and the tooltip isn't transparent.
await tester.pump(const Duration(seconds: 2));
expect(semantics, hasSemantics(
TestSemantics.root(
children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
children: <TestSemantics>[
TestSemantics(label: 'before'),
TestSemantics(label: 'child', tooltip: 'B', children: <TestSemantics>[TestSemantics(label: 'B')]),
TestSemantics(label: 'after'),
],
),
],
),
],
),
ignoreId: true,
ignoreRect: true,
ignoreTransform: true,
));
semantics.dispose();
});
testWidgetsWithLeakTracking('Tooltip text scales with textScaleFactor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip text scales with textScaleFactor', (WidgetTester tester) async {
Widget buildApp(String text, { required double textScaleFactor }) { Widget buildApp(String text, { required double textScaleFactor }) {
return Theme( return Theme(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment