Unverified Commit 1c535de7 authored by Tong Mu's avatar Tong Mu Committed by GitHub

Soften layer breakage (#42953)

* Replace hard break of Layer.find/All with findAllAnnotations
* Deprecate findAll
parent 124dc661
......@@ -123,7 +123,7 @@ class MouseTracker extends ChangeNotifier {
///
/// The second parameter is a function with which the [MouseTracker] can
/// search for [MouseTrackerAnnotation]s at a given position.
/// Usually it is [Layer.findAll] of the root layer.
/// Usually it is [Layer.findAllAnnotations] of the root layer.
///
/// All of the parameters must not be null.
MouseTracker(this._router, this.annotationFinder)
......
......@@ -18,8 +18,7 @@ import 'debug.dart';
///
/// See also:
///
/// * [Layer.find], [Layer.findAll], and [Layer.findAnnotations], which create
/// and use objects of this class.
/// * [Layer.findAnnotations], which create and use objects of this class.
@immutable
class AnnotationEntry<T> {
/// Create an entry of found annotation by providing the oject and related
......@@ -48,8 +47,8 @@ class AnnotationEntry<T> {
/// See also:
///
/// * [AnnotationEntry], which are members of this class.
/// * [Layer.findAll], and [Layer.findAnnotations], which create and use an
/// object of this class.
/// * [Layer.findAllAnnotations], and [Layer.findAnnotations], which create and
/// use an object of this class.
class AnnotationResult<T> {
final List<AnnotationEntry<T>> _entries = <AnnotationEntry<T>>[];
......@@ -280,23 +279,28 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
/// location described by `localPosition`.
///
/// This method is called by the default implementation of [find] and
/// [findAll]. Override this method to customize how the layer should search
/// for annotations, or if the layer has its own annotations to add.
/// [findAllAnnotations]. Override this method to customize how the layer
/// should search for annotations, or if the layer has its own annotations to
/// add.
///
/// The default implementation simply returns `false`, which means neither
/// the layer nor its children has annotations, and the annotation search
/// is not absorbed either (see below for explanation).
///
/// ## About layer annotations
///
/// {@template flutter.rendering.layer.findAnnotations.aboutAnnotations}
/// Annotation is an optional object of any type that can be carried with a
/// An annotation is an optional object of any type that can be carried with a
/// layer. An annotation can be found at a location as long as the owner layer
/// contains the location and is walked to.
///
/// The annotations are searched by first visitng each child recursively, then
/// this layer, resulting in an order from visually front to back. Annotations
/// must meet the given restrictions, such as type and position.
/// The annotations are searched by first visiting each child recursively,
/// then this layer, resulting in an order from visually front to back.
/// Annotations must meet the given restrictions, such as type and position.
///
/// The common way for a value to be found here is by pushing an
/// [AnnotatedRegionLayer] into the layer tree, or by adding the desired
/// annotation by overriding `findAnnotations`.
/// annotation by overriding [findAnnotations].
/// {@endtemplate}
///
/// ## Parameters and return value
......@@ -326,7 +330,9 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
AnnotationResult<S> result,
Offset localPosition, {
@required bool onlyFirst,
});
}) {
return false;
}
/// Search this layer and its subtree for the first annotation of type `S`
/// under the point described by `localPosition`.
......@@ -334,8 +340,10 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
/// Returns null if no matching annotations are found.
///
/// By default this method simply calls [findAnnotations] with `onlyFirst:
/// true` and returns the first result. It is encouraged to override
/// [findAnnotations] instead of this method.
/// true` and returns the annotation of the first result. Prefer overriding
/// [findAnnotations] instead of this method, because during an annotation
/// search, only [findAnnotations] is recursively called, while custom
/// behavior in this method is ignored.
///
/// ## About layer annotations
///
......@@ -343,13 +351,43 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
///
/// See also:
///
/// * [findAll], which is similar but returns all annotations found at the
/// given position.
/// * [findAllAnnotations], which is similar but returns all annotations found
/// at the given position.
/// * [AnnotatedRegionLayer], for placing values in the layer tree.
AnnotationEntry<S> find<S>(Offset localPosition) {
S find<S>(Offset localPosition) {
final AnnotationResult<S> result = AnnotationResult<S>();
findAnnotations<S>(result, localPosition, onlyFirst: true);
return result.entries.isEmpty ? null : result.entries.first;
return result.entries.isEmpty ? null : result.entries.first.annotation;
}
/// Search this layer and its subtree for all annotations of type `S` under
/// the point described by `localPosition`.
///
/// Returns a result with empty entries if no matching annotations are found.
///
/// By default this method simply calls [findAnnotations] with `onlyFirst:
/// false` and returns the annotations of its result. Prefer overriding
/// [findAnnotations] instead of this method, because during an annotation
/// search, only [findAnnotations] is recursively called, while custom
/// behavior in this method is ignored.
///
/// ## About layer annotations
///
/// {@macro flutter.rendering.layer.findAnnotations.aboutAnnotations}
///
/// See also:
///
/// * [find], which is similar but returns the first annotation found at the
/// given position.
/// * [findAllAnnotations], which is similar but returns an
/// [AnnotationResult], which contains more information, such as the local
/// position of the event related to each annotation, and is equally fast,
/// hence is preferred over [findAll].
/// * [AnnotatedRegionLayer], for placing values in the layer tree.
@Deprecated('Use findAllAnnotations instead. This API will be removed in early 2020.')
Iterable<S> findAll<S>(Offset localPosition) {
final AnnotationResult<S> result = findAllAnnotations(localPosition);
return result.entries.map((AnnotationEntry<S> entry) => entry.annotation);
}
/// Search this layer and its subtree for all annotations of type `S` under
......@@ -358,8 +396,10 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
/// Returns a result with empty entries if no matching annotations are found.
///
/// By default this method simply calls [findAnnotations] with `onlyFirst:
/// false` and returns its result. It is encouraged to override
/// [findAnnotations] instead of this method.
/// false` and returns the annotations of its result. Prefer overriding
/// [findAnnotations] instead of this method, because during an annotation
/// search, only [findAnnotations] is recursively called, while custom
/// behavior in this method is ignored.
///
/// ## About layer annotations
///
......@@ -370,7 +410,7 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
/// * [find], which is similar but returns the first annotation found at the
/// given position.
/// * [AnnotatedRegionLayer], for placing values in the layer tree.
AnnotationResult<S> findAll<S>(Offset localPosition) {
AnnotationResult<S> findAllAnnotations<S>(Offset localPosition) {
final AnnotationResult<S> result = AnnotationResult<S>();
findAnnotations<S>(result, localPosition, onlyFirst: false);
return result;
......@@ -2247,10 +2287,10 @@ class FollowerLayer extends ContainerLayer {
/// layer to the tree is the common way of adding an annotation.
///
/// An annotation is an optional object of any type that, when attached with a
/// layer, can be retrieved using [Layer.find] or [Layer.findAll] with a
/// position. The search process is done recursively, controlled by a concept
/// of being opaque to a type of annotation, explained in the document of
/// [Layer.findAnnotations].
/// layer, can be retrieved using [Layer.find] or [Layer.findAllAnnotations]
/// with a position. The search process is done recursively, controlled by a
/// concept of being opaque to a type of annotation, explained in the document
/// of [Layer.findAnnotations].
///
/// When an annotation search arrives, this layer defers the same search to each
/// of this layer's children, respecting their opacity. Then it adds this
......
......@@ -190,13 +190,13 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
///
/// See also:
///
/// * [Layer.findAll], which is used by this method to find all
/// * [Layer.findAllAnnotations], which is used by this method to find all
/// [AnnotatedRegionLayer]s annotated for mouse tracking.
Iterable<MouseTrackerAnnotation> hitTestMouseTrackers(Offset position) {
// Layer hit testing is done using device pixels, so we have to convert
// the logical coordinates of the event location back to device pixels
// here.
return layer.findAll<MouseTrackerAnnotation>(
return layer.findAllAnnotations<MouseTrackerAnnotation>(
position * configuration.devicePixelRatio
).annotations;
}
......@@ -243,12 +243,12 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
final Rect bounds = paintBounds;
final Offset top = Offset(bounds.center.dx, _window.padding.top / _window.devicePixelRatio);
final Offset bottom = Offset(bounds.center.dx, bounds.center.dy - _window.padding.bottom / _window.devicePixelRatio);
final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top)?.annotation;
final SystemUiOverlayStyle upperOverlayStyle = layer.find<SystemUiOverlayStyle>(top);
// Only android has a customizable system navigation bar.
SystemUiOverlayStyle lowerOverlayStyle;
switch (defaultTargetPlatform) {
case TargetPlatform.android:
lowerOverlayStyle = layer.find<SystemUiOverlayStyle>(bottom)?.annotation;
lowerOverlayStyle = layer.find<SystemUiOverlayStyle>(bottom);
break;
case TargetPlatform.iOS:
case TargetPlatform.fuchsia:
......
......@@ -22,9 +22,9 @@ void main() {
i += 1;
}
expect(containerLayer.find<int>(const Offset(0.0, 1.0)).annotation, 0);
expect(containerLayer.find<int>(const Offset(0.0, 101.0)).annotation, 1);
expect(containerLayer.find<int>(const Offset(0.0, 201.0)).annotation, 2);
expect(containerLayer.find<int>(const Offset(0.0, 1.0)), 0);
expect(containerLayer.find<int>(const Offset(0.0, 101.0)), 1);
expect(containerLayer.find<int>(const Offset(0.0, 201.0)), 2);
});
test('finds a value within the clip in a ClipRectLayer', () {
......@@ -41,9 +41,9 @@ void main() {
i += 1;
}
expect(containerLayer.find<int>(const Offset(0.0, 1.0)).annotation, 0);
expect(containerLayer.find<int>(const Offset(0.0, 101.0)).annotation, 1);
expect(containerLayer.find<int>(const Offset(0.0, 201.0)).annotation, 2);
expect(containerLayer.find<int>(const Offset(0.0, 1.0)), 0);
expect(containerLayer.find<int>(const Offset(0.0, 101.0)), 1);
expect(containerLayer.find<int>(const Offset(0.0, 201.0)), 2);
});
......@@ -61,9 +61,9 @@ void main() {
i += 1;
}
expect(containerLayer.find<int>(const Offset(5.0, 5.0)).annotation, 0);
expect(containerLayer.find<int>(const Offset(5.0, 105.0)).annotation, 1);
expect(containerLayer.find<int>(const Offset(5.0, 205.0)).annotation, 2);
expect(containerLayer.find<int>(const Offset(5.0, 5.0)), 0);
expect(containerLayer.find<int>(const Offset(5.0, 105.0)), 1);
expect(containerLayer.find<int>(const Offset(5.0, 205.0)), 2);
});
test('finds a value under a TransformLayer', () {
......@@ -87,11 +87,11 @@ void main() {
i += 1;
}
expect(transformLayer.find<int>(const Offset(0.0, 100.0)).annotation, 0);
expect(transformLayer.find<int>(const Offset(0.0, 200.0)).annotation, 0);
expect(transformLayer.find<int>(const Offset(0.0, 270.0)).annotation, 1);
expect(transformLayer.find<int>(const Offset(0.0, 400.0)).annotation, 1);
expect(transformLayer.find<int>(const Offset(0.0, 530.0)).annotation, 2);
expect(transformLayer.find<int>(const Offset(0.0, 100.0)), 0);
expect(transformLayer.find<int>(const Offset(0.0, 200.0)), 0);
expect(transformLayer.find<int>(const Offset(0.0, 270.0)), 1);
expect(transformLayer.find<int>(const Offset(0.0, 400.0)), 1);
expect(transformLayer.find<int>(const Offset(0.0, 530.0)), 2);
});
test('looks for child AnnotatedRegions before parents', () {
......@@ -101,7 +101,7 @@ void main() {
parent.append(child);
layer.append(parent);
expect(parent.find<int>(Offset.zero).annotation, 2);
expect(parent.find<int>(Offset.zero), 2);
});
test('looks for correct type', () {
......@@ -111,7 +111,7 @@ void main() {
layer.append(child2);
layer.append(child1);
expect(layer.find<String>(Offset.zero).annotation, 'hello');
expect(layer.find<String>(Offset.zero), 'hello');
});
test('does not clip Layer.find on an AnnotatedRegion with an unrelated type', () {
......@@ -121,7 +121,7 @@ void main() {
parent.append(child);
layer.append(parent);
expect(layer.find<int>(const Offset(100.0, 100.0)).annotation, 1);
expect(layer.find<int>(const Offset(100.0, 100.0)), 1);
});
test('handles non-invertable transforms', () {
......@@ -133,10 +133,10 @@ void main() {
parent.transform = Matrix4.diagonal3Values(1.0, 1.0, 1.0);
expect(parent.find<int>(const Offset(0.0, 0.0)).annotation, 1);
expect(parent.find<int>(const Offset(0.0, 0.0)), 1);
});
});
group('$AnnotatedRegion findAll', () {
group('$AnnotatedRegion findAllAnnotations', () {
test('finds the first value in a OffsetLayer when sized', () {
final ContainerLayer containerLayer = ContainerLayer();
final List<OffsetLayer> layers = <OffsetLayer>[
......@@ -151,9 +151,9 @@ void main() {
i += 1;
}
expect(containerLayer.findAll<int>(const Offset(0.0, 1.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(0.0, 101.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(0.0, 201.0)).annotations.toList(), equals(<int>[2]));
expect(containerLayer.findAllAnnotations<int>(const Offset(0.0, 1.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAllAnnotations<int>(const Offset(0.0, 101.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAllAnnotations<int>(const Offset(0.0, 201.0)).annotations.toList(), equals(<int>[2]));
});
test('finds a value within the clip in a ClipRectLayer', () {
......@@ -170,9 +170,9 @@ void main() {
i += 1;
}
expect(containerLayer.findAll<int>(const Offset(0.0, 1.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(0.0, 101.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(0.0, 201.0)).annotations.toList(), equals(<int>[2]));
expect(containerLayer.findAllAnnotations<int>(const Offset(0.0, 1.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAllAnnotations<int>(const Offset(0.0, 101.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAllAnnotations<int>(const Offset(0.0, 201.0)).annotations.toList(), equals(<int>[2]));
});
......@@ -190,9 +190,9 @@ void main() {
i += 1;
}
expect(containerLayer.findAll<int>(const Offset(5.0, 5.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAll<int>(const Offset(5.0, 105.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAll<int>(const Offset(5.0, 205.0)).annotations.toList(), equals(<int>[2]));
expect(containerLayer.findAllAnnotations<int>(const Offset(5.0, 5.0)).annotations.toList(), equals(<int>[0]));
expect(containerLayer.findAllAnnotations<int>(const Offset(5.0, 105.0)).annotations.toList(), equals(<int>[1]));
expect(containerLayer.findAllAnnotations<int>(const Offset(5.0, 205.0)).annotations.toList(), equals(<int>[2]));
});
test('finds a value under a TransformLayer', () {
......@@ -216,11 +216,11 @@ void main() {
i += 1;
}
expect(transformLayer.findAll<int>(const Offset(0.0, 100.0)).annotations.toList(), equals(<int>[0]));
expect(transformLayer.findAll<int>(const Offset(0.0, 200.0)).annotations.toList(), equals(<int>[0]));
expect(transformLayer.findAll<int>(const Offset(0.0, 270.0)).annotations.toList(), equals(<int>[1]));
expect(transformLayer.findAll<int>(const Offset(0.0, 400.0)).annotations.toList(), equals(<int>[1]));
expect(transformLayer.findAll<int>(const Offset(0.0, 530.0)).annotations.toList(), equals(<int>[2]));
expect(transformLayer.findAllAnnotations<int>(const Offset(0.0, 100.0)).annotations.toList(), equals(<int>[0]));
expect(transformLayer.findAllAnnotations<int>(const Offset(0.0, 200.0)).annotations.toList(), equals(<int>[0]));
expect(transformLayer.findAllAnnotations<int>(const Offset(0.0, 270.0)).annotations.toList(), equals(<int>[1]));
expect(transformLayer.findAllAnnotations<int>(const Offset(0.0, 400.0)).annotations.toList(), equals(<int>[1]));
expect(transformLayer.findAllAnnotations<int>(const Offset(0.0, 530.0)).annotations.toList(), equals(<int>[2]));
});
test('finds multiple nested, overlapping regions', () {
......@@ -237,7 +237,7 @@ void main() {
parent.append(layer);
}
expect(parent.findAll<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[3, 1, 2, 0,]));
expect(parent.findAllAnnotations<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[3, 1, 2, 0,]));
});
test('looks for child AnnotatedRegions before parents', () {
......@@ -251,7 +251,7 @@ void main() {
parent.append(child3);
layer.append(parent);
expect(parent.findAll<int>(Offset.zero).annotations.toList(), equals(<int>[4, 3, 2, 1]));
expect(parent.findAllAnnotations<int>(Offset.zero).annotations.toList(), equals(<int>[4, 3, 2, 1]));
});
test('looks for correct type', () {
......@@ -261,7 +261,7 @@ void main() {
layer.append(child2);
layer.append(child1);
expect(layer.findAll<String>(Offset.zero).annotations.toList(), equals(<String>['hello']));
expect(layer.findAllAnnotations<String>(Offset.zero).annotations.toList(), equals(<String>['hello']));
});
test('does not clip Layer.find on an AnnotatedRegion with an unrelated type', () {
......@@ -271,7 +271,7 @@ void main() {
parent.append(child);
layer.append(parent);
expect(layer.findAll<int>(const Offset(100.0, 100.0)).annotations.toList(), equals(<int>[1]));
expect(layer.findAllAnnotations<int>(const Offset(100.0, 100.0)).annotations.toList(), equals(<int>[1]));
});
test('handles non-invertable transforms', () {
......@@ -279,11 +279,11 @@ void main() {
final TransformLayer parent = TransformLayer(transform: Matrix4.diagonal3Values(0.0, 1.0, 1.0));
parent.append(child);
expect(parent.findAll<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[]));
expect(parent.findAllAnnotations<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[]));
parent.transform = Matrix4.diagonal3Values(1.0, 1.0, 1.0);
expect(parent.findAll<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[1]));
expect(parent.findAllAnnotations<int>(const Offset(0.0, 0.0)).annotations.toList(), equals(<int>[1]));
});
});
}
......@@ -34,12 +34,12 @@ void main() {
int result = RendererBinding.instance.renderView.debugLayer.find<int>(Offset(
10.0 * window.devicePixelRatio,
10.0 * window.devicePixelRatio,
))?.annotation;
));
expect(result, null);
result = RendererBinding.instance.renderView.debugLayer.find<int>(Offset(
50.0 * window.devicePixelRatio,
50.0 * window.devicePixelRatio,
)).annotation;
));
expect(result, 1);
});
}
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