-
Notifications
You must be signed in to change notification settings - Fork 29.8k
More missing clipBehavior respects #103931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The previous patch led to some internal tests breaking for two reasons:
|
| return PaintingContext(childLayer, bounds); | ||
| } | ||
|
|
||
| void _checkForClipBehavior(String type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: _debugCheckForClipBehavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, remove the assert from this method and make it a bool Function() that could be passed directly to assert where it is called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or I guess, more like a bool Function() Function(String)
jonahwilliams
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
| assert(() { | ||
| final RenderObject active = RenderObject.debugActivePaint!; | ||
| active.visitChildren((RenderObject child) { | ||
| final Rect? approximatePaintClip = active.describeApproximatePaintClip(child); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if I have two children, but I only want to clip one in my current config and to do that I call pushClipRect(clipBehavior: Clip.none) for one child and pushClipRect(clipBehavior: Clip.hardEdge) for the other one. describeApproximatePaintClip would return null for one of the children, but non-null for the other.
In that case, wouldn't I incorrectly see this error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I didn't see a test proving that this would work)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this error should work in that case, but I will add a test.
This seems like a very strange thing to do though, and it's not how any of the framework ROs are implemented today.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added another test for this in object_test.dart.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand how this can work for that case... the _debugCheckForClipBehavior method checks all the children, but is called for each pushed clip layer, which might only involve one child.
In general I don't think the PaintingContext has enough information to make this assert work, since it doesn't know which children are being painted in which layer. Some children might not be painted at all, some might be painted in one layer, some another, etc.
|
(I removed the merge label pending my question above) |
| object.owner!.flushCompositingBits(); | ||
| expect( | ||
| () => PaintingContext(ContainerLayer(), Rect.largest).paintChild(object, Offset.zero), | ||
| returnsNormally, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@goderbauer specifically the RO I added below does what I think you were asking for, and makes sure to return the right response from describeApproximatePaintClip based on which child you ask for.
| final RenderSizedBox child = RenderSizedBox(const Size(300, 300)); | ||
| final RenderSizedBox specialChild = RenderSizedBox(const Size(200, 200)); | ||
| final TestMultiClipBehaviorClipRenderObject object = TestMultiClipBehaviorClipRenderObject(child, specialChild); | ||
| layout(object); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that this layout call doesn't actually execute the paint method of TestMultiClipBehaviorClipRenderObject because the phase parameter of layout defaults to EnginePhase.layout. To execute paint and exercise the code path in _debugCheckForClipBehavior that I am concerned about, can you change this line to:
| layout(object); | |
| layout(object, phase: EnginePhase.paint); |
I would expect that this test would now fail unexpectedly with the FlutterError thrown in _debugCheckForClipBehavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm calling the paint code explicitly in the next line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tested this locally and the test still passes - would you prefer I update the test to expect that call returns normally instead of the more explicit call on PaintingContext.paintChild below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are of course right. I miss-read the next lines and paint is executed as expected. Apologies for that....
However, I still think the bug is there. Re-reading this test again, I think it is just hidden by the fact that TestMultiClipBehaviorClipRenderObject doesn't implement visitChildren correctly (it does not visit the specialChild). If you fix that and implement visitChildren in TestMultiClipBehaviorClipRenderObject correctly (i.e. as shown below) I am pretty sure that this test will fail.
@override void visitChildren(RenderObjectVisitor visitor) {
super.visitChildren(visitor);
visitor(specialChild);
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hah oops. I'll take a look
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah.
I think the two options are: remove the assert, or make it a (new) rule (that probably no one is breaking?) that RenderObjects must clip using the same clip behavior, and if you really want to clip using multiple clipBehaviors you have to create two separate RenderObjects to do it.
That seems like a slightly confusing rule, but it comes with the benefit of having a better error message when you mess things up in the much more common case of incorrectly applying your one and only clip behavior to children, and we could include details about it in the error message.
| if (approximatePaintClip != null) { | ||
| throw FlutterError.fromParts(<DiagnosticsNode>[ | ||
| ErrorSummary('A render object pushes a clip $type with clipBehavior Clip.none, but describes a non-null ' | ||
| 'approximate clip for at least one of its children.'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pushed/described?
| return PaintingContext(childLayer, bounds); | ||
| } | ||
|
|
||
| void _debugCheckForClipBehavior(String type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this might be better named _debugCheckForNoneClipBehavior or some such, i was a bit confused at first as to why it was assuming the clip was "none" until i looked at all the call sites
|
Removed the assert. I think it may be worth seeing if we can put some kind of restriction like this on render objects, but don't want this to get held up by that. |
goderbauer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Very related to #103748
These are more RO implementations that are ignoring
Clip.none.I've added a framework assertion so that if someone tries to call
PaintingContext.pushClipFooand incorrectly implementsdescribeApproximatePaintClip, they'll get an error now. That makes some of these ROs fail without new tests. However, some ROs already try to check for this case in their ownpaintmethods and bypasspushClipFoowhen their clip behavior isClip.noneanyway, so it's not foolproof.Another one for the visibility_detector refactor.