Prevent ChildView
from retaining an otherwise dropped view
#1749
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #1748
Previously,
ChildView
used to hold a strong handle to the underlying view it wanted to render. That was because we wanted to prevent rendering a view that was already dropped. Retaining the view, however, has the downside of delaying the dropping of descendant views until a scene has been invalidated and re-rendered. In particular, the following could happen:X
could contain and render a viewY
that takes focusX
gets dropped, note that viewY
isn't dropped as part of this because the rendered element forX
hasn't been dropped yet and it contains aChildView
pointing toY
Y
to get droppedY
is still the focused element at this point, despite it not being rendered anywhere and its parent being gone.Y
's hierarchy and panic 馃挜There are several solutions that I considered and discarded when exploring the space of this problem:
MutableAppContext::flush_effects
. What I don't love about this is that it might cause us to invalidate the presenter multiple times if e.g.X
contains child viewY
, andY
contains child viewZ
, etc.. I suppose it's still linear but something about the reentrancy nature of this approach feels uncanny.Y
's hierarchy, simply don't panic if a parent view doesn't exist. I discarded this because it feels bad to leave some fundamental state in a dirty state, as it might violate some invariant elsewhere in the code.Ultimately, I concluded that it is a programmer error to try to render a child view without holding it with a strong handle in the parent view. And, in general, holding strong references to views in elements seems like an anti-pattern. Therefore, instead of holding a strong handle in
ChildView
, we will store a weak view handle and avoid laying out/painting/etc. that view if it has been dropped already, logging an error to the console. This avoids the reentrancy of solution 1) and the incorrectness of solution 2) above.It comes at the cost of potentially passing something that will be dropped before the
ChildView
has a chance to render, but it seems like those cases should be pretty rare and we will catch them with the newly-added log statement.