This commit is contained in:
parent
9da74f66ca
commit
6d303af97e
@ -2293,6 +2293,9 @@ class _WidgetInspectorState extends State<WidgetInspector>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// Be careful changing this build method. The _InspectorOverlayLayer
|
||||||
|
// assumes the root RenderObject for the WidgetInspector will be
|
||||||
|
// a RenderStack with a _RenderInspectorOverlay as the last child.
|
||||||
return Stack(children: <Widget>[
|
return Stack(children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: _handleTap,
|
onTap: _handleTap,
|
||||||
@ -2441,15 +2444,16 @@ class _RenderInspectorOverlay extends RenderBox {
|
|||||||
context.addLayer(_InspectorOverlayLayer(
|
context.addLayer(_InspectorOverlayLayer(
|
||||||
overlayRect: Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height),
|
overlayRect: Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height),
|
||||||
selection: selection,
|
selection: selection,
|
||||||
|
rootRenderObject: parent is RenderObject ? parent as RenderObject : null,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class _TransformedRect {
|
class _TransformedRect {
|
||||||
_TransformedRect(RenderObject object)
|
_TransformedRect(RenderObject object, RenderObject ancestor)
|
||||||
: rect = object.semanticBounds,
|
: rect = object.semanticBounds,
|
||||||
transform = object.getTransformTo(null);
|
transform = object.getTransformTo(ancestor);
|
||||||
|
|
||||||
final Rect rect;
|
final Rect rect;
|
||||||
final Matrix4 transform;
|
final Matrix4 transform;
|
||||||
@ -2517,6 +2521,7 @@ class _InspectorOverlayLayer extends Layer {
|
|||||||
_InspectorOverlayLayer({
|
_InspectorOverlayLayer({
|
||||||
@required this.overlayRect,
|
@required this.overlayRect,
|
||||||
@required this.selection,
|
@required this.selection,
|
||||||
|
@required this.rootRenderObject,
|
||||||
}) : assert(overlayRect != null),
|
}) : assert(overlayRect != null),
|
||||||
assert(selection != null) {
|
assert(selection != null) {
|
||||||
bool inDebugMode = false;
|
bool inDebugMode = false;
|
||||||
@ -2543,6 +2548,10 @@ class _InspectorOverlayLayer extends Layer {
|
|||||||
/// (as described at [Layer]).
|
/// (as described at [Layer]).
|
||||||
final Rect overlayRect;
|
final Rect overlayRect;
|
||||||
|
|
||||||
|
/// Widget inspector root render object. The selection overlay will be painted
|
||||||
|
/// with transforms relative to this render object.
|
||||||
|
final RenderObject rootRenderObject;
|
||||||
|
|
||||||
_InspectorOverlayRenderState _lastState;
|
_InspectorOverlayRenderState _lastState;
|
||||||
|
|
||||||
/// Picture generated from _lastState.
|
/// Picture generated from _lastState.
|
||||||
@ -2557,16 +2566,21 @@ class _InspectorOverlayLayer extends Layer {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
final RenderObject selected = selection.current;
|
final RenderObject selected = selection.current;
|
||||||
|
|
||||||
|
if (!_isInInspectorRenderObjectTree(selected))
|
||||||
|
return;
|
||||||
|
|
||||||
final List<_TransformedRect> candidates = <_TransformedRect>[];
|
final List<_TransformedRect> candidates = <_TransformedRect>[];
|
||||||
for (final RenderObject candidate in selection.candidates) {
|
for (final RenderObject candidate in selection.candidates) {
|
||||||
if (candidate == selected || !candidate.attached)
|
if (candidate == selected || !candidate.attached
|
||||||
|
|| !_isInInspectorRenderObjectTree(candidate))
|
||||||
continue;
|
continue;
|
||||||
candidates.add(_TransformedRect(candidate));
|
candidates.add(_TransformedRect(candidate, rootRenderObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
final _InspectorOverlayRenderState state = _InspectorOverlayRenderState(
|
final _InspectorOverlayRenderState state = _InspectorOverlayRenderState(
|
||||||
overlayRect: overlayRect,
|
overlayRect: overlayRect,
|
||||||
selected: _TransformedRect(selected),
|
selected: _TransformedRect(selected, rootRenderObject),
|
||||||
tooltip: selection.currentElement.toStringShort(),
|
tooltip: selection.currentElement.toStringShort(),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
candidates: candidates,
|
candidates: candidates,
|
||||||
@ -2583,6 +2597,9 @@ class _InspectorOverlayLayer extends Layer {
|
|||||||
final ui.PictureRecorder recorder = ui.PictureRecorder();
|
final ui.PictureRecorder recorder = ui.PictureRecorder();
|
||||||
final Canvas canvas = Canvas(recorder, state.overlayRect);
|
final Canvas canvas = Canvas(recorder, state.overlayRect);
|
||||||
final Size size = state.overlayRect.size;
|
final Size size = state.overlayRect.size;
|
||||||
|
// The overlay rect could have an offset if the widget inspector does
|
||||||
|
// not take all the screen.
|
||||||
|
canvas.translate(state.overlayRect.left, state.overlayRect.top);
|
||||||
|
|
||||||
final Paint fillPaint = Paint()
|
final Paint fillPaint = Paint()
|
||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
@ -2695,6 +2712,24 @@ class _InspectorOverlayLayer extends Layer {
|
|||||||
}) {
|
}) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether or not a render object belongs to this inspector widget
|
||||||
|
/// tree.
|
||||||
|
/// The inspector selection is static, so if there are multiple inspector
|
||||||
|
/// overlays in the same app (i.e. an storyboard), a selected or candidate
|
||||||
|
/// render object may not belong to this tree.
|
||||||
|
bool _isInInspectorRenderObjectTree(RenderObject child) {
|
||||||
|
RenderObject current = child.parent as RenderObject;
|
||||||
|
while (current != null) {
|
||||||
|
// We found the widget inspector render object.
|
||||||
|
if (current is RenderStack
|
||||||
|
&& current.lastChild is _RenderInspectorOverlay) {
|
||||||
|
return rootRenderObject == current;
|
||||||
|
}
|
||||||
|
current = current.parent as RenderObject;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const double _kScreenEdgeMargin = 10.0;
|
const double _kScreenEdgeMargin = 10.0;
|
||||||
|
@ -512,6 +512,123 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
|
|||||||
expect(inspectorState.selection.candidates.where((RenderObject object) => object is RenderParagraph).length, equals(2));
|
expect(inspectorState.selection.candidates.where((RenderObject object) => object is RenderParagraph).length, equals(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('WidgetInspector with Transform above', (WidgetTester tester) async {
|
||||||
|
final GlobalKey childKey = GlobalKey();
|
||||||
|
final GlobalKey repaintBoundaryKey = GlobalKey();
|
||||||
|
|
||||||
|
final Matrix4 mainTransform = Matrix4.identity()
|
||||||
|
..translate(50.0, 30.0)
|
||||||
|
..scale(0.8, 0.8)
|
||||||
|
..translate(100.0, 50.0);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
RepaintBoundary(
|
||||||
|
key: repaintBoundaryKey,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.grey,
|
||||||
|
child: Transform(
|
||||||
|
transform: mainTransform,
|
||||||
|
child: Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: WidgetInspector(
|
||||||
|
selectButtonBuilder: null,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.white,
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
key: childKey,
|
||||||
|
height: 100.0,
|
||||||
|
width: 50.0,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(childKey));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
find.byKey(repaintBoundaryKey),
|
||||||
|
matchesGoldenFile('inspector.overlay_positioning_with_transform.png'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Multiple widget inspectors', (WidgetTester tester) async {
|
||||||
|
// This test verifies that interacting with different inspectors
|
||||||
|
// works correctly. This use case may be an app that displays multiple
|
||||||
|
// apps inside (i.e. a storyboard).
|
||||||
|
final GlobalKey selectButton1Key = GlobalKey();
|
||||||
|
final GlobalKey selectButton2Key = GlobalKey();
|
||||||
|
|
||||||
|
final GlobalKey inspector1Key = GlobalKey();
|
||||||
|
final GlobalKey inspector2Key = GlobalKey();
|
||||||
|
|
||||||
|
final GlobalKey child1Key = GlobalKey();
|
||||||
|
final GlobalKey child2Key = GlobalKey();
|
||||||
|
|
||||||
|
InspectorSelectButtonBuilder selectButtonBuilder(Key key) {
|
||||||
|
return (BuildContext context, VoidCallback onPressed) {
|
||||||
|
return Material(child: RaisedButton(onPressed: onPressed, key: key));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// State type is private, hence using dynamic.
|
||||||
|
// The inspector state is static, so it's enough with reading one of them.
|
||||||
|
dynamic getInspectorState() => inspector1Key.currentState;
|
||||||
|
String paragraphText(RenderParagraph paragraph) {
|
||||||
|
final TextSpan textSpan = paragraph.text as TextSpan;
|
||||||
|
return textSpan.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Flexible(
|
||||||
|
child: WidgetInspector(
|
||||||
|
key: inspector1Key,
|
||||||
|
selectButtonBuilder: selectButtonBuilder(selectButton1Key),
|
||||||
|
child: Container(
|
||||||
|
key: child1Key,
|
||||||
|
child: const Text('Child 1'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: WidgetInspector(
|
||||||
|
key: inspector2Key,
|
||||||
|
selectButtonBuilder: selectButtonBuilder(selectButton2Key),
|
||||||
|
child: Container(
|
||||||
|
key: child2Key,
|
||||||
|
child: const Text('Child 2'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final InspectorSelection selection = getInspectorState().selection as InspectorSelection;
|
||||||
|
// The selection is static, so it may be initialized from previous tests.
|
||||||
|
selection?.clear();
|
||||||
|
|
||||||
|
await tester.tap(find.text('Child 1'));
|
||||||
|
await tester.pump();
|
||||||
|
expect(paragraphText(selection.current as RenderParagraph), equals('Child 1'));
|
||||||
|
|
||||||
|
await tester.tap(find.text('Child 2'));
|
||||||
|
await tester.pump();
|
||||||
|
expect(paragraphText(selection.current as RenderParagraph), equals('Child 2'));
|
||||||
|
});
|
||||||
|
|
||||||
test('WidgetInspectorService null id', () {
|
test('WidgetInspectorService null id', () {
|
||||||
service.disposeAllGroups();
|
service.disposeAllGroups();
|
||||||
expect(service.toObject(null), isNull);
|
expect(service.toObject(null), isNull);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user