Skip to content
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] Rewrite YOGA_TREE_CONTIGUOUS mode with improved behavior and cleaner integration #343

Merged
merged 8 commits into from
Jun 15, 2017

Conversation

appleguy
Copy link
Member

@appleguy appleguy commented Jun 9, 2017

After experimentation with the ASYogaLayoutSpec (or non-contiguous) approach to
integrating Yoga, test results and feedback from the authors of Yoga have shown
that this approach can't be made completely correct,

There are issues with some of the features required to represent Web-style
flexbox; in particular: padding, margins, and border handling have varience.

This diff is a first step towards a truly correct and elegant implementation of
Yoga integration with Texture. In addition to reducing the footprint of
the integration, which is an explicit goal of work at this stage, these changes
already support improved behavior - including mixing between ASLayoutSpecs
even as subnodes of Yoga layout-driven nodes, in addition to above them. Yoga
may be used for any set of nodes.

Because Yoga usage is limited at this time, it's safe to merge this diff and
further improvements will be refinements in this direction.

… ASLayoutSpec.

After experimentation with the ASYogaLayoutSpec (or non-contiguous) approach to
integrating Yoga, test results and feedback from the authors of Yoga have shown
that this approach can't be made completely correct,

There are issues with some of the features required to represent Web-style
flexbox; in particular: padding, margins, and border handling have varience.

This diff is a first step towards a truly correct and elegant implementation of
Yoga integration with Texture. In addition to reducing the footprint of
the integration, which is an explicit goal of work at this stage, these changes
already support improved behavior - including mixing between ASLayoutSpecs
even as subnodes of Yoga layout-driven nodes, in addition to above them. Yoga
may be used for any set of nodes.

Because Yoga usage is limited at this time, it's safe to merge this diff and
further improvements will be refinements in this direction.
@appleguy appleguy self-assigned this Jun 9, 2017
@appleguy appleguy requested review from maicki and nguyenhuy June 9, 2017 06:55
}
#endif /* YOGA */

// Manual size calculation via calculateSizeThatFits:
if (((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) ||
(_layoutSpecBlock != NULL)) == NO) {
if (_layoutSpecBlock == NULL && (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == 0) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition is equivalent to the previous one, but easier to read the intention of.

@@ -391,7 +391,7 @@ - (void)_insertElementsIntoMap:(ASMutableElementMap *)map
nodeBlock = [dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath];
}

ASSizeRange constrainedSize;
ASSizeRange constrainedSize = ASSizeRangeUnconstrained;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes an Xcode 9 static analyzer warning. I think it's the right default - if it is ever encountered - but please take a moment to consider it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nguyenhuy would know better, but I believe this is fine because if we don't fetch size ranges at this stage, it means that they're using async layout and they'll perform measurement (and provide a size range) on their own if they want to.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that's a sensible default value. If shouldFetchSizeRanges is false, that means they're using the async layout and the default value will never be used.

else if (propertyName == ASYogaPositionProperty) {
ASEdgeInsets position = self.position;
YGEdge edge = YGEdgeLeft;
for (int i = 0; i < YGEdgeAll + 1; ++i) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, I can macro-ize / simplify the 4 looping cases below. However, since each of them use a few different property-specific invocations, this level of boilerplate seems reasonable for now.

+ (void)initialize
{
[super initialize];
YGConfigSetUseWebDefaults(YGConfigGetDefault(), true);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested this codepath carefully in the debugger to confirm this code works exactly as intended, so this is ready to go once un-commented. I'd like to enable this in the next ~2-3 weeks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍

return _yogaNode;
}

- (YGNodeRef)yogaNodeCreateIfNeeded
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A call to this method is required for Yoga to be used on any given node. Without it, the style properties are not set up on the Yoga node either. This provides an extra level of gating as well.

__weak ASDisplayNode *_yogaParent;
ASLayout *_yogaCalculatedLayout;
BOOL _yogaLayoutInProgress;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the ASHierarchyState options. ASHierarchyState propagation turned out to not be desirable, as the Yoga layout may be used on any individual parent+children combination, or contiguous subtrees separated by non-Yoga nodes too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable. Should we thread-affine this property? Can yoga layouts proceed in parallel?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, ideally this would be both thread-affined and node-affined.

Considering the other PR doing this with ASLayoutElementContext, I wonder if instead we should have a table inside each ASDisplayNode with an unordered_map from pthread ID to an object context?

ASDisplayNodeAssert(yogaNode, @"-[ASDisplayNode setYogaMeasureFuncIfNeeded] called with a NULL yogaNode! %@", self);
if (yogaNode) {
// TODO(appleguy): Use __bridge_transfer to retain nodes and clean up appropriately.
YGNodeSetContext(yogaNode, (__bridge void *)self);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is appropriate to use ASWeakProxy here, and strong-retain the proxy itself? Otherwise it seems a retain cycle would be created.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, ASWeakProxy seems to me like the best way to go here. I believe we could only avoid it if we could guarantee that Yoga will never perform any measurement after a node begins deallocation, probably not possible.

Copy link
Member

@Adlai-Holler Adlai-Holler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! I know you're busy as hell so it's great to see significant progress on this front. No objections from me for landing – we should get this in the field as soon as possible and gather info to guide the next step.

ASDisplayNodeAssert(yogaNode, @"-[ASDisplayNode setYogaMeasureFuncIfNeeded] called with a NULL yogaNode! %@", self);
if (yogaNode) {
// TODO(appleguy): Use __bridge_transfer to retain nodes and clean up appropriately.
YGNodeSetContext(yogaNode, (__bridge void *)self);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, ASWeakProxy seems to me like the best way to go here. I believe we could only avoid it if we could guarantee that Yoga will never perform any measurement after a node begins deallocation, probably not possible.

@@ -391,7 +391,7 @@ - (void)_insertElementsIntoMap:(ASMutableElementMap *)map
nodeBlock = [dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath];
}

ASSizeRange constrainedSize;
ASSizeRange constrainedSize = ASSizeRangeUnconstrained;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nguyenhuy would know better, but I believe this is fine because if we don't fetch size ranges at this stage, it means that they're using async layout and they'll perform measurement (and provide a size range) on their own if they want to.

+ (void)initialize
{
[super initialize];
YGConfigSetUseWebDefaults(YGConfigGetDefault(), true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍

__weak ASDisplayNode *_yogaParent;
ASLayout *_yogaCalculatedLayout;
BOOL _yogaLayoutInProgress;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable. Should we thread-affine this property? Can yoga layouts proceed in parallel?

@ghost
Copy link

ghost commented Jun 15, 2017

2 Warnings
⚠️ This is a big PR, please consider splitting it up to ease code review.
⚠️ Please ensure license is correct for PhotoCellNode.m:

//
//  PhotoCellNode.m
//  Texture
//
//  Copyright (c) 2014-present, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
//  grant of patent rights can be found in the PATENTS file in the same directory.
//
//  Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
//  Pinterest, Inc.  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//

    

Generated by 🚫 Danger

@appleguy appleguy merged commit 55928f3 into master Jun 15, 2017
@appleguy appleguy deleted the YogaContiguousRedux branch June 15, 2017 02:36
bernieperez pushed a commit to AtomTickets/Texture that referenced this pull request Apr 25, 2018
…leaner integration (TextureGroup#343)

* [Yoga] Rewrite YOGA_TREE_CONTIGUOUS mode with support for mixing with ASLayoutSpec.

After experimentation with the ASYogaLayoutSpec (or non-contiguous) approach to
integrating Yoga, test results and feedback from the authors of Yoga have shown
that this approach can't be made completely correct,

There are issues with some of the features required to represent Web-style
flexbox; in particular: padding, margins, and border handling have varience.

This diff is a first step towards a truly correct and elegant implementation of
Yoga integration with Texture. In addition to reducing the footprint of
the integration, which is an explicit goal of work at this stage, these changes
already support improved behavior - including mixing between ASLayoutSpecs
even as subnodes of Yoga layout-driven nodes, in addition to above them. Yoga
may be used for any set of nodes.

Because Yoga usage is limited at this time, it's safe to merge this diff and
further improvements will be refinements in this direction.

* [ASDKgram] Add Yoga layout implementation for PhotoCellNode.

* [Yoga] Final fixes for the upgraded implementation of the Contiguous layout mode.

* [Yoga] Add CHANGELOG.md entry and fix for Yoga rounding to screen scale.

* [Yoga] Minor cleanup to remove old comments and generalize utility methods.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants