-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[Yoga] Implement ASYogaLayoutSpec, a simplified integration strategy for Yoga. #270
Conversation
…l-tree integration. This approach allows us to avoid any ASDisplayNode.mm integration points. However, it is not yet proven to be possible to achieve correctness with this approach. The entry point (to start calculating), and the measurement function inputs, lack the full expressiveness of ASSizeRange; we need to make sure that workarounds like using style.minSize are successful in simulating the behavior of a full Yoga tree.
🚫 CI failed with log |
|
Ok I'm an ignorant :P |
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.
It seems to me that the style.mainSize
workarounds are actually correct! I'm curious about cases that you encountered in which this integration doesn't work as expected.
I'll do another round of review before approving, whenever you feel like it's ready.
Source/Layout/ASYogaLayoutSpec.mm
Outdated
// If the root node also has style.*Size set, these will be overridden below. | ||
// YGNodeCalculateLayout currently doesn't offer the ability to pass a minimum size (max is passed there). | ||
YGNodeStyleSetMinWidth (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.width)); | ||
YGNodeStyleSetMinHeight(rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.height)); |
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.
👍
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.
@nguyenhuy thanks for your comments here, it's very helpful to me! I just realized, what if this node has a min size already set on it - such as minWidth = 10% fraction? Do we have enough information here, maybe if we use the parentSize variant, to resolve that percentage and choose the more restrictive one?
hmm, this might be OK, because we perform these lines before calling calculateLayoutThatFits:. Still, interesting to think if we could make use of these extra values.
ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize);
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize);
YGNODE_STYLE_SET_DIMENSION(yogaNode, Height, style.height); | ||
|
||
YGNODE_STYLE_SET_DIMENSION(yogaNode, MinWidth, style.minWidth); | ||
YGNODE_STYLE_SET_DIMENSION(yogaNode, MinHeight, style.minHeight); |
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.
👍
Source/Layout/ASYogaLayoutSpec.mm
Outdated
YGNodeRef rootYogaNode = YGNodeNew(); | ||
|
||
// Apply the constrainedSize as a base, known frame of reference. | ||
// If the root node also has style.*Size set, these will be overridden 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.
Could you revisit this. It seems to be outdated because these min width and height aren't (and shouldn't be) overridden
Source/Layout/ASYogaUtilities.mm
Outdated
if (heightMode == YGMeasureModeExactly) { | ||
sizeRange.min.height = sizeRange.max.height; | ||
} | ||
ASDisplayNodeCAssert([layoutElement isKindOfClass:[ASDisplayNode class]], @"!"); |
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 actually looks good to me! I briefly checked Yoga's source code and it looks like whatever width and height values given to this func were resolved against yogaNode->style.minDimensions
(if needed). Thus, we don't need to consider layoutElement.style.minSize
here.
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.
Thinking again, I believe we should do sizeRange.min.(width|height) = layoutElement.style.minSize.(width|height)
when (width|height)Mode
is either YGMeasureModeAtMost
or YGMeasureModeUndefined
.
The reason is that, even if width and height were resolved against their min values, in the case of these modes, a sub-element still needs a min constraint to measure against. And style.minSize
will not be picked-up by the sub-element if it's not included in rootConstrainedSize
.
Source/Layout/ASYogaLayoutSpec.h
Outdated
@property (nonatomic, strong, nullable) NSArray<ASDisplayNode *> *yogaChildren; | ||
@end | ||
|
||
#endif /* !YOGA_TREE_CONTIGUOUS */ |
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.
It seems like this spec only accepts display nodes. Any particular reasons why it can't just work with generic ASLayoutElement instances? It would be nice if it can do that, and even has the same API with ASStackLayoutSpec
, should we want to make it the default stack algorithm spec and to ensure the transition is seamless. Plus, we can leverage existing snapshot tests of ASStackLayoutSpec
to test this Yoga spec.
🚫 CI failed with log |
🚫 CI failed with log |
I made some updates here, although unfortunately there is still an issue that I can't figure out. I've spent so much time on it that I have to move on for a while, although my team's work is blocked until a path forward is figured out :(. My understanding is that the Yoga import is not available in the unit test target. If we did have a way to run the ASStackLayoutSpec test suite against this, it would be a huge step. |
@garrettmoon FYI, it appears the CI failure is not related to this change:
|
@appleguy Can you rebase your PR the fix for the CI error landed on master. |
🚫 CI failed with log |
🚫 CI failed with log |
🚫 CI failed with log |
8dd924f
to
7c82304
Compare
🚫 CI failed with log |
7c82304
to
0892c27
Compare
🚫 CI failed with log |
@garrettmoon this appears to be the only failure left...do you know if this is known or being seen on any other PRs?
|
@nguyenhuy @maicki ready for review! It's building correctly on the CI, and I also fixed the remaining issue I had with a series of nested stacks not behaving the same as they did previously (at the moment there are no known issues in the computed layout geometry, although I'm expanding testing and hope to utilize some of the ASStackLayoutSpec tests eventually). If you have any concrete ideas on how to integrate this with the Stack tests, let me know - this would be very valuable input. A known problem is that this leaks the yoga nodes right now, although the memory footprint of those is quite small. I will clean this up before landing, but let's work through any questions you have first. |
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.
@appleguy This is absolutely huge! Congratulations on getting this to completion man! Merge at will.
Source/Layout/ASYogaLayoutSpec.mm
Outdated
{ | ||
ASLayoutElementStyle *style = element.style; | ||
|
||
YGNodeSetContext(yogaNode, (__bridge void *)element); |
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.
Small issue, not worth blocking merge. Thinking about ownership of element
.
Should this be something like (__bridge_retained void *)element
then in the yoga context destructor (YGFree?), id val = (__bridge_transfer id)YGNodeGetContext(node)
?
Again, this looks like a headache that we don't need to wrestle with unless we see crashes from deallocated elements in a particular version of the framework. But now it's written down 🙂 .
Review is very old, diff updated.
1d39845
to
fdbfe52
Compare
🚫 CI failed with log |
…transfer, YGNodeFree)
fdbfe52
to
da055aa
Compare
Generated by 🚫 Danger |
…for Yoga. (TextureGroup#270) * [Yoga] Implement ASYogaLayoutSpec, an experimental alternative to full-tree integration. This approach allows us to avoid any ASDisplayNode.mm integration points. However, it is not yet proven to be possible to achieve correctness with this approach. The entry point (to start calculating), and the measurement function inputs, lack the full expressiveness of ASSizeRange; we need to make sure that workarounds like using style.minSize are successful in simulating the behavior of a full Yoga tree. * [Yoga] Fix file comments, move towards <ASLayoutElement> support. * [Yoga] Important fix for simplified, non-contiguous Yoga integration. * [Yoga] Complete implementation of manual memory management (__bridge_transfer, YGNodeFree)
[If this is merged, it should be in a disabled state - e.g. with the ASAvailability.h flag set to YOGA / 1, enabling the current behavior]
This approach allows us to avoid any ASDisplayNode.mm integration points.
However, it is not yet proven to be possible to achieve correctness with this approach.
The entry point (to start calculating), and the measurement function inputs, lack
the full expressiveness of ASSizeRange; we need to make sure that workarounds like
using style.minSize are successful in simulating the behavior of a full Yoga tree.